diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml
index ce5832696..ad866971f 100644
--- a/.github/workflows/builds.yml
+++ b/.github/workflows/builds.yml
@@ -72,10 +72,8 @@ jobs:
path: '${{ github.workspace }}/dss_capi/release/*.tar.gz'
build_macos_x64:
- name: 'macOS x64'
+ name: 'macOS x64 and ARM64'
runs-on: macos-latest
- env:
- KLUSOLVE_URL: 'https://github.com/dss-extensions/klusolve/releases/download/1.0.0a1/klusolvex_1.0.0a1_darwin_x64.tar.gz'
steps:
- uses: actions/checkout@v2
with:
@@ -88,16 +86,23 @@ jobs:
sudo installer -package /Volumes/fpc-3.2.2.intelarm64-macosx/fpc-3.2.2-intelarm64-macosx.mpkg -target /
- name: 'Download/extract KLUSolve(X)'
run: |
- wget "${KLUSOLVE_URL}" -Oklusolve.tar.gz -q
- tar zxf klusolve.tar.gz
+ wget "https://github.com/dss-extensions/klusolve/releases/download/1.0.0a1/klusolvex_1.0.0a1_darwin_x64.tar.gz" -Oklusolve_x64.tar.gz -q
+ wget "https://github.com/dss-extensions/klusolve/releases/download/1.0.0a1/klusolvex_1.0.0a1_darwin_arm64.tar.gz" -Oklusolve_arm64.tar.gz -q
+ tar zxf klusolve_x64.tar.gz
+ tar zxf klusolve_arm64.tar.gz
cp -r klusolvex/lib/* dss_capi/lib/
- - name: Build
+ - name: Build x64
run: |
cd dss_capi
source ./build/make_metadata.sh
./build/build_macos_x64.sh
ls -lR lib
-
+ - name: Build ARM64
+ run: |
+ cd dss_capi
+ source ./build/make_metadata.sh
+ ./build/build_macos_arm64.sh
+ ls -lR lib
- name: 'Upload artifacts'
uses: "actions/upload-artifact@v2"
#if: github.event_name == 'release' && github.event.action == 'created'
diff --git a/.gitignore b/.gitignore
index 0b0d183a2..aa426c539 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,3 +66,5 @@ __recovery
*.dproj.local
__history
.vscode
+__pycache__
+*.out.*
diff --git a/LICENSE b/LICENSE
index f75b74043..0f1c25925 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,6 @@
-Copyright (c) 2008-2019, Electric Power Research Institute, Inc.
-Copyright (c) 2017-2019, Paulo Meira
+Copyright (c) 2008-2022, Electric Power Research Institute, Inc.
+Copyright (c) 2017-2022, Paulo Meira
+Copyright (c) 2018-2022, DSS Extensions contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/README.md b/README.md
index 7d1fa019d..1c24a64ce 100644
--- a/README.md
+++ b/README.md
@@ -18,9 +18,9 @@ If you are looking for the bindings to other languages:
- [DSS Sharp](http://github.com/dss-extensions/dss_sharp/) is available for .NET/C#, also mimics the COM classes, but Windows-only at the moment. Soon it will be possible to use it via COM too.
- [DSS MATLAB](http://github.com/dss-extensions/dss_matlab/) presents multi-platform integration (Windows, Linux, MacOS) with DSS C-API and is also very compatible bastante with the COM classes.
-Version 0.10.7, based on OpenDSS revision 2963 (around version OpenDSS 9.1.3.4), with many extra/custom features.
+Version 0.12.0dev, based on OpenDSS revision 3363, with many extra/custom features.
-**This is the work-in-progress branch, which will become 0.12.0.**
+**This is the work-in-progress branch, which will become 0.12.0. For a specific version, check the Git tags.**
While the main objective of COM compatibility has been reach, this is still a work-in-progress and is subject to changes.
*Note that, while the interface with OpenDSS is stable (v7, classic version), the OpenDSS-PM (v8, actor-based parallel machine version) interface is experimental in our builds.* From version 0.10, the v8 interface is a lot more stable than in 0.9.8. Since version 0.10.5, the parallel-machine code from Version 8 is not built for binary releases anymore -- stay tuned for a future unified version.
@@ -42,7 +42,8 @@ Since 2019-03-05, the `dss_capi` repository contains all the Pascal code used to
See [the changelog](https://github.com/dss-extensions/dss_capi/blob/0.10.x/docs/changelog.md) for a detailed list.
-- **2020-03-09 / version 0.10.7-1: Includes a fix for some reports which presented corrupted text in version 0.10.7.**
+- **2022-02-xx / version 0.12.0: Beta versions of 0.12.0 available. Final 0.12.0 expected in May 2022.**
+- 2021-03-09 / version 0.10.7-1: Includes a fix for some reports which presented corrupted text in version 0.10.7.
- 2020-12-28 / version 0.10.7: Maintenance release based on on OpenDSS revision 2963. Includes fixes and new features from the official OpenDSS. [A new document describing the DSS properties](https://github.com/dss-extensions/dss_capi/blob/0.10.x/docs/dss_properties.md) was added.
- 2020-07-31 / version 0.10.6: New API extensions, and ported changes from the official OpenDSS codebase. Includes some bugfixes, a new extended validation error messages and new compatibility toggles.
- 2020-03-03 / version 0.10.5: Maintenance release with several minor fixes. Includes changes ported from COM and the official OpenDSS codebase. Version 8 binary releases excluded.
@@ -61,8 +62,7 @@ See [the changelog](https://github.com/dss-extensions/dss_capi/blob/0.10.x/docs/
## Missing features and limitations
-- Currently not implemented:
- - `DSSEvents` from `DLL/ImplEvents.pas`: seems too dependent on COM.
+- Currently not fully implemented:
- Plotting in general
## Extra features
@@ -141,6 +141,8 @@ Currently most testing/validation is based on [DSS Python](http://github.com/dss
## Roadmap
+(Still being updated for 0.12.x)
+
Besides bug fixes, the main funcionality of this library is mostly done. Notable desirable features that may be implemented are:
- Expose more classes and important methods/properties for all classes
- More and better documentation. We already integrated the help strings from the IDL/COM definition files in the header files.
diff --git a/README.pt-BR.md b/README.pt-BR.md
index d9444c588..a74d8342e 100644
--- a/README.pt-BR.md
+++ b/README.pt-BR.md
@@ -8,7 +8,7 @@
Esta biblioteca expõe o motor do OpenDSS/OpenDSS-PM (v7/v8) através de uma interface C plana, que tenta reproduzir a maioria dos métodos COM. De fato, a maior parte do código foi inicialmente derivado dos arquivos da implementação COM. O DLL resultante pode ser usado diretamente ou através de módulos de interface, como o módulo `DSS Python`. DSS Python representa um módulo para linguagem Python que imita a mesma estrutura do módulo COM (como exposto via `win32com` ou `comtypes`), efetivamente nos permitindo alcançar compatilibilidade multi-plataforma a nível de Python. Também há suporte para outras linguagens diversas -- caso tenha interesse numa linguagem não suportada, abra um novo "issue".
-
+
Caso procure integração com outras linguagens de programação:
@@ -18,7 +18,9 @@ Caso procure integração com outras linguagens de programação:
- [DSS Sharp](http://github.com/dss-extensions/dss_sharp/) para .NET/C#, no momento apenas Windows. Em breve também será possível usá-lo via COM.
- [DSS MATLAB](http://github.com/dss-extensions/dss_matlab/) permite integração multi-plataforma (Windows, Linux, MacOS) bastante compatível com o módulo COM, de fato contorna algumas dificuldades de COM.
-Versão 0.10.7, baseada no OpenDSS SVN r2963 (em torno do OpenDSS 9.1.3.4), com várias funcionalidades extras.
+Versão 0.12.0dev, baseada no OpenDSS revisão (SVN) 3363, com várias funcionalidades customizadas e extras.
+
+**Este branch pode estar em desenvolvimento. Para uma versão específica, consulte os tags do Git do repositório.**
Apesar de o objetivo principal (compatibilidade com COM) ter sido alcançado, este é um sempre um trabalho em andamento.
*Observe que, enquanto a interface clássica (v7 + aprimoramentos) é estável, a interface para o OpenDSS-PM (v8, baseada em atores e execução paralela) ainda é experimental.* A partir da versão 0.10, a interface v8 está bem mais estável que na versão 0.9.8 da DSS C-API. A partir da versão 0.10.5, o código da pasta `Version8` não é mais compilado -- uma nova versão unificada é esperada para uma futura versão.
@@ -61,7 +63,6 @@ Veja o [registro de alterações (em inglês)](https://github.com/dss-extensions
## Funcionalidades faltantes e limitações
- Ainda não implementados:
- - `DSSEvents` de `DLL/ImplEvents.pas`: parece ser muito dependente de COM.
- Gráficos em geral
## Funcionalides extras
@@ -248,6 +249,8 @@ Atualmente, todos os testes e validação são baseados no [DSS Python](http://g
## Planos
+(Ainda sendo atualizados para versão 0.12.x)
+
Além de correções de problemas, a funcionalidade principal desta biblioteca está pronta. Alguns pontos que pretendemos trabalhar envolvem:
- Expor os principais métodos e propriedades faltantes (não presentes nem mesmo na interface COM), assim como classes.
- Documentação melhor e mais completa. As strings de ajuda dos arquivos de definição IDL/COM já estão reproduzidos nos headers (pasta `include`), mas apenas em inglês.
diff --git a/build/build_macos_arm64.sh b/build/build_macos_arm64.sh
new file mode 100755
index 000000000..e463148e0
--- /dev/null
+++ b/build/build_macos_arm64.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+set -e -x
+
+python3 src/classic_to_ctx.py
+
+export LDFLAGS=-L`pwd`/lib/darwin_arm64/
+
+rm -rf build/units_arm64
+mkdir build/units_arm64
+fpc -Paarch64 @src/darwin-arm64.cfg -B src/dss_capi.lpr
+
+# Make the lib look in the same folder for KLUSolveX
+DSS_CAPI_LIB="lib/darwin_arm64/libdss_capi.dylib"
+CURRENT_LIBKLUSOLVE=`otool -L "$DSS_CAPI_LIB" | grep libklusolvex | cut -f 1 -d ' ' | sed $'s/^[ \t]*//'`
+NEW_LIBKLUSOLVE="@loader_path/./libklusolvex.dylib"
+install_name_tool -change "$CURRENT_LIBKLUSOLVE" "$NEW_LIBKLUSOLVE" "$DSS_CAPI_LIB"
+install_name_tool -id "@loader_path/./libdss_capi.dylib" "$DSS_CAPI_LIB"
+
+rm -rf build/units_arm64
+mkdir build/units_arm64
+fpc -Paarch64 @src/darwin-arm64-dbg.cfg -B src/dss_capid.lpr
+
+# Make the lib look in the same folder for KLUSolveX
+DSS_CAPI_LIB="lib/darwin_arm64/libdss_capid.dylib"
+CURRENT_LIBKLUSOLVE=`otool -L "$DSS_CAPI_LIB" | grep libklusolvex | cut -f 1 -d ' ' | sed $'s/^[ \t]*//'`
+NEW_LIBKLUSOLVE="@loader_path/./libklusolvex.dylib"
+install_name_tool -change "$CURRENT_LIBKLUSOLVE" "$NEW_LIBKLUSOLVE" "$DSS_CAPI_LIB"
+install_name_tool -id "@loader_path/./libdss_capi.dylib" "$DSS_CAPI_LIB"
+
+mkdir -p release/dss_capi/lib
+cp -R lib/darwin_arm64 release/dss_capi/lib/darwin_arm64
+cp -R include release/dss_capi/
+# cp -R examples release/dss_capi/
+cp LICENSE release/dss_capi/
+cp OPENDSS_LICENSE release/dss_capi/
+if [ -d "klusolvex" ]; then
+ cp klusolvex/LICENSE release/dss_capi/KLUSOLVE_LICENSE
+else
+ cp ../klusolvex/LICENSE release/dss_capi/KLUSOLVE_LICENSE
+fi
+cd release
+tar zcf "dss_capi_${DSS_CAPI_VERSION}_darwin_arm64.tar.gz" dss_capi
+cd ..
+rm -rf release/dss_capi
diff --git a/docs/changelog.md b/docs/changelog.md
index 943b9a11b..662fcfcde 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -12,6 +12,7 @@
- expose the OpenDSS v8/v9 PM features as possible
- continue work on the plotting and extended reporting API
- see also the GitHub milestone: https://github.com/dss-extensions/dss_capi/milestone/6
+ - remove `LegacyModels` (older/deprecated models for PVSystem, Storage and related classes)
# Version 0.12
@@ -53,6 +54,12 @@ This version still maintains basic compatibility with the 0.10.x series of relea
- Drop function aliases: previously deprecated function aliases (`LoadShapes_Set_Sinterval` and `LoadShapes_Get_sInterval`) were removed to simplify the build process. Use `LoadShapes_Set_SInterval` and `LoadShapes_Get_SInterval` instead.
- Monitor headers: From the official OpenDSS, since May 2021, the monitor binary stream doesn't include the header anymore. When porting the change to DSS Extensions, we took the opportunity to rewrite the related code, simplifying it. As such, the implementation in DSS Extensions deviates from the official one. Extra blank chars are not included, and fields should be more consistent. As a recommendation, if your code needs to be compatible with both implementations, trimming the fields should be enough.
+- Error messages: most messages are now more specific and, if running a DSS script from files, include the file names and line numbers.
+- Spectrum: To reduce overhead during object edits, now required to exist before the object that uses it. This is consistent with most of the other types in OpenDSS.
+- New object and batch APIs
+- New experimental API for loading scripts/data from ZIP files
+- New convenience functions to bulk load commands from the API
+- User-models: headers updated, and removed support for user-models in `LegacyModels` mode. `LegacyModels` will be removed in v0.13.
Due to the high number of IO changes, we recommend checking the performance before and after the upgrade to ensure your use case is not affected negatively. If issues are found, please do report.
diff --git a/docs/images/repomap.png b/docs/images/repomap.png
index 7e5e07579..6fb041d06 100644
Binary files a/docs/images/repomap.png and b/docs/images/repomap.png differ
diff --git a/docs/images/repomap.svg b/docs/images/repomap.svg
index 3647026df..267ea16dc 100644
--- a/docs/images/repomap.svg
+++ b/docs/images/repomap.svg
@@ -1,6 +1,4 @@
-
-
diff --git a/docs/images/repomap_pt.png b/docs/images/repomap_pt.png
new file mode 100644
index 000000000..664758ab3
Binary files /dev/null and b/docs/images/repomap_pt.png differ
diff --git a/docs/images/repomap_pt.svg b/docs/images/repomap_pt.svg
new file mode 100644
index 000000000..e1a136398
--- /dev/null
+++ b/docs/images/repomap_pt.svg
@@ -0,0 +1,666 @@
+
+
diff --git a/include/dss_UserModels.h b/include/dss_UserModels.h
index e74e4d83a..49000e323 100644
--- a/include/dss_UserModels.h
+++ b/include/dss_UserModels.h
@@ -28,6 +28,13 @@
# endif
#endif
+#ifndef dss_long_bool
+#ifdef DSS_CAPI_DLL
+#define dss_long_bool int32_t
+#else
+#define dss_long_bool bool
+#endif
+#endif
#ifdef __cplusplus
extern "C" {
@@ -139,8 +146,10 @@ struct TCapControlVars
DeadTime,
LastOpenTime;
+ dss_long_bool
+ Voverride;
+
bool
- Voverride,
VoverrideEvent,
VoverrideBusSpecified; // Added 8-11-11
@@ -169,28 +178,9 @@ struct TCapControlVars
LastStepInService; // Change this to force an update of cap states
- // NOTE: VOverrideBusName and CapacitorName are UnicodeStrings
- //
- // Without the actual string data, the total bytes for the string struct
- // fields is 12 when the OpenDSS binary is built with Delphi. Since this
- // pointer can be different when used with FPC (16 bytes instead of 12),
- // this pointer needs to be processed different according to the compiler.
- //
- // Delphi UnicodeString structure seems to be:
- // uint16_t codePage;
- // uint16_t bytesPerChar; // should always be 2
- // uint32_t refCount;
- // uint32_t length;
- // char *data;
- //
- // FPC UnicodeString structure seems to be:
- // uint32_t codePage;
- // uint32_t bytesPerChar; // should always be 2
- // uint32_t refCount;
- // uint32_t length;
- // char *data;
-
- char* VOverrideBusName; // Actual string data encoded in UTF-16
+ // NOTE: VOverrideBusName and CapacitorName may be UnicodeStrings in Delphi.
+ // These pointers could be processed different according to the Pascal compiler.
+ char* VOverrideBusName; // Actual string data encoded in UTF-16
char* CapacitorName; // Actual string data encoded in UTF-16
int32_t ControlActionHandle;
@@ -200,19 +190,43 @@ struct TCapControlVars
struct TStorageVars
{
- double
+ double
kWrating,
kWhRating,
kWhStored,
kWhReserve,
ChargeEff,
DisChargeEff,
- kVArating,
kVStorageBase,
- kvarRequested,
RThev,
- XThev,
-
+ XThev;
+
+ double
+ // Inverter Related Properties
+ kVArating,
+ kvarlimit,
+ kvarlimitneg;
+
+ dss_long_bool
+ P_Priority,
+ PF_Priority;
+
+ double
+ pctkWrated,
+ EffFactor;
+
+ double
+ // Interaction with InvControl
+ Vreg,
+ Vavg,
+ VVOperation,
+ VWOperation,
+ DRCOperation,
+ VVDRCOperation,
+ WPOperation,
+ WVOperation;
+
+ double
// Dynamics variables
Vthev_re, // Thevenin equivalent voltage (complex) for dynamic model
Vthev_im,
@@ -307,6 +321,8 @@ struct TDSSCallBacks
DSS_MODEL_CALLBACK(void, GetPublicDataPtr)(void **PublicData, int32_t *PublicDataBytes);
DSS_MODEL_CALLBACK(int32_t, GetActiveElementName)(char *FullName, uint32_t MaxNameLen);
DSS_MODEL_CALLBACK(void*, GetActiveElementPtr)(void); // Returns pointer to active circuit element
+
+ //TODO: check FPC vs Delphi compatibility for const parameters in ControlQueuePush
DSS_MODEL_CALLBACK(int32_t, ControlQueuePush)(const int32_t Hour, const double Sec, const int32_t Code, const int32_t ProxyHdl, void *Owner);
DSS_MODEL_CALLBACK(void, GetResultStr)(char *S, uint32_t Maxlen);
};
diff --git a/include/dss_capi.h b/include/dss_capi.h
index 5160485a0..3a783a0a7 100644
--- a/include/dss_capi.h
+++ b/include/dss_capi.h
@@ -15,6 +15,7 @@
# endif
# else
# include
+# include
# endif
#else
# ifdef _MSC_VER
@@ -25,6 +26,7 @@
# endif
# else
# include
+# include
# endif
#endif
@@ -164,9 +166,25 @@ extern "C" {
DSSMessageType_Progress = 3,
DSSMessageType_ProgressCaption = 4,
DSSMessageType_ProgressFormCaption = 5,
- DSSMessageType_ProgressPercent = 6
+ DSSMessageType_ProgressPercent = 6,
+ DSSMessageType_FireOffEditor = 7
};
+ enum BatchOperation {
+ BatchOperation_Set = 0,
+ BatchOperation_Multiply = 1,
+ BatchOperation_Increment = 2
+ };
+
+ enum SolverOptions {
+ // The values themselves are subject to change in future versions,
+ // use this enum for easier upgrades
+ SolverOptions_ReuseNothing = 0,
+ SolverOptions_ReuseCompressedMatrix = 1, // Reuse only the prepared CSC matrix
+ SolverOptions_ReuseSymbolicFactorization = 2, // Reuse the symbolic factorization, implies ReuseCompressedMatrix
+ SolverOptions_ReuseNumericFactorization = 3, // Reuse the numeric factorization, implies ReuseSymbolicFactorization
+ SolverOptions_AlwaysResetYPrimInvalid = 0x10000000 // Bit flag, see CktElement.pas
+ };
/*
Function types for plotting and writing/message callbacks.
Receives a string that contains the JSON-encoded parameters.
@@ -876,7 +894,7 @@ extern "C" {
DSS_CAPI_DLL void Circuit_Get_SubstationLosses_GR(void);
/*
- Total power, watts delivered to the circuit
+ Total power, kW delivered to the circuit
*/
DSS_CAPI_DLL void Circuit_Get_TotalPower(double** ResultPtr, int32_t* ResultCount);
/*
@@ -1302,14 +1320,16 @@ extern "C" {
DSS_CAPI_DLL void CktElement_Get_AllVariableValues_GR(void);
/*
- For PCElement, get the value of a variable by name. If Code>0 Then no variable by this name or not a PCelement.
+ For PCElement, set/get the value of a variable by name. If Code>0 Then no variable by this name or not a PCelement.
*/
DSS_CAPI_DLL double CktElement_Get_Variable(char* MyVarName, int32_t *Code);
+ DSS_CAPI_DLL double CktElement_Set_Variable(char* MyVarName, int32_t *Code, double Value);
/*
- For PCElement, get the value of a variable by integer index.
+ For PCElement, set/get the value of a variable by integer index.
*/
DSS_CAPI_DLL double CktElement_Get_Variablei(int32_t Idx, int32_t *Code);
+ DSS_CAPI_DLL double CktElement_Set_Variablei(int32_t Idx, int32_t *Code, double Value);
/*
Array of integer containing the node numbers (representing phases, for example) for each conductor of each terminal.
@@ -1571,6 +1591,19 @@ extern "C" {
DSS_CAPI_DLL uint16_t DSS_Get_LegacyModels(void);
DSS_CAPI_DLL void DSS_Set_LegacyModels(uint16_t Value);
+ /*
+ If enabled, the DOScmd command is allowed. Otherwise, an error is reported if the user tries to use it.
+
+ Defaults to False/0 (disabled state). Users should consider DOScmd deprecated on DSS Extensions.
+
+ This can also be set through the environment variable DSS_CAPI_LEGACY_MODELS. Setting it to 1 enables
+ the legacy components, using the old models from the start.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL uint16_t DSS_Get_AllowDOScmd(void);
+ DSS_CAPI_DLL void DSS_Set_AllowDOScmd(uint16_t Value);
+
/*
If disabled, the engine will not change the active working directory during execution. E.g. a "compile"
command will not "chdir" to the file path.
@@ -3514,79 +3547,79 @@ extern "C" {
/*
Delivers the number of CPUs on the current PC
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_NumCPUs(void);
+ DSS_CAPI_DLL int32_t Parallel_Get_NumCPUs(void);
/*
Delivers the number of Cores of the local PC
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_NumCores(void);
+ DSS_CAPI_DLL int32_t Parallel_Get_NumCores(void);
/*
Gets the ID of the Active Actor
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_ActiveActor(void);
+ DSS_CAPI_DLL int32_t Parallel_Get_ActiveActor(void);
/*
Sets the Active Actor
*/
- // DSS_CAPI_DLL void Parallel_Set_ActiveActor(int32_t Value);
+ DSS_CAPI_DLL void Parallel_Set_ActiveActor(int32_t Value);
- // DSS_CAPI_DLL void Parallel_CreateActor(void);
+ DSS_CAPI_DLL void Parallel_CreateActor(void);
/*
Gets the CPU of the Active Actor
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_ActorCPU(void);
+ DSS_CAPI_DLL int32_t Parallel_Get_ActorCPU(void);
/*
Sets the CPU for the Active Actor
*/
- // DSS_CAPI_DLL void Parallel_Set_ActorCPU(int32_t Value);
+ DSS_CAPI_DLL void Parallel_Set_ActorCPU(int32_t Value);
/*
Gets the number of Actors created
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_NumOfActors(void);
+ DSS_CAPI_DLL int32_t Parallel_Get_NumOfActors(void);
- // DSS_CAPI_DLL void Parallel_Wait(void);
+ DSS_CAPI_DLL void Parallel_Wait(void);
/*
Gets the progress of all existing actors in pct
*/
- // DSS_CAPI_DLL void Parallel_Get_ActorProgress(int32_t** ResultPtr, int32_t* ResultCount);
+ DSS_CAPI_DLL void Parallel_Get_ActorProgress(int32_t** ResultPtr, int32_t* ResultCount);
/*
Same as Parallel_Get_ActorProgress but using the global buffer interface for results
*/
- // DSS_CAPI_DLL void Parallel_Get_ActorProgress_GR(void);
+ DSS_CAPI_DLL void Parallel_Get_ActorProgress_GR(void);
/*
Gets the status of each actor
*/
- // DSS_CAPI_DLL void Parallel_Get_ActorStatus(int32_t** ResultPtr, int32_t* ResultCount);
+ DSS_CAPI_DLL void Parallel_Get_ActorStatus(int32_t** ResultPtr, int32_t* ResultCount);
/*
Same as Parallel_Get_ActorStatus but using the global buffer interface for results
*/
- // DSS_CAPI_DLL void Parallel_Get_ActorStatus_GR(void);
+ DSS_CAPI_DLL void Parallel_Get_ActorStatus_GR(void);
/*
Sets ON/OFF (1/0) Parallel features of the Engine
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_ActiveParallel(void);
+ DSS_CAPI_DLL int32_t Parallel_Get_ActiveParallel(void);
/*
Delivers if the Parallel features of the Engine are Active
*/
- // DSS_CAPI_DLL void Parallel_Set_ActiveParallel(int32_t Value);
+ DSS_CAPI_DLL void Parallel_Set_ActiveParallel(int32_t Value);
/*
Reads the values of the ConcatenateReports option (1=enabled, 0=disabled)
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_ConcatenateReports(void);
+ DSS_CAPI_DLL int32_t Parallel_Get_ConcatenateReports(void);
/*
Enable/Disable (1/0) the ConcatenateReports option for extracting monitors data
*/
- // DSS_CAPI_DLL void Parallel_Set_ConcatenateReports(int32_t Value);
+ DSS_CAPI_DLL void Parallel_Set_ConcatenateReports(int32_t Value);
/*
String to be parsed. Loading this string resets the Parser to the beginning of the line. Then parse off the tokens in sequence.
@@ -3860,7 +3893,7 @@ extern "C" {
/*
Same as PDElements_Get_AllPctEmerg but using the global buffer interface for results
*/
- DSS_CAPI_DLL void PDElements_Get_AllPctEmerg_GR(void);
+ DSS_CAPI_DLL void PDElements_Get_AllPctEmerg_GR(uint16_t AllNodes);
/*
@@ -4037,12 +4070,12 @@ extern "C" {
DSS_CAPI_DLL void PVSystems_Set_Name(char* Value);
/*
- Get the present value of the Irradiance property in W/sq-m
+ Get the present value of the Irradiance property in kW/sq-m
*/
DSS_CAPI_DLL double PVSystems_Get_Irradiance(void);
/*
- Set the present Irradiance value in W/sq-m
+ Set the present Irradiance value in kW/sq-m
*/
DSS_CAPI_DLL void PVSystems_Set_Irradiance(double Value);
@@ -5395,7 +5428,7 @@ extern "C" {
*/
DSS_CAPI_DLL void Solution_Set_MinIterations(int32_t Value);
- // DSS_CAPI_DLL void Solution_SolveAll(void);
+ DSS_CAPI_DLL void Solution_SolveAll(void);
DSS_CAPI_DLL void Solution_Get_IncMatrix(int32_t** ResultPtr, int32_t* ResultCount);
@@ -6112,7 +6145,7 @@ extern "C" {
DSS_CAPI_DLL void YMatrix_AddInAuxCurrents(int32_t SType);
DSS_CAPI_DLL void YMatrix_getIpointer(double **IvectorPtr);
DSS_CAPI_DLL void YMatrix_getVpointer(double **VvectorPtr);
- DSS_CAPI_DLL int32_t YMatrix_SolveSystem(double **NodeVPtr);
+ DSS_CAPI_DLL int32_t YMatrix_SolveSystem(double *NodeVPtr);
DSS_CAPI_DLL void YMatrix_Set_SystemYChanged(uint16_t arg);
DSS_CAPI_DLL uint16_t YMatrix_Get_SystemYChanged(void);
DSS_CAPI_DLL void YMatrix_Set_UseAuxCurrents(uint16_t arg);
@@ -6679,6 +6712,190 @@ extern "C" {
DSS_CAPI_DLL void YMatrix_Set_SolverOptions(uint64_t opts);
DSS_CAPI_DLL uint64_t YMatrix_Get_SolverOptions(void);
+ DSS_CAPI_DLL void Text_CommandBlock(char *Value);
+ DSS_CAPI_DLL void Text_CommandArray(char** ValuePtr, int32_t ValueCount);
+
+ /*
+ Opens and prepares a ZIP file to be used by the DSS text parser.
+ Currently, the ZIP format support is limited by what is provided in the Free Pascal distribution.
+ Besides that, the full filenames inside the ZIP must be shorter than 256 characters.
+ The limitations should be removed in a future revision.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL void ZIP_Open(char* FileName);
+
+ /*
+ Runs a "Redirect" command inside the current (open) ZIP file.
+ In the current implementation, all files required by the script must
+ be present inside the ZIP, using relative paths. The only exceptions are
+ memory-mapped files.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL void ZIP_Redirect(char* FileInZip);
+
+ /*
+ Check if the given path name is present in the current ZIP file.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL int16_t ZIP_Contains(char* Name);
+
+ /*
+ List of strings consisting of all names match the regular expression provided in regexp.
+ If no expression is provided, all names in the current open ZIP are returned.
+
+ See https://regex.sorokin.engineer/en/latest/regular_expressions.html for information on
+ the expression syntax and options.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL void ZIP_List(char*** ResultPtr, int32_t* ResultCount, char* RegExp);
+
+ /*
+ Extracts the contents of the file "FileName" from the current (open) ZIP file.
+ Returns a byte-string.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL void ZIP_Extract(int8_t** ResultPtr, int32_t* ResultCount, char* FileName);
+
+ DSS_CAPI_DLL void ZIP_Extract_GR(char* FileName);
+
+ /*
+ Closes the current open ZIP file.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL void ZIP_Close(void);
+
+ /*
+ Functions for the new API
+ */
+
+ /*
+ Extract the current properties as a JSON encoded string.
+ WARNING: this is unstable and subject to change.
+ */
+ DSS_CAPI_DLL char* DSS_ExtractSchema(void *ctx);
+
+ DSS_CAPI_DLL void DSS_Dispose_String(char *S);
+ DSS_CAPI_DLL void DSS_Dispose_PPointer(void*** p);
+
+ DSS_CAPI_DLL void* Obj_New(void* ctx, int32_t ClsIdx, char* Name, int16_t Activate, int16_t BeginEdit);
+ DSS_CAPI_DLL void* Obj_GetHandleByName(void* ctx, int32_t ClsIdx, char* Name);
+ DSS_CAPI_DLL void* Obj_GetHandleByIdx(void* ctx, int32_t ClsIdx, int32_t Idx);
+ DSS_CAPI_DLL int16_t Obj_PropertySideEffects(void *obj, int32_t Index, int32_t PreviousInt);
+ DSS_CAPI_DLL void Obj_BeginEdit(void *obj);
+ DSS_CAPI_DLL void Obj_EndEdit(void *obj, int32_t NumChanges);
+
+ /*
+ Returns the object name (direct access, no copy is done, no disposal required by the user; read only!)
+ */
+ DSS_CAPI_DLL char* Obj_GetName(void *obj);
+
+ /*
+ Returns the object's class name (direct access, no copy is done, no disposal required by the user; read only!)
+ */
+ DSS_CAPI_DLL char* Obj_GetClassName(void *obj);
+
+
+ DSS_CAPI_DLL int32_t Obj_GetIdx(void *obj);
+ DSS_CAPI_DLL char* Obj_GetClassIdx(void *obj);
+
+ /*
+ Activates an object. The object is set as the current
+ active DSSObject or CktElement, and in the list of its parent class.
+ If AllLists is true, other internal lists of OpenDSS are also
+ updated (implies slow/linear searches).
+ */
+ DSS_CAPI_DLL void Obj_Activate(void *obj, int16_t AllLists);
+
+ /*
+ Returns the pointer to the internal property fill sequence, and optionally
+ the highest value in CurrentCount (if not null).
+ */
+ DSS_CAPI_DLL int32_t* Obj_GetPropSeqPtr(void *obj, int32_t *CurrentCount);
+
+ DSS_CAPI_DLL double Obj_GetFloat64(void *obj, int32_t Index);
+ DSS_CAPI_DLL int32_t Obj_GetInt32(void *obj, int32_t Index);
+ DSS_CAPI_DLL void* Obj_GetObject(void *obj, int32_t Index);
+
+ // Note: strings returned by these two must be disposed with DSS_Dispose_String
+ DSS_CAPI_DLL char* Obj_GetString(void *obj, int32_t Index);
+ DSS_CAPI_DLL char* Obj_GetAsString(void *obj, int32_t Index);
+
+ DSS_CAPI_DLL void Obj_GetFloat64Array(double** ResultPtr, int32_t* ResultCount, void *obj, int32_t Index);
+ DSS_CAPI_DLL void Obj_GetInt32Array(int32_t** ResultPtr, int32_t* ResultCount, void *obj, int32_t Index);
+ DSS_CAPI_DLL void Obj_GetStringArray(char*** ResultPtr, int32_t* ResultCount, void *obj, int32_t Index);
+ DSS_CAPI_DLL void Obj_GetObjectArray(void** ResultPtr, int32_t* ResultCount, void *obj, int32_t Index);
+
+ DSS_CAPI_DLL void Obj_SetAsString(void *obj, int32_t Index, char* Value);
+ DSS_CAPI_DLL void Obj_SetFloat64(void *obj, int32_t Index, double Value);
+ DSS_CAPI_DLL void Obj_SetInt32(void *obj, int32_t Index, int32_t Value);
+ DSS_CAPI_DLL void Obj_SetString(void *obj, int32_t Index, char* Value);
+ DSS_CAPI_DLL void Obj_SetObject(void *obj, int32_t Index, void* Value);
+
+ DSS_CAPI_DLL void Obj_SetFloat64Array(void *obj, int32_t Index, double* Value, int32_t ValueCount);
+ DSS_CAPI_DLL void Obj_SetInt32Array(void *obj, int32_t Index, int32_t* Value, int32_t ValueCount);
+ DSS_CAPI_DLL void Obj_SetStringArray(void *obj, int32_t Index, char** Value, int32_t ValueCount);
+ DSS_CAPI_DLL void Obj_SetObjectArray(void *obj, int32_t Index, void **Value, int32_t ValueCount);
+
+ DSS_CAPI_DLL void Batch_Dispose(void **batch);
+ DSS_CAPI_DLL void Batch_BeginEdit(void **batch, int32_t batchSize);
+ DSS_CAPI_DLL void Batch_EndEdit(void **batch, int32_t batchSize, int32_t numEdits);
+ DSS_CAPI_DLL void Batch_GetPropSeq(int32_t** ResultPtr, int32_t* ResultCount, void **batch, int32_t batchSize);
+
+ DSS_CAPI_DLL void Batch_CreateFromNew(void* ctx, void*** ResultPtr, int32_t* ResultCount, int32_t clsid, char** names, int32_t count, int16_t BeginEdit);
+ DSS_CAPI_DLL void Batch_CreateByClass(void* ctx, void*** ResultPtr, int32_t* ResultCount, int32_t clsidx);
+ DSS_CAPI_DLL void Batch_CreateByRegExp(void* ctx, void*** ResultPtr, int32_t* ResultCount, int32_t clsidx, char* re);
+ DSS_CAPI_DLL void Batch_CreateByIndex(void* ctx, void*** ResultPtr, int32_t* ResultCount, int32_t clsidx, int32_t* Value, int32_t ValueCount);
+ DSS_CAPI_DLL void Batch_CreateByInt32Property(void* ctx, void*** ResultPtr, int32_t* ResultCount, int32_t ClsIdx, int32_t idx, int32_t value);
+
+ DSS_CAPI_DLL void Batch_GetFloat64(double** ResultPtr, int32_t* ResultCount, void **batch, int32_t batchSize, int32_t Index);
+ DSS_CAPI_DLL void Batch_GetInt32(int32_t** ResultPtr, int32_t* ResultCount, void **batch, int32_t batchSize, int32_t Index);
+ DSS_CAPI_DLL void Batch_GetString(char*** ResultPtr, int32_t* ResultCount, void **batch, int32_t batchSize, int32_t Index);
+ DSS_CAPI_DLL void Batch_GetAsString(char*** ResultPtr, int32_t* ResultCount, void **batch, int32_t batchSize, int32_t Index);
+
+ DSS_CAPI_DLL void Batch_GetObject(void **batch, int32_t batchSize, int32_t Index, void** ResultPtr, int32_t* ResultCount);
+
+ // DSS_CAPI_DLL void Batch_SetAsString(void **batch, int32_t batchSize, int32_t Index, char* Value);
+ DSS_CAPI_DLL void Batch_Float64(void **batch, int32_t batchSize, int32_t Index, int32_t Operation, double Value);
+ DSS_CAPI_DLL void Batch_Int32(void **batch, int32_t batchSize, int32_t Index, int32_t Operation, int32_t Value);
+ DSS_CAPI_DLL void Batch_SetString(void **batch, int32_t batchSize, int32_t Index, char* Value);
+ DSS_CAPI_DLL void Batch_SetObject(void **batch, int32_t batchSize, int32_t Index, void* Value);
+
+ DSS_CAPI_DLL void Batch_SetFloat64Array(void **batch, int32_t batchSize, int32_t Index, double* Value);
+ DSS_CAPI_DLL void Batch_SetInt32Array(void **batch, int32_t batchSize, int32_t Index, int32_t* Value);
+ DSS_CAPI_DLL void Batch_SetStringArray(void **batch, int32_t batchSize, int32_t Index, char** Value);
+ DSS_CAPI_DLL void Batch_SetObjectArray(void **batch, int32_t batchSize, int32_t Index, void** Value);
+
+ DSS_CAPI_DLL void Batch_CreateFromNewS(void* ctx, void*** ResultPtr, int32_t* ResultCount, char* clsname, char** names, int32_t count, int16_t BeginEdit);
+ DSS_CAPI_DLL void Batch_CreateByClassS(void* ctx, void*** ResultPtr, int32_t* ResultCount, char* clsname);
+ DSS_CAPI_DLL void Batch_CreateByRegExpS(void* ctx, void*** ResultPtr, int32_t* ResultCount, char* clsname, char* re);
+ DSS_CAPI_DLL void Batch_CreateByIndexS(void* ctx, void*** ResultPtr, int32_t* ResultCount, char* clsname, int32_t* Value, int32_t ValueCount);
+ DSS_CAPI_DLL void Batch_CreateByInt32PropertyS(void* ctx, void*** ResultPtr, int32_t* ResultCount, char* clsname, char* name, int32_t value);
+
+ DSS_CAPI_DLL void Batch_GetFloat64S(double** ResultPtr, int32_t* ResultCount, void **batch, int32_t batchSize, char* Name);
+ DSS_CAPI_DLL void Batch_GetInt32S(int32_t** ResultPtr, int32_t* ResultCount, void **batch, int32_t batchSize, char* Name);
+ DSS_CAPI_DLL void Batch_GetStringS(char*** ResultPtr, int32_t* ResultCount, void **batch, int32_t batchSize, char* Name);
+ DSS_CAPI_DLL void Batch_GetAsStringS(char*** ResultPtr, int32_t* ResultCount, void **batch, int32_t batchSize, char* Name);
+
+ DSS_CAPI_DLL void Batch_GetObjectS(void **batch, int32_t batchSize, char* Name, void** ResultPtr, int32_t* ResultCount);
+
+ // DSS_CAPI_DLL void Batch_SetAsStringS(void **batch, int32_t batchSize, char* Name, char* Value);
+ DSS_CAPI_DLL void Batch_Float64S(void **batch, int32_t batchSize, char* Name, int32_t Operation, double Value);
+ DSS_CAPI_DLL void Batch_Int32S(void **batch, int32_t batchSize, char* Name, int32_t Operation, int32_t Value);
+ DSS_CAPI_DLL void Batch_SetStringS(void **batch, int32_t batchSize, char* Name, char* Value);
+ DSS_CAPI_DLL void Batch_SetObjectS(void **batch, int32_t batchSize, char* Name, void* Value);
+
+ DSS_CAPI_DLL void Batch_SetFloat64ArrayS(void **batch, int32_t batchSize, char* Name, double* Value);
+ DSS_CAPI_DLL void Batch_SetInt32ArrayS(void **batch, int32_t batchSize, char* Name, int32_t* Value);
+ DSS_CAPI_DLL void Batch_SetStringArrayS(void **batch, int32_t batchSize, char* Name, char** Value);
+ DSS_CAPI_DLL void Batch_SetObjectArrayS(void **batch, int32_t batchSize, char* Name, void** Value);
+
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/include/dss_capi_ctx.h b/include/dss_capi_ctx.h
index ffd33766f..6bb9eee42 100644
--- a/include/dss_capi_ctx.h
+++ b/include/dss_capi_ctx.h
@@ -45,7 +45,7 @@ extern "C" {
previous prime instance returned by ctx_Set_Prime, if required.
*/
DSS_CAPI_DLL void *ctx_Set_Prime(void *ctx);
-
+
DSS_CAPI_DLL void ctx_DSS_ResetStringBuffer(void* ctx);
@@ -740,7 +740,7 @@ extern "C" {
DSS_CAPI_DLL void ctx_Circuit_Get_SubstationLosses_GR(void* ctx);
/*
- Total power, watts delivered to the circuit
+ Total power, kW delivered to the circuit
*/
DSS_CAPI_DLL void ctx_Circuit_Get_TotalPower(void* ctx, double** ResultPtr, int32_t* ResultCount);
/*
@@ -1166,14 +1166,16 @@ extern "C" {
DSS_CAPI_DLL void ctx_CktElement_Get_AllVariableValues_GR(void* ctx);
/*
- For PCElement, get the value of a variable by name. If Code>0 Then no variable by this name or not a PCelement.
+ For PCElement, set/get the value of a variable by name. If Code>0 Then no variable by this name or not a PCelement.
*/
DSS_CAPI_DLL double ctx_CktElement_Get_Variable(void* ctx, char* MyVarName, int32_t *Code);
+ DSS_CAPI_DLL double ctx_CktElement_Set_Variable(void* ctx, char* MyVarName, int32_t *Code, double Value);
/*
- For PCElement, get the value of a variable by integer index.
+ For PCElement, set/get the value of a variable by integer index.
*/
DSS_CAPI_DLL double ctx_CktElement_Get_Variablei(void* ctx, int32_t Idx, int32_t *Code);
+ DSS_CAPI_DLL double ctx_CktElement_Set_Variablei(void* ctx, int32_t Idx, int32_t *Code, double Value);
/*
Array of integer containing the node numbers (representing phases, for example) for each conductor of each terminal.
@@ -1435,6 +1437,19 @@ extern "C" {
DSS_CAPI_DLL uint16_t ctx_DSS_Get_LegacyModels(void* ctx);
DSS_CAPI_DLL void ctx_DSS_Set_LegacyModels(void* ctx, uint16_t Value);
+ /*
+ If enabled, the DOScmd command is allowed. Otherwise, an error is reported if the user tries to use it.
+
+ Defaults to False/0 (disabled state). Users should consider DOScmd deprecated on DSS Extensions.
+
+ This can also be set through the environment variable DSS_CAPI_LEGACY_MODELS. Setting it to 1 enables
+ the legacy components, using the old models from the start.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL uint16_t ctx_DSS_Get_AllowDOScmd(void* ctx);
+ DSS_CAPI_DLL void ctx_DSS_Set_AllowDOScmd(void* ctx, uint16_t Value);
+
/*
If disabled, the engine will not change the active working directory during execution. E.g. a "compile"
command will not "chdir" to the file path.
@@ -3378,79 +3393,79 @@ extern "C" {
/*
Delivers the number of CPUs on the current PC
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_NumCPUs(void);
+ DSS_CAPI_DLL int32_t ctx_Parallel_Get_NumCPUs(void* ctx);
/*
Delivers the number of Cores of the local PC
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_NumCores(void);
+ DSS_CAPI_DLL int32_t ctx_Parallel_Get_NumCores(void* ctx);
/*
Gets the ID of the Active Actor
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_ActiveActor(void);
+ DSS_CAPI_DLL int32_t ctx_Parallel_Get_ActiveActor(void* ctx);
/*
Sets the Active Actor
*/
- // DSS_CAPI_DLL void Parallel_Set_ActiveActor(int32_t Value);
+ DSS_CAPI_DLL void ctx_Parallel_Set_ActiveActor(void* ctx, int32_t Value);
- // DSS_CAPI_DLL void Parallel_CreateActor(void);
+ DSS_CAPI_DLL void ctx_Parallel_CreateActor(void* ctx);
/*
Gets the CPU of the Active Actor
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_ActorCPU(void);
+ DSS_CAPI_DLL int32_t ctx_Parallel_Get_ActorCPU(void* ctx);
/*
Sets the CPU for the Active Actor
*/
- // DSS_CAPI_DLL void Parallel_Set_ActorCPU(int32_t Value);
+ DSS_CAPI_DLL void ctx_Parallel_Set_ActorCPU(void* ctx, int32_t Value);
/*
Gets the number of Actors created
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_NumOfActors(void);
+ DSS_CAPI_DLL int32_t ctx_Parallel_Get_NumOfActors(void* ctx);
- // DSS_CAPI_DLL void Parallel_Wait(void);
+ DSS_CAPI_DLL void ctx_Parallel_Wait(void* ctx);
/*
Gets the progress of all existing actors in pct
*/
- // DSS_CAPI_DLL void Parallel_Get_ActorProgress(int32_t** ResultPtr, int32_t* ResultCount);
+ DSS_CAPI_DLL void ctx_Parallel_Get_ActorProgress(void* ctx, int32_t** ResultPtr, int32_t* ResultCount);
/*
Same as Parallel_Get_ActorProgress but using the global buffer interface for results
*/
- // DSS_CAPI_DLL void Parallel_Get_ActorProgress_GR(void);
+ DSS_CAPI_DLL void ctx_Parallel_Get_ActorProgress_GR(void* ctx);
/*
Gets the status of each actor
*/
- // DSS_CAPI_DLL void Parallel_Get_ActorStatus(int32_t** ResultPtr, int32_t* ResultCount);
+ DSS_CAPI_DLL void ctx_Parallel_Get_ActorStatus(void* ctx, int32_t** ResultPtr, int32_t* ResultCount);
/*
Same as Parallel_Get_ActorStatus but using the global buffer interface for results
*/
- // DSS_CAPI_DLL void Parallel_Get_ActorStatus_GR(void);
+ DSS_CAPI_DLL void ctx_Parallel_Get_ActorStatus_GR(void* ctx);
/*
Sets ON/OFF (1/0) Parallel features of the Engine
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_ActiveParallel(void);
+ DSS_CAPI_DLL int32_t ctx_Parallel_Get_ActiveParallel(void* ctx);
/*
Delivers if the Parallel features of the Engine are Active
*/
- // DSS_CAPI_DLL void Parallel_Set_ActiveParallel(int32_t Value);
+ DSS_CAPI_DLL void ctx_Parallel_Set_ActiveParallel(void* ctx, int32_t Value);
/*
Reads the values of the ConcatenateReports option (1=enabled, 0=disabled)
*/
- // DSS_CAPI_DLL int32_t Parallel_Get_ConcatenateReports(void);
+ DSS_CAPI_DLL int32_t ctx_Parallel_Get_ConcatenateReports(void* ctx);
/*
Enable/Disable (1/0) the ConcatenateReports option for extracting monitors data
*/
- // DSS_CAPI_DLL void Parallel_Set_ConcatenateReports(int32_t Value);
+ DSS_CAPI_DLL void ctx_Parallel_Set_ConcatenateReports(void* ctx, int32_t Value);
/*
String to be parsed. Loading this string resets the Parser to the beginning of the line. Then parse off the tokens in sequence.
@@ -3724,7 +3739,7 @@ extern "C" {
/*
Same as PDElements_Get_AllPctEmerg but using the global buffer interface for results
*/
- DSS_CAPI_DLL void ctx_PDElements_Get_AllPctEmerg_GR(void* ctx);
+ DSS_CAPI_DLL void ctx_PDElements_Get_AllPctEmerg_GR(void* ctx, uint16_t AllNodes);
/*
@@ -3901,12 +3916,12 @@ extern "C" {
DSS_CAPI_DLL void ctx_PVSystems_Set_Name(void* ctx, char* Value);
/*
- Get the present value of the Irradiance property in W/sq-m
+ Get the present value of the Irradiance property in kW/sq-m
*/
DSS_CAPI_DLL double ctx_PVSystems_Get_Irradiance(void* ctx);
/*
- Set the present Irradiance value in W/sq-m
+ Set the present Irradiance value in kW/sq-m
*/
DSS_CAPI_DLL void ctx_PVSystems_Set_Irradiance(void* ctx, double Value);
@@ -5259,7 +5274,7 @@ extern "C" {
*/
DSS_CAPI_DLL void ctx_Solution_Set_MinIterations(void* ctx, int32_t Value);
- // DSS_CAPI_DLL void Solution_SolveAll(void);
+ DSS_CAPI_DLL void ctx_Solution_SolveAll(void* ctx);
DSS_CAPI_DLL void ctx_Solution_Get_IncMatrix(void* ctx, int32_t** ResultPtr, int32_t* ResultCount);
@@ -5976,7 +5991,7 @@ extern "C" {
DSS_CAPI_DLL void ctx_YMatrix_AddInAuxCurrents(void* ctx, int32_t SType);
DSS_CAPI_DLL void ctx_YMatrix_getIpointer(void* ctx, double **IvectorPtr);
DSS_CAPI_DLL void ctx_YMatrix_getVpointer(void* ctx, double **VvectorPtr);
- DSS_CAPI_DLL int32_t ctx_YMatrix_SolveSystem(void* ctx, double **NodeVPtr);
+ DSS_CAPI_DLL int32_t ctx_YMatrix_SolveSystem(void* ctx, double *NodeVPtr);
DSS_CAPI_DLL void ctx_YMatrix_Set_SystemYChanged(void* ctx, uint16_t arg);
DSS_CAPI_DLL uint16_t ctx_YMatrix_Get_SystemYChanged(void* ctx);
DSS_CAPI_DLL void ctx_YMatrix_Set_UseAuxCurrents(void* ctx, uint16_t arg);
@@ -6543,6 +6558,114 @@ extern "C" {
DSS_CAPI_DLL void ctx_YMatrix_Set_SolverOptions(void* ctx, uint64_t opts);
DSS_CAPI_DLL uint64_t ctx_YMatrix_Get_SolverOptions(void* ctx);
+ DSS_CAPI_DLL void ctx_Text_CommandBlock(void* ctx, char *Value);
+ DSS_CAPI_DLL void ctx_Text_CommandArray(void* ctx, char** ValuePtr, int32_t ValueCount);
+
+ /*
+ Opens and prepares a ZIP file to be used by the DSS text parser.
+ Currently, the ZIP format support is limited by what is provided in the Free Pascal distribution.
+ Besides that, the full filenames inside the ZIP must be shorter than 256 characters.
+ The limitations should be removed in a future revision.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL void ctx_ZIP_Open(void* ctx, char* FileName);
+
+ /*
+ Runs a "Redirect" command inside the current (open) ZIP file.
+ In the current implementation, all files required by the script must
+ be present inside the ZIP, using relative paths. The only exceptions are
+ memory-mapped files.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL void ctx_ZIP_Redirect(void* ctx, char* FileInZip);
+
+ /*
+ Check if the given path name is present in the current ZIP file.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL int16_t ctx_ZIP_Contains(void* ctx, char* Name);
+
+ /*
+ List of strings consisting of all names match the regular expression provided in regexp.
+ If no expression is provided, all names in the current open ZIP are returned.
+
+ See https://regex.sorokin.engineer/en/latest/regular_expressions.html for information on
+ the expression syntax and options.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL void ctx_ZIP_List(void* ctx, char*** ResultPtr, int32_t* ResultCount, char* RegExp);
+
+ /*
+ Extracts the contents of the file "FileName" from the current (open) ZIP file.
+ Returns a byte-string.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL void ctx_ZIP_Extract(void* ctx, int8_t** ResultPtr, int32_t* ResultCount, char* FileName);
+
+ DSS_CAPI_DLL void ctx_ZIP_Extract_GR(void* ctx, char* FileName);
+
+ /*
+ Closes the current open ZIP file.
+
+ (API Extension)
+ */
+ DSS_CAPI_DLL void ctx_ZIP_Close(void* ctx);
+
+ /*
+ Functions for the new API
+ */
+
+ /*
+ Extract the current properties as a JSON encoded string.
+ WARNING: this is unstable and subject to change.
+ */
+
+
+
+ /*
+ Returns the object name (direct access, no copy is done, no disposal required by the user; read only!)
+ */
+
+ /*
+ Returns the object's class name (direct access, no copy is done, no disposal required by the user; read only!)
+ */
+
+
+
+ /*
+ Activates an object. The object is set as the current
+ active DSSObject or CktElement, and in the list of its parent class.
+ If AllLists is true, other internal lists of OpenDSS are also
+ updated (implies slow/linear searches).
+ */
+
+ /*
+ Returns the pointer to the internal property fill sequence, and optionally
+ the highest value in CurrentCount (if not null).
+ */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#ifdef __cplusplus
} // extern "C"
diff --git a/src/CAPI/CAPI_ActiveClass.pas b/src/CAPI/CAPI_ActiveClass.pas
index 114fd4973..fd6e74b07 100644
--- a/src/CAPI/CAPI_ActiveClass.pas
+++ b/src/CAPI/CAPI_ActiveClass.pas
@@ -136,7 +136,7 @@ function ActiveClass_Get_ActiveClassParent(): PAnsiChar; CDECL;
begin
if DSSPrime.ActiveDSSClass = NIL then
begin
- Result := DSS_GetAsPAnsiChar(DSSPrime, 'Parent Class unknonwn');
+ Result := DSS_GetAsPAnsiChar(DSSPrime, 'Parent Class unknown');
Exit;
end;
@@ -147,7 +147,7 @@ function ActiveClass_Get_ActiveClassParent(): PAnsiChar; CDECL;
else if DSSPrime.ActiveDSSClass.ClassType.InheritsFrom(TPDClass) then
Result := DSS_GetAsPAnsiChar(DSSPrime, 'TPDClass')
else if DSSPrime.ActiveDSSClass.ClassType.InheritsFrom(TPCClass) then
- Result := DSS_GetAsPAnsiChar(DSSPrime, 'TPCClas') //NOTE: kept as "Clas" for compatibility
+ Result := DSS_GetAsPAnsiChar(DSSPrime, 'TPCClass')
else
Result := DSS_GetAsPAnsiChar(DSSPrime, 'Generic Object');
end;
diff --git a/src/CAPI/CAPI_Bus.pas b/src/CAPI/CAPI_Bus.pas
index 1f52c8375..9658cc58a 100644
--- a/src/CAPI/CAPI_Bus.pas
+++ b/src/CAPI/CAPI_Bus.pas
@@ -71,7 +71,7 @@ implementation
CAPI_Constants,
DSSGlobals,
Circuit,
- Ucomplex,
+ UComplex, DSSUcomplex,
MathUtil,
sysutils,
ExecHelper,
@@ -95,7 +95,7 @@ function _hasActiveBus(DSS: TDSSContext): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active bus found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, _('No active bus found! Activate one and retry.'), 8989);
end;
Exit;
end;
@@ -106,10 +106,10 @@ function _activeObj(DSS: TDSSContext; out obj: TDSSBus): Boolean; inline;
begin
Result := False;
obj := NIL;
-
+
if not _hasActiveBus(DSS) then
Exit;
-
+
obj := DSS.ActiveCircuit.Buses[DSS.ActiveCircuit.ActiveBusIndex];
Result := True;
end;
@@ -146,7 +146,7 @@ procedure Bus_Get_SeqVoltages(var ResultPtr: PDouble; ResultCount: PAPISize); CD
VPh, V012: Complex3;
begin
- if (InvalidCircuit(DSSPrime)) or
+ if (InvalidCircuit(DSSPrime)) or
(not ((DSSPrime.ActiveCircuit.ActiveBusIndex > 0) and (DSSPrime.ActiveCircuit.ActiveBusIndex <= DSSPrime.ActiveCircuit.NumBuses))) then
begin
DefaultResult(ResultPtr, ResultCount);
@@ -282,7 +282,7 @@ procedure Bus_Get_Isc(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
i, iV, NValues: Integer;
begin
- if (InvalidCircuit(DSSPrime)) or
+ if (InvalidCircuit(DSSPrime)) or
(not ((DSSPrime.ActiveCircuit.ActiveBusIndex > 0) and (DSSPrime.ActiveCircuit.ActiveBusIndex <= DSSPrime.ActiveCircuit.NumBuses))) then
begin
DefaultResult(ResultPtr, ResultCount);
@@ -325,7 +325,7 @@ procedure Bus_Get_Voc(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
i, iV, NValues: Integer;
begin
- if (InvalidCircuit(DSSPrime)) or
+ if (InvalidCircuit(DSSPrime)) or
(not ((DSSPrime.ActiveCircuit.ActiveBusIndex > 0) and (DSSPrime.ActiveCircuit.ActiveBusIndex <= DSSPrime.ActiveCircuit.NumBuses))) then
begin
DefaultResult(ResultPtr, ResultCount);
@@ -426,7 +426,7 @@ procedure Bus_Get_Zsc0(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
Z: Complex;
begin
- if (InvalidCircuit(DSSPrime)) or
+ if (InvalidCircuit(DSSPrime)) or
(not ((DSSPrime.ActiveCircuit.ActiveBusIndex > 0) and (DSSPrime.ActiveCircuit.ActiveBusIndex <= DSSPrime.ActiveCircuit.NumBuses))) then
begin
DefaultResult(ResultPtr, ResultCount);
@@ -454,13 +454,13 @@ procedure Bus_Get_Zsc1(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
Result: PDoubleArray0;
Z: Complex;
begin
- if (InvalidCircuit(DSSPrime)) or
+ if (InvalidCircuit(DSSPrime)) or
(not ((DSSPrime.ActiveCircuit.ActiveBusIndex > 0) and (DSSPrime.ActiveCircuit.ActiveBusIndex <= DSSPrime.ActiveCircuit.NumBuses))) then
begin
DefaultResult(ResultPtr, ResultCount);
Exit;
end;
-
+
with DSSPrime.ActiveCircuit do
begin
Z := Buses^[ActiveBusIndex].Zsc1;
@@ -485,7 +485,7 @@ procedure Bus_Get_ZscMatrix(var ResultPtr: PDouble; ResultCount: PAPISize); CDEC
begin
DefaultResult(ResultPtr, ResultCount);
- if (InvalidCircuit(DSSPrime)) or
+ if (InvalidCircuit(DSSPrime)) or
(not ((DSSPrime.ActiveCircuit.ActiveBusIndex > 0) and (DSSPrime.ActiveCircuit.ActiveBusIndex <= DSSPrime.ActiveCircuit.NumBuses))) then
Exit;
@@ -511,7 +511,7 @@ procedure Bus_Get_ZscMatrix(var ResultPtr: PDouble; ResultCount: PAPISize); CDEC
end
except
On E: Exception do
- DoSimpleMsg(DSSPrime, 'ZscMatrix Error: ' + E.message + CRLF, 5016);
+ DoSimpleMsg(DSSPrime, 'ZscMatrix Error: %s', [E.message], 5016);
end;
end;
@@ -524,7 +524,6 @@ procedure Bus_Get_ZscMatrix_GR(); CDECL;
//------------------------------------------------------------------------------
function Bus_ZscRefresh(): TAPIBoolean; CDECL;
begin
-
Result := FALSE; // Init in case of failure
if DSSPrime.DSSExecutive.DoZscRefresh = 0 then
@@ -540,7 +539,7 @@ procedure Bus_Get_YscMatrix(var ResultPtr: PDouble; ResultCount: PAPISize); CDEC
begin
DefaultResult(ResultPtr, ResultCount);
- if (InvalidCircuit(DSSPrime)) or
+ if (InvalidCircuit(DSSPrime)) or
(not ((DSSPrime.ActiveCircuit.ActiveBusIndex > 0) and (DSSPrime.ActiveCircuit.ActiveBusIndex <= DSSPrime.ActiveCircuit.NumBuses))) then
Exit;
@@ -565,7 +564,7 @@ procedure Bus_Get_YscMatrix(var ResultPtr: PDouble; ResultCount: PAPISize); CDEC
end
except
On E: Exception do
- DoSimpleMsg(DSSPrime, 'ZscMatrix Error: ' + E.message + CRLF, 5017);
+ DoSimpleMsg(DSSPrime, 'ZscMatrix Error: %s', [E.message], 5017);
end;
end;
@@ -649,7 +648,7 @@ function Bus_GetUniqueNodeNumber(StartNumber: Integer): Integer; CDECL;
Result := 0;
if InvalidCircuit(DSSPrime) then
Exit;
-
+
with DSSPrime.ActiveCircuit do
if ActiveBusIndex > 0 then
Result := Utilities.GetUniqueNodeNumber(DSSPrime, BusList.NameOfIndex(ActiveBusIndex), StartNumber);
@@ -665,7 +664,7 @@ procedure Bus_Get_CplxSeqVoltages(var ResultPtr: PDouble; ResultCount: PAPISize)
VPh, V012: Complex3;
begin
- if (InvalidCircuit(DSSPrime)) or
+ if (InvalidCircuit(DSSPrime)) or
(not ((DSSPrime.ActiveCircuit.ActiveBusIndex > 0) and (DSSPrime.ActiveCircuit.ActiveBusIndex <= DSSPrime.ActiveCircuit.NumBuses))) then
begin
DefaultResult(ResultPtr, ResultCount);
@@ -816,8 +815,8 @@ procedure Bus_Get_puVLL(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
NodeIdxi := FindIdx(jj); // Get the index of the Node that matches i
inc(jj);
until NodeIdxi > 0;
-
- // (2020-03-01) Changed in DSS C-API to avoid some corner
+
+ // (2020-03-01) Changed in DSS C-API to avoid some corner
// cases that resulted in infinite loops
for k := 1 to 3 do
begin
@@ -827,7 +826,7 @@ procedure Bus_Get_puVLL(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
else
inc(jj);
- if NodeIdxj > 0 then
+ if NodeIdxj > 0 then
break;
end;
if NodeIdxj = 0 then
@@ -838,13 +837,13 @@ procedure Bus_Get_puVLL(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
end;
//------------------------------------------------------------------------------------------------
with Solution do
- Volts := Csub(NodeV^[GetRef(NodeIdxi)], NodeV^[GetRef(NodeIdxj)]);
+ Volts := NodeV^[GetRef(NodeIdxi)] - NodeV^[GetRef(NodeIdxj)];
Result[iV] := Volts.re / BaseFactor;
Inc(iV);
Result[iV] := Volts.im / BaseFactor;
Inc(iV);
end;
- end; {With pBus}
+ end; // With pBus
end
else
begin // for 1-phase buses, do not attempt to compute.
@@ -900,8 +899,8 @@ procedure Bus_Get_VLL(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
NodeIdxi := FindIdx(jj); // Get the index of the Node that matches i
inc(jj);
until NodeIdxi > 0;
-
- // (2020-03-01) Changed in DSS C-API to avoid some corner
+
+ // (2020-03-01) Changed in DSS C-API to avoid some corner
// cases that resulted in infinite loops
for k := 1 to 3 do
begin
@@ -911,7 +910,7 @@ procedure Bus_Get_VLL(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
else
inc(jj);
- if NodeIdxj > 0 then
+ if NodeIdxj > 0 then
break;
end;
if NodeIdxj = 0 then
@@ -922,7 +921,7 @@ procedure Bus_Get_VLL(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
end;
//------------------------------------------------------------------------------------------------
with Solution do
- Volts := Csub(NodeV^[GetRef(NodeIdxi)], NodeV^[GetRef(NodeIdxj)]);
+ Volts := NodeV^[GetRef(NodeIdxi)] - NodeV^[GetRef(NodeIdxj)];
Result[iV] := Volts.re;
Inc(iV);
Result[iV] := Volts.im;
@@ -1011,7 +1010,7 @@ procedure Bus_Get_VMagAngle(var ResultPtr: PDouble; ResultCount: PAPISize); CDEC
DefaultResult(ResultPtr, ResultCount);
Exit;
end;
-
+
with DSSPrime.ActiveCircuit do
begin
Nvalues := pBus.NumNodesThisBus;
@@ -1044,7 +1043,7 @@ procedure Bus_Get_VMagAngle_GR(); CDECL;
//------------------------------------------------------------------------------
function Bus_Get_TotalMiles(): Double; CDECL;
-var
+var
pBus : TDSSBus;
begin
Result := 0.0;
@@ -1054,7 +1053,7 @@ function Bus_Get_TotalMiles(): Double; CDECL;
end;
//------------------------------------------------------------------------------
function Bus_Get_SectionID(): Integer; CDECL;
-var
+var
pBus : TDSSBus;
begin
Result := 0;
@@ -1157,7 +1156,7 @@ procedure Bus_Get_LineList_GR(); CDECL;
//------------------------------------------------------------------------------
procedure Bus_Get_LoadList(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
-{ Returns list of LOAD elements connected to this bus}
+// Returns list of LOAD elements connected to this bus
var
BusReference, j, k, LoadCount: Integer;
pElem: TDSSCktElement;
@@ -1173,7 +1172,7 @@ procedure Bus_Get_LoadList(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CD
with DSSPrime.ActiveCircuit do
begin
BusReference := ActiveBusIndex;
- { Count number of LOAD elements connected to this bus }
+ // Count number of LOAD elements connected to this bus
LoadCount := 0;
pElem := TDSSCktElement(Loads.First);
while pElem <> NIL do
@@ -1188,9 +1187,9 @@ procedure Bus_Get_LoadList(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CD
DefaultResult(ResultPtr, ResultCount, '');
Exit;
end;
-
- //TODO: same list of elements to avoid a second loop through them all?
-
+
+ //TODO: save list of elements to avoid a second loop through them all?
+
Result := DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, LoadCount);
k := 0;
pElem := TDSSCktElement(Loads.First);
@@ -1225,7 +1224,7 @@ procedure Bus_Get_ZSC012Matrix(var ResultPtr: PDouble; ResultCount: PAPISize); C
DefaultResult(ResultPtr, ResultCount);
Exit;
end;
-
+
with pBus do
begin
if NumNodesThisBus <> 3 then
@@ -1233,7 +1232,7 @@ procedure Bus_Get_ZSC012Matrix(var ResultPtr: PDouble; ResultCount: PAPISize); C
DefaultResult(ResultPtr, ResultCount);
Exit;
end;
-
+
Nvalues := SQR(NumNodesThisBus) * 2; // Should be 9 complex numbers
// Compute ZSC012 for 3-phase buses else leave it zeros
// ZSC012 = Ap2s Zsc As2p
diff --git a/src/CAPI/CAPI_CNData.pas b/src/CAPI/CAPI_CNData.pas
index a6e62bdaf..4aee9c846 100644
--- a/src/CAPI/CAPI_CNData.pas
+++ b/src/CAPI/CAPI_CNData.pas
@@ -8,12 +8,6 @@ interface
CNData,
CableData;
-type
- CableDataProps = (EpsR = 1, InsLayer, DiaIns, DiaCable);
- CNDataProps = (k = 1, DiaStrand, GmrStrand, Rstrand);
-
-procedure CableDataSetDefaults(prop: CableDataProps; conductor: TCableDataObj);
-
// Common to all classes
function CNData_Get_Count(): Integer; CDECL;
function CNData_Get_First(): Integer; CDECL;
@@ -80,6 +74,10 @@ implementation
DSSClass,
DSSHelper;
+const
+ ConductorPropOffset = ord(High(TCableDataProp)) + ord(High(TCNDataProp));
+ CableDataPropOffset = ord(High(TCNDataProp));
+
//------------------------------------------------------------------------------
function _activeObj(DSS: TDSSContext; out obj: TCNDataObj): boolean; inline;
begin
@@ -93,63 +91,13 @@ function _activeObj(DSS: TDSSContext; out obj: TCNDataObj): boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active CNData object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['CNData'], 8989);
end;
Exit;
end;
Result := True;
end;
-//------------------------------------------------------------------------------
-procedure CableDataSetDefaults(prop: CableDataProps; conductor: TCableDataObj);
-begin
- {Set defaults}
- with conductor do
- begin
- {Check for critical errors}
- case prop of
- CableDataProps.EpsR:
- if (FEpsR < 1.0) then
- DoSimpleMsg('Error: Insulation permittivity must be greater than one for CableData ' + Name, 999);
- CableDataProps.InsLayer:
- if (FInsLayer <= 0.0) then
- DoSimpleMsg('Error: Insulation layer thickness must be positive for CableData ' + Name, 999);
- CableDataProps.DiaIns:
- if (FDiaIns <= 0.0) then
- DoSimpleMsg('Error: Diameter over insulation layer must be positive for CableData ' + Name, 999);
- CableDataProps.DiaCable:
- if (FDiaCable <= 0.0) then
- DoSimpleMsg('Error: Diameter over cable must be positive for CableData ' + Name, 999);
- end;
- end;
-end;
-//------------------------------------------------------------------------------
-procedure CNDataSetDefaults(prop: CNDataProps; conductor: TCNDataObj);
-begin
- {Set defaults}
- with conductor do
- begin
- {Set defaults}
- case prop of
- CNDataProps.DiaStrand:
- if FGmrStrand <= 0.0 then
- FGmrStrand := 0.7788 * 0.5 * FDiaStrand;
- end;
-
- {Check for critical errors}
- case prop of
- CNDataProps.k:
- if (FkStrand < 2) then
- DoSimpleMsg('Error: Must have at least 2 concentric neutral strands for CNData ' + Name, 999);
- CNDataProps.DiaStrand:
- if (FDiaStrand <= 0.0) then
- DoSimpleMsg('Error: Neutral strand diameter must be positive for CNData ' + Name, 999);
- CNDataProps.GmrStrand:
- if (FGmrStrand <= 0.0) then
- DoSimpleMsg('Error: Neutral strand GMR must be positive for CNData ' + Name, 999);
- end;
- end;
-end;
//------------------------------------------------------------------------------
function CNData_Get_Count(): Integer; CDECL;
begin
@@ -177,13 +125,13 @@ function CNData_Get_Next(): Integer; CDECL;
//------------------------------------------------------------------------------
function CNData_Get_Name(): PAnsiChar; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := NIL;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, pCNData.Name);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.Name);
end;
//------------------------------------------------------------------------------
procedure CNData_Set_Name(const Value: PAnsiChar); CDECL;
@@ -194,7 +142,7 @@ procedure CNData_Set_Name(const Value: PAnsiChar); CDECL;
Exit;
if not DSSPrime.CNDataClass.SetActive(Value) then
- DoSimpleMsg(DSSPrime, 'CNData "' + Value + '" Not Found in Active Circuit.', 51008);
+ DoSimpleMsg(DSSPrime, 'CNData "%s" not found in Active Circuit.', [Value], 51008);
// Still same active object if not found
end;
@@ -216,440 +164,400 @@ procedure CNData_Get_AllNames_GR(); CDECL;
//------------------------------------------------------------------------------
function CNData_Get_NormAmps(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.NormAmps;
+ Result := elem.NormAmps;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_NormAmps(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pCNData.NormAmps := Value;
- ConductorSetDefaults(ConductorProps.NormAmps, pCNData);
+ elem.NormAmps := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.NormAmps))
end;
//------------------------------------------------------------------------------
function CNData_Get_EmergAmps(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.EmergAmps;
+ Result := elem.EmergAmps;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_EmergAmps(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pCNData.EmergAmps := Value;
- ConductorSetDefaults(ConductorProps.EmergAmps, pCNData);
+ elem.EmergAmps := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.EmergAmps))
end;
//------------------------------------------------------------------------------
function CNData_Get_Diameter(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FRadius * 2.0;
+ Result := elem.FRadius * 2.0;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_Diameter(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FRadius := Value / 2.0;
- ConductorSetDefaults(ConductorProps.diam, pCNData);
- end;
+ elem.FRadius := Value / 2.0;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.diam))
end;
//------------------------------------------------------------------------------
function CNData_Get_Radius(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FRadius;
+ Result := elem.FRadius;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_Radius(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FRadius := Value;
- ConductorSetDefaults(ConductorProps.Radius, pCNData);
- end;
+ elem.FRadius := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.Radius))
end;
//------------------------------------------------------------------------------
function CNData_Get_GMRac(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FGMR60;
+ Result := elem.FGMR60;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_GMRac(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FGMR60 := Value;
- ConductorSetDefaults(ConductorProps.GMRac, pCNData);
- end;
+ elem.FGMR60 := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.GMRac))
end;
//------------------------------------------------------------------------------
function CNData_Get_Rac(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FR60;
+ Result := elem.FR60;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_Rac(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FR60 := Value;
- ConductorSetDefaults(ConductorProps.Rac, pCNData);
- end;
+ elem.FR60 := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.Rac))
end;
//------------------------------------------------------------------------------
function CNData_Get_Rdc(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FRDC;
+ Result := elem.FRDC;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_Rdc(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FRDC := Value;
- ConductorSetDefaults(ConductorProps.Rdc, pCNData);
- end;
+ elem.FRDC := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.Rdc))
end;
//------------------------------------------------------------------------------
function CNData_Get_GMRUnits(): Integer; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FGMRUnits;
+ Result := elem.FGMRUnits;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_GMRUnits(Value: Integer); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
+ prevVal: Integer;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FGMRUnits := Value;
- ConductorSetDefaults(ConductorProps.GMRunits, pCNData);
- end;
+ prevVal := elem.FGMRUnits;
+ elem.FGMRUnits := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.GMRunits), prevVal)
end;
//------------------------------------------------------------------------------
function CNData_Get_RadiusUnits(): Integer; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FRadiusUnits;
+ Result := elem.FRadiusUnits;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_RadiusUnits(Value: Integer); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
+ prevVal: Integer;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FRadiusUnits := Value;
- ConductorSetDefaults(ConductorProps.radunits, pCNData);
- end;
+ prevVal := elem.FRadiusUnits;
+ elem.FRadiusUnits := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.radunits), prevVal)
end;
//------------------------------------------------------------------------------
function CNData_Get_ResistanceUnits(): Integer; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FResistanceUnits;
+ Result := elem.FResistanceUnits;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_ResistanceUnits(Value: Integer); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
+ prevVal: Integer;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FResistanceUnits := Value;
- ConductorSetDefaults(ConductorProps.Runits, pCNData);
- end;
+ prevVal := elem.FResistanceUnits;
+ elem.FResistanceUnits := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.Runits), prevVal)
end;
//------------------------------------------------------------------------------
function CNData_Get_EpsR(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FEpsR;
+ Result := elem.FEpsR;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_EpsR(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FEpsR := Value;
- CableDataSetDefaults(CableDataProps.EpsR, pCNData);
- end;
+ elem.FEpsR := Value;
+ elem.PropertySideEffects(CableDataPropOffset + ord(TCableDataProp.EpsR))
end;
//------------------------------------------------------------------------------
function CNData_Get_InsLayer(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FInsLayer;
+ Result := elem.FInsLayer;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_InsLayer(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FInsLayer := Value;
- CableDataSetDefaults(CableDataProps.InsLayer, pCNData);
- end;
+ elem.FInsLayer := Value;
+ elem.PropertySideEffects(CableDataPropOffset + ord(TCableDataProp.InsLayer))
end;
//------------------------------------------------------------------------------
function CNData_Get_DiaIns(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FDiaIns;
+ Result := elem.FDiaIns;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_DiaIns(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FDiaIns := Value;
- CableDataSetDefaults(CableDataProps.DiaIns, pCNData);
- end;
+ elem.FDiaIns := Value;
+ elem.PropertySideEffects(CableDataPropOffset + ord(TCableDataProp.DiaIns))
end;
//------------------------------------------------------------------------------
function CNData_Get_DiaCable(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FDiaCable;
+ Result := elem.FDiaCable;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_DiaCable(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FDiaCable := Value;
- CableDataSetDefaults(CableDataProps.DiaCable, pCNData);
- end;
+ elem.FDiaCable := Value;
+ elem.PropertySideEffects(CableDataPropOffset + ord(TCableDataProp.DiaCable))
end;
//------------------------------------------------------------------------------
function CNData_Get_k(): Integer; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FkStrand;
+ Result := elem.FkStrand;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_k(Value: Integer); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
+ prevVal: Integer;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FkStrand := Value;
- CNDataSetDefaults(CNDataProps.k, pCNData);
- end;
+ prevVal := elem.FkStrand;
+ elem.FkStrand := Value;
+ elem.PropertySideEffects(ord(TCNDataProp.k), prevVal)
end;
//------------------------------------------------------------------------------
function CNData_Get_DiaStrand(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FDiaStrand;
+ Result := elem.FDiaStrand;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_DiaStrand(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FDiaStrand := Value;
- CNDataSetDefaults(CNDataProps.DiaStrand, pCNData);
- end;
+ elem.FDiaStrand := Value;
+ elem.PropertySideEffects(ord(TCNDataProp.DiaStrand))
end;
//------------------------------------------------------------------------------
function CNData_Get_GmrStrand(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FGmrStrand;
+ Result := elem.FGmrStrand;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_GmrStrand(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FGmrStrand := Value;
- CNDataSetDefaults(CNDataProps.GmrStrand, pCNData);
- end;
+ elem.FGmrStrand := Value;
+ elem.PropertySideEffects(ord(TCNDataProp.GmrStrand))
end;
//------------------------------------------------------------------------------
function CNData_Get_RStrand(): Double; CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pCNData.FRStrand;
+ Result := elem.FRStrand;
end;
//------------------------------------------------------------------------------
procedure CNData_Set_RStrand(Value: Double); CDECL;
var
- pCNData: TCNDataObj;
+ elem: TCNDataObj;
begin
- if not _activeObj(DSSPrime, pCNData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- with pCNData do
- begin
- FRStrand := Value;
- CNDataSetDefaults(CNDataProps.RStrand, pCNData);
- end;
+ elem.FRStrand := Value;
+ elem.PropertySideEffects(ord(TCNDataProp.RStrand))
end;
//------------------------------------------------------------------------------
function CNData_Get_idx(): Integer; CDECL;
@@ -664,7 +572,7 @@ function CNData_Get_idx(): Integer; CDECL;
procedure CNData_Set_idx(Value: Integer); CDECL;
begin
if DSSPrime.CNDataClass.ElementList.Get(Value) = NIL then
- DoSimpleMsg(DSSPrime, 'Invalid CNData index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['CNData', Value], 656565);
end;
//------------------------------------------------------------------------------
end.
diff --git a/src/CAPI/CAPI_CapControls.pas b/src/CAPI/CAPI_CapControls.pas
index 67491cda0..30ddbe2d0 100644
--- a/src/CAPI/CAPI_CapControls.pas
+++ b/src/CAPI/CAPI_CapControls.pas
@@ -55,15 +55,18 @@ implementation
Executive,
ControlElem,
CapControl,
- CapControlVars,
SysUtils,
DSSPointerList,
Utilities,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ DSSObjectHelper;
+
+type
+ TObj = TCapControlObj;
//------------------------------------------------------------------------------
-function _activeObj(DSS: TDSSContext; out obj: TCapControlObj): Boolean; inline;
+function _activeObj(DSS: TDSSContext; out obj: TObj): Boolean; inline;
begin
Result := False;
obj := NIL;
@@ -75,7 +78,7 @@ function _activeObj(DSS: TDSSContext; out obj: TCapControlObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active CapControl object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['CapControl'], 8989);
end;
Exit;
end;
@@ -83,16 +86,34 @@ function _activeObj(DSS: TDSSContext; out obj: TCapControlObj): Boolean; inline;
Result := True;
end;
//------------------------------------------------------------------------------
-procedure Set_Parameter(DSS: TDSSContext; const parm: String; const val: String);
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: String); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.ParsePropertyValue(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Double); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.SetDouble(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Integer); overload;
var
- cmd: String;
- elem: TCapControlObj;
+ elem: TObj;
begin
if not _activeObj(DSS, elem) then
Exit;
DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
- cmd := Format('capcontrol.%s.%s=%s', [elem.Name, parm, val]);
- DSS.DSSExecutive.Command := cmd;
+ elem.SetInteger(idx, val);
end;
//------------------------------------------------------------------------------
procedure CapControls_Get_AllNames(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
@@ -112,7 +133,7 @@ procedure CapControls_Get_AllNames_GR(); CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_Capacitor(): PAnsiChar; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
@@ -122,7 +143,7 @@ function CapControls_Get_Capacitor(): PAnsiChar; CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_CTratio(): Double; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -132,7 +153,7 @@ function CapControls_Get_CTratio(): Double; CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_DeadTime(): Double; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -142,7 +163,7 @@ function CapControls_Get_DeadTime(): Double; CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_Delay(): Double; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -152,7 +173,7 @@ function CapControls_Get_Delay(): Double; CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_DelayOff(): Double; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -178,7 +199,7 @@ function CapControls_Get_Next(): Integer; CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_Mode(): Integer; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := dssCapControlVoltage;
if not _activeObj(DSSPrime, elem) then
@@ -202,17 +223,18 @@ function CapControls_Get_Mode(): Integer; CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_MonitoredObj(): PAnsiChar; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.ElementName);
+ if elem.MonitoredElement <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(elem.MonitoredElement.FullName));
end;
//------------------------------------------------------------------------------
function CapControls_Get_MonitoredTerm(): Integer; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -222,7 +244,7 @@ function CapControls_Get_MonitoredTerm(): Integer; CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_Name(): PAnsiChar; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
@@ -232,7 +254,7 @@ function CapControls_Get_Name(): PAnsiChar; CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_OFFSetting(): Double; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -242,7 +264,7 @@ function CapControls_Get_OFFSetting(): Double; CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_ONSetting(): Double; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -252,7 +274,7 @@ function CapControls_Get_ONSetting(): Double; CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_PTratio(): Double; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -262,7 +284,7 @@ function CapControls_Get_PTratio(): Double; CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_UseVoltOverride(): TAPIBoolean; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := FALSE;
if not _activeObj(DSSPrime, elem) then
@@ -272,7 +294,7 @@ function CapControls_Get_UseVoltOverride(): TAPIBoolean; CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_Vmax(): Double; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -282,7 +304,7 @@ function CapControls_Get_Vmax(): Double; CDECL;
//------------------------------------------------------------------------------
function CapControls_Get_Vmin(): Double; CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -292,32 +314,32 @@ function CapControls_Get_Vmin(): Double; CDECL;
//------------------------------------------------------------------------------
procedure CapControls_Set_Capacitor(const Value: PAnsiChar); CDECL;
begin
- Set_Parameter(DSSPrime, 'Capacitor', value);
+ Set_Parameter(DSSPrime, ord(TCapControlProp.Capacitor), value);
end;
//------------------------------------------------------------------------------
procedure CapControls_Set_CTratio(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'CTratio', FloatToStr(value));
+ Set_Parameter(DSSPrime, ord(TCapControlProp.CTratio), value);
end;
//------------------------------------------------------------------------------
procedure CapControls_Set_DeadTime(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'DeadTime', FloatToStr(value));
+ Set_Parameter(DSSPrime, ord(TCapControlProp.DeadTime), value);
end;
//------------------------------------------------------------------------------
procedure CapControls_Set_Delay(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Delay', FloatToStr(value));
+ Set_Parameter(DSSPrime, ord(TCapControlProp.Delay), value);
end;
//------------------------------------------------------------------------------
procedure CapControls_Set_DelayOff(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'DelayOff', FloatToStr(value));
+ Set_Parameter(DSSPrime, ord(TCapControlProp.DelayOff), value);
end;
//------------------------------------------------------------------------------
procedure CapControls_Set_Mode(Value: Integer); CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -337,12 +359,12 @@ procedure CapControls_Set_Mode(Value: Integer); CDECL;
//------------------------------------------------------------------------------
procedure CapControls_Set_MonitoredObj(const Value: PAnsiChar); CDECL;
begin
- Set_Parameter(DSSPrime, 'Element', value);
+ Set_Parameter(DSSPrime, ord(TCapControlProp.Element), value);
end;
//------------------------------------------------------------------------------
procedure CapControls_Set_MonitoredTerm(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'Terminal', IntToStr(value));
+ Set_Parameter(DSSPrime, ord(TCapControlProp.Terminal), value);
end;
//------------------------------------------------------------------------------
procedure CapControls_Set_Name(const Value: PAnsiChar); CDECL;
@@ -357,38 +379,38 @@ procedure CapControls_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'CapControl "' + Value + '" Not Found in Active Circuit.', 5003);
+ DoSimpleMsg(DSSPrime, 'CapControl "%d" not found in Active Circuit.', [Value], 5003);
end;
end;
//------------------------------------------------------------------------------
procedure CapControls_Set_OFFSetting(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'OffSetting', FloatToStr(value));
+ Set_Parameter(DSSPrime, ord(TCapControlProp.OffSetting), value);
end;
//------------------------------------------------------------------------------
procedure CapControls_Set_ONSetting(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'OnSetting', FloatToStr(value));
+ Set_Parameter(DSSPrime, ord(TCapControlProp.OnSetting), value);
end;
//------------------------------------------------------------------------------
procedure CapControls_Set_PTratio(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'PTratio', FloatToStr(value));
+ Set_Parameter(DSSPrime, ord(TCapControlProp.PTratio), value);
end;
//------------------------------------------------------------------------------
procedure CapControls_Set_UseVoltOverride(Value: TAPIBoolean); CDECL;
begin
- Set_Parameter(DSSPrime, 'VoltOverride', StrYorN(Value = True))
+ Set_Parameter(DSSPrime, ord(TCapControlProp.VoltOverride), Integer(Value));
end;
//------------------------------------------------------------------------------
procedure CapControls_Set_Vmax(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Vmax', FloatToStr(value));
+ Set_Parameter(DSSPrime, ord(TCapControlProp.Vmax), Value);
end;
//------------------------------------------------------------------------------
procedure CapControls_Set_Vmin(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Vmin', FloatToStr(value));
+ Set_Parameter(DSSPrime, ord(TCapControlProp.Vmin), Value);
end;
//------------------------------------------------------------------------------
function CapControls_Get_Count(): Integer; CDECL;
@@ -400,7 +422,7 @@ function CapControls_Get_Count(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure CapControls_Reset(); CDECL;
var
- elem: TCapControlObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -417,14 +439,14 @@ function CapControls_Get_idx(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure CapControls_Set_idx(Value: Integer); CDECL;
var
- pCapControl: TCapControlObj;
+ pCapControl: TObj;
begin
if InvalidCircuit(DSSPrime) then
Exit;
pCapControl := DSSPrime.ActiveCircuit.CapControls.Get(Value);
if pCapControl = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid CapControl index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['CapControl', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pCapControl;
diff --git a/src/CAPI/CAPI_Capacitors.pas b/src/CAPI/CAPI_Capacitors.pas
index 5407560ed..a800fe0bf 100644
--- a/src/CAPI/CAPI_Capacitors.pas
+++ b/src/CAPI/CAPI_Capacitors.pas
@@ -44,7 +44,11 @@ implementation
SysUtils,
DSSPointerList,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ DSSObjectHelper;
+
+type
+ TObj = TCapacitorObj;
//------------------------------------------------------------------------------
function _activeObj(DSS: TDSSContext; out obj: TCapacitorObj): Boolean; inline;
@@ -59,7 +63,7 @@ function _activeObj(DSS: TDSSContext; out obj: TCapacitorObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active Capacitor object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['Capacitor'], 8989);
end;
Exit;
end;
@@ -67,16 +71,34 @@ function _activeObj(DSS: TDSSContext; out obj: TCapacitorObj): Boolean; inline;
Result := True;
end;
//------------------------------------------------------------------------------
-procedure Set_Parameter(DSS: TDSSContext; const parm: String; const val: String);
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: String); overload;
var
- cmd: String;
- elem: TCapacitorObj;
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.ParsePropertyValue(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Double); overload;
+var
+ elem: TObj;
begin
if not _activeObj(DSS, elem) then
Exit;
DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
- cmd := Format('capacitor.%s.%s=%s', [elem.Name, parm, val]);
- DSS.DSSExecutive.Command := cmd;
+ elem.SetDouble(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Integer); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.SetInteger(idx, val);
end;
//------------------------------------------------------------------------------
procedure Capacitors_Get_AllNames(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
@@ -118,7 +140,7 @@ function Capacitors_Get_IsDelta(): TAPIBoolean; CDECL;
Result := FALSE;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := (elem.Connection > 0);
+ Result := (elem.Connection = TCapacitorConnection.Delta);
end;
//------------------------------------------------------------------------------
function Capacitors_Get_kV(): Double; CDECL;
@@ -167,17 +189,20 @@ procedure Capacitors_Set_IsDelta(Value: TAPIBoolean); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.Connection := Integer(Value);
+ if Value then
+ elem.Connection := TCapacitorConnection.Delta
+ else
+ elem.Connection := TCapacitorConnection.Wye;
end;
//------------------------------------------------------------------------------
procedure Capacitors_Set_kV(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'kv', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TCapacitorProp.kv), Value);
end;
//------------------------------------------------------------------------------
procedure Capacitors_Set_kvar(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'kvar', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TCapacitorProp.kvar), Value);
end;
//------------------------------------------------------------------------------
procedure Capacitors_Set_Name(const Value: PAnsiChar); CDECL;
@@ -192,13 +217,13 @@ procedure Capacitors_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'Capacitor "' + Value + '" Not Found in Active Circuit.', 5003);
+ DoSimpleMsg(DSSPrime, 'Capacitor "%s" not found in Active Circuit.', [Value], 5003);
end;
end;
//------------------------------------------------------------------------------
procedure Capacitors_Set_NumSteps(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'numsteps', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TCapacitorProp.NumSteps), Value);
end;
//------------------------------------------------------------------------------
function Capacitors_Get_Count(): Integer; CDECL;
@@ -272,8 +297,8 @@ procedure Capacitors_Set_States(ValuePtr: PInteger; ValueCount: TAPISize); CDECL
if (ValueCount <> elem.NumSteps) and DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSSPrime, Format('The number of states provided (%d) does not match the number of steps (%d) in the active capacitor.',
- [ValueCount, elem.NumSteps]),
+ DoSimpleMsg(DSSPrime, 'The number of states provided (%d) does not match the number of steps (%d) in the active capacitor.',
+ [ValueCount, elem.NumSteps],
8989
);
Exit;
@@ -344,7 +369,7 @@ procedure Capacitors_Set_idx(Value: Integer); CDECL;
pCapacitor := DSSPrime.ActiveCircuit.ShuntCapacitors.Get(Value);
if pCapacitor = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Capacitor index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid Capacitor index: "%d".', [Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pCapacitor;
diff --git a/src/CAPI/CAPI_Circuit.pas b/src/CAPI/CAPI_Circuit.pas
index 7b7c6d826..3a1827161 100644
--- a/src/CAPI/CAPI_Circuit.pas
+++ b/src/CAPI/CAPI_Circuit.pas
@@ -84,7 +84,7 @@ implementation
DSSClassDefs,
DSSGlobals,
Line,
- UComplex,
+ UComplex, DSSUcomplex,
sysutils,
CktElement,
DSSObject,
@@ -148,7 +148,7 @@ procedure Circuit_Get_LineLosses(var ResultPtr: PDouble; ResultCount: PAPISize);
Loss := Cmplx(0.0, 0.0);
while pLine <> NIL do
begin
- CAccum(Loss, pLine.Losses);
+ Loss += pLine.Losses;
pLine := Lines.Next;
end;
Result[0] := Loss.re * 0.001;
@@ -275,7 +275,7 @@ procedure Circuit_Get_AllElementNames(var ResultPtr: PPAnsiChar; ResultCount: PA
for i := 1 to NumDevices do
begin
with TDSSCktElement(CktElements.Get(i)) do
- Result[i - 1] := DSS_CopyStringAsPChar(ParentClass.Name + '.' + Name);
+ Result[i - 1] := DSS_CopyStringAsPChar(FullName);
end;
end
end;
@@ -304,7 +304,7 @@ procedure Circuit_Get_SubstationLosses(var ResultPtr: PDouble; ResultCount: PAPI
while pTransf <> NIL do
begin
if pTransf.Issubstation then
- Caccum(Loss, pTransf.Losses);
+ Loss += pTransf.Losses;
pTransf := Transformers.Next;
end;
Result[0] := Loss.re * 0.001;
@@ -338,7 +338,7 @@ procedure Circuit_Get_TotalPower(var ResultPtr: PDouble; ResultCount: PAPISize);
cPower := Cmplx(0.0, 0.0);
while pCktElem <> NIL do
begin
- CAccum(cPower, pcktElem.Power[1]);
+ cPower += pcktElem.Power[1];
pCktElem := Sources.Next;
end;
Result[0] := cPower.re * 0.001;
@@ -494,7 +494,7 @@ function Circuit_SetActiveElement(const FullName: PAnsiChar): Integer; CDECL;
Result := -1;
if InvalidCircuit(DSSPrime) then
begin
- DoSimpleMsg(DSSPrime, 'Create a circuit before trying to set an element active!', 5015);
+ DoSimpleMsg(DSSPrime, _('Create a circuit before trying to set an element active!'), 5015);
Exit;
end;
@@ -921,7 +921,7 @@ function Circuit_SetActiveClass(const ClassName: PAnsiChar): Integer; CDECL;
DevClassIndex := DSSPrime.ClassNames.Find(ClassName);
if DevClassIndex = 0 then
begin
- DoSimplemsg(DSSPrime, 'Error: Class ' + ClassName + ' not found.', 5016);
+ DoSimpleMsg(DSSPrime, 'Class %s not found.', [ClassName], 5016);
Exit;
end;
@@ -999,7 +999,7 @@ procedure Circuit_Get_YNodeOrder(var ResultPtr: PPAnsiChar; ResultCount: PAPISiz
for i := 1 to NumNodes do
begin
with MapNodeToBus^[i] do
- Result[k] := DSS_CopyStringAsPChar(Format('%s.%-d', [Uppercase(BusList.NameOfIndex(Busref)), NodeNum]));
+ Result[k] := DSS_CopyStringAsPChar(Format('%s.%-d', [AnsiUpperCase(BusList.NameOfIndex(Busref)), NodeNum]));
Inc(k);
end;
end
@@ -1075,7 +1075,7 @@ procedure Circuit_SetCktElementIndex(const Value: Integer); CDECL;
begin
if InvalidCircuit(DSSPrime) then
begin
- DoSimpleMsg(DSSPrime, 'Create a circuit before trying to set an element active!', 5015);
+ DoSimpleMsg(DSSPrime, _('Create a circuit before trying to set an element active!'), 5015);
Exit;
end;
@@ -1084,7 +1084,7 @@ procedure Circuit_SetCktElementIndex(const Value: Integer); CDECL;
if NumDevices > Value then
ActiveCktElement := CktElements.Get(Value + 1)
else
- DoSimpleMsg(DSSPrime, 'Invalid CktElement index', 5030);
+ DoSimpleMsg(DSSPrime, _('Invalid CktElement index'), 5030);
end;
end;
@@ -1092,7 +1092,7 @@ procedure Circuit_SetCktElementName(const Value: PAnsiChar); CDECL;
begin
if Circuit_SetActiveElement(Value) = -1 then
begin
- DoSimpleMsg(DSSPrime, Format('Invalid CktElement name "%s"', [Value]), 5031);
+ DoSimpleMsg(DSSPrime, 'Invalid CktElement name "%s"', [Value], 5031);
end;
end;
//------------------------------------------------------------------------------
diff --git a/src/CAPI/CAPI_CktElement.pas b/src/CAPI/CAPI_CktElement.pas
index c79b68dbf..ab7439a51 100644
--- a/src/CAPI/CAPI_CktElement.pas
+++ b/src/CAPI/CAPI_CktElement.pas
@@ -63,6 +63,8 @@ procedure CktElement_Get_AllVariableValues(var ResultPtr: PDouble; ResultCount:
procedure CktElement_Get_AllVariableValues_GR(); CDECL;
function CktElement_Get_Variable(const MyVarName: PAnsiChar; out Code: Integer): Double; CDECL;
function CktElement_Get_Variablei(Idx: Integer; out Code: Integer): Double; CDECL;
+procedure CktElement_Set_Variable(const MyVarName: PAnsiChar; out Code: Integer; Value: Double); CDECL;
+procedure CktElement_Set_Variablei(Idx: Integer; out Code: Integer; Value: Double); CDECL;
procedure CktElement_Get_NodeOrder(var ResultPtr: PInteger; ResultCount: PAPISize); CDECL;
procedure CktElement_Get_NodeOrder_GR(); CDECL;
function CktElement_Get_HasOCPDevice(): TAPIBoolean; CDECL;
@@ -87,7 +89,7 @@ implementation
CAPI_Constants,
DSSClassDefs,
DSSGlobals,
- UComplex,
+ UComplex, DSSUcomplex,
Sysutils,
PDElement,
PCElement,
@@ -222,7 +224,7 @@ function InvalidCktElement(DSS: TDSSContext): Boolean; inline;
Result := (DSSPrime.ActiveCircuit.ActiveCktElement = NIL);
if Result and DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active circuit element found! Activate one and retry.', 97800);
+ DoSimpleMsg(DSS, _('No active circuit element found! Activate one and retry.'), 97800);
end;
end;
//------------------------------------------------------------------------------
@@ -260,7 +262,7 @@ function CktElement_Get_Name(): PAnsiChar; CDECL;
Exit;
with DSSPrime.ActiveCircuit.ActiveCktElement do
- Result := DSS_GetAsPAnsiChar(DSSPrime, ParentClass.Name + '.' + Name);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, FullName);
end;
//------------------------------------------------------------------------------
function CktElement_Get_NumConductors(): Integer; CDECL;
@@ -308,9 +310,9 @@ procedure CktElement_Set_BusNames(ValuePtr: PPAnsiChar; ValueCount: TAPISize); C
if (Count <> ActiveCktElement.NTerms) AND (DSS_CAPI_EXT_ERRORS) then
begin
DoSimpleMsg(DSSPrime,
- Format('The number of buses provided (%d) does not match the number of terminals (%d).',
+ 'The number of buses provided (%d) does not match the number of terminals (%d).',
[ValueCount, Integer(ActiveCktElement.NTerms)]
- ), 97895
+ , 97895
);
Exit;
end;
@@ -597,7 +599,7 @@ procedure CktElement_Get_SeqPowers(var ResultPtr: PDouble; ResultCount: PAPISize
k := (j - 1) * NConds;
n := NodeRef^[k + 1];
Vph[1] := Solution.NodeV^[n]; // Get voltage at node
- S := Cmul(Vph[1], conjg(cBuffer^[k + 1])); // Compute power per phase
+ S := Vph[1] * cong(cBuffer^[k + 1]); // Compute power per phase
Result[icount] := S.re * 0.003; // 3-phase kW conversion
inc(icount);
Result[icount] := S.im * 0.003; // 3-phase kvar conversion
@@ -626,7 +628,7 @@ procedure CktElement_Get_SeqPowers(var ResultPtr: PDouble; ResultCount: PAPISize
Phase2SymComp(@Vph, @V012);
for i := 1 to 3 do
begin
- S := Cmul(V012[i], conjg(I012[i]));
+ S := V012[i] * cong(I012[i]);
Result[icount] := S.re * 0.003; // 3-phase kW conversion
inc(icount);
Result[icount] := S.im * 0.003; // 3-phase kW conversion
@@ -847,7 +849,7 @@ procedure CktElement_Get_Residuals(var ResultPtr: PDouble; ResultCount: PAPISize
for j := 1 to Nconds do
begin
inc(k);
- Caccum(cResid, CBuffer^[k]);
+ cResid += CBuffer^[k];
end;
Result[iV] := Cabs(cResid);
Inc(iV);
@@ -898,7 +900,13 @@ function CktElement_Get_DisplayName(): PAnsiChar; CDECL;
if InvalidCktElement(DSSPrime) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, DSSPrime.ActiveCircuit.ActiveCktElement.DisplayName)
+ if DSSPrime.ActiveCircuit.ActiveCktElement.DisplayName <> '' then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, DSSPrime.ActiveCircuit.ActiveCktElement.DisplayName)
+ else
+ Result := DSS_GetAsPAnsiChar(DSSPrime,
+ DSSPrime.ActiveCircuit.ActiveCktElement.ParentClass.Name + '_' +
+ DSSPrime.ActiveCircuit.ActiveCktElement.Name
+ );
end;
//------------------------------------------------------------------------------
@@ -942,7 +950,7 @@ function CktElement_Get_Controller(idx: Integer): PAnsiChar; CDECL;
begin
ctrl := ActiveCktElement.ControlElementList.Get(idx);
if ctrl <> NIL then
- Result := DSS_GetAsPAnsiChar(DSSPrime, Format('%s.%s', [ctrl.ParentClass.Name, ctrl.Name]));
+ Result := DSS_GetAsPAnsiChar(DSSPrime, ctrl.FullName);
end;
end;
end;
@@ -956,7 +964,7 @@ function CktElement_Get_EnergyMeter(): PAnsiChar; CDECL;
if InvalidCktElement(DSSPrime) then
Exit;
- if DSSPrime.ActiveCircuit.ActiveCktElement.HasEnergyMeter then
+ if Flg.HasEnergyMeter in DSSPrime.ActiveCircuit.ActiveCktElement.Flags then
begin
pd := DSSPrime.ActiveCircuit.ActiveCktElement as TPDElement;
Result := DSS_GetAsPAnsiChar(DSSPrime, pd.MeterObj.Name);
@@ -1209,6 +1217,54 @@ function CktElement_Get_Variablei(Idx: Integer; out Code: Integer): Double; CDEC
end;
end;
//------------------------------------------------------------------------------
+procedure CktElement_Set_Variable(const MyVarName: PAnsiChar; out Code: Integer; Value: Double); CDECL; //TODO: Remove Code and use Error interface?
+var
+ pPCElem: TPCElement;
+ VarIndex: Integer;
+begin
+ Code := 1; // Signifies an error; no value set
+
+ if InvalidCktElement(DSSPrime) then
+ Exit;
+
+ with DSSPrime.ActiveCircuit.ActiveCktElement do
+ begin
+ if (DSSObjType and BASECLASSMASK) <> PC_ELEMENT then
+ Exit;
+
+ pPCElem := (DSSPrime.ActiveCircuit.ActiveCktElement as TPCElement);
+ VarIndex := pPCElem.LookupVariable(MyVarName);
+ if (VarIndex > 0) and (VarIndex <= pPCElem.NumVariables) then
+ begin
+ pPCElem.Variable[VarIndex] := Value;
+ Code := 0; // Signify result is OK.
+ end;
+ end;
+end;
+//------------------------------------------------------------------------------
+procedure CktElement_Set_Variablei(Idx: Integer; out Code: Integer; Value: Double); CDECL;
+var
+ pPCElem: TPCElement;
+begin
+ Code := 1; // Signifies an error; no value set
+
+ if InvalidCktElement(DSSPrime) then
+ Exit;
+
+ with DSSPrime.ActiveCircuit.ActiveCktElement do
+ begin
+ if (DSSObjType and BASECLASSMASK) <> PC_ELEMENT then
+ Exit;
+
+ pPCElem := (DSSPrime.ActiveCircuit.ActiveCktElement as TPCElement);
+ if (Idx > 0) and (Idx <= pPCElem.NumVariables) then
+ begin
+ pPCElem.Variable[Idx] := Value;
+ Code := 0; // Signify result is OK.
+ end;
+ end;
+end;
+//------------------------------------------------------------------------------
procedure CktElement_Get_NodeOrder(var ResultPtr: PInteger; ResultCount: PAPISize); CDECL;
var
Result: PIntegerArray0;
@@ -1228,7 +1284,7 @@ procedure CktElement_Get_NodeOrder(var ResultPtr: PInteger; ResultCount: PAPISiz
if NodeRef = NIL then
begin
// Warn and exit
- DoSimpleMsg('Nodes are not initialized. Try solving the system first.', 15013);
+ DoSimpleMsg(_('Nodes are not initialized. Try solving the system first.'), 15013);
DefaultResult(ResultPtr, ResultCount);
Exit;
end;
@@ -1260,7 +1316,7 @@ function CktElement_Get_HasOCPDevice(): TAPIBoolean; CDECL;
Result := FALSE;
Exit;
end;
- Result := DSSPrime.ActiveCircuit.ActiveCktElement.HasOCPDevice;
+ Result := Flg.HasOCPDevice in DSSPrime.ActiveCircuit.ActiveCktElement.Flags;
end;
//------------------------------------------------------------------------------
function CktElement_Get_NumControls(): Integer; CDECL;
@@ -1399,7 +1455,7 @@ function CktElement_Get_IsIsolated(): TAPIBoolean; CDECL;
Exit;
end;
- Result := DSSPrime.ActiveCircuit.ActiveCktElement.IsIsolated;
+ Result := Flg.IsIsolated in DSSPrime.ActiveCircuit.ActiveCktElement.Flags;
end;
//------------------------------------------------------------------------------
procedure CktElement_Get_TotalPowers(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
@@ -1433,7 +1489,7 @@ procedure CktElement_Get_TotalPowers(var ResultPtr: PDouble; ResultCount: PAPISi
myEnd := NConds * j;
for i := myInit to myEnd do
begin
- myBuffer[j - 1] := cadd(myBuffer[j - 1], cBuffer^[i]);
+ myBuffer[j - 1] := myBuffer[j - 1] + cBuffer^[i];
end;
Result[iV + 0] := myBuffer[j - 1].re * 0.001;
Result[iV + 1] := myBuffer[j - 1].im * 0.001;
@@ -1459,7 +1515,7 @@ procedure CktElement_Get_NodeRef(var ResultPtr: PInteger; ResultCount: PAPISize)
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSSPrime, 'NodeRef is not populated for the current element!', 97801);
+ DoSimpleMsg(DSSPrime, _('NodeRef is not populated for the current element!'), 97801);
end;
Exit;
end;
diff --git a/src/CAPI/CAPI_CmathLib.pas b/src/CAPI/CAPI_CmathLib.pas
index b418d1ddf..8391d6d92 100644
--- a/src/CAPI/CAPI_CmathLib.pas
+++ b/src/CAPI/CAPI_CmathLib.pas
@@ -25,7 +25,7 @@ implementation
CAPI_Constants,
DSSClass,
DSSHelper,
- Ucomplex;
+ UComplex, DSSUcomplex;
procedure CmathLib_Get_cmplx(var ResultPtr: PDouble; ResultCount: PAPISize; RealPart, ImagPart: Double); CDECL;
var
@@ -96,7 +96,7 @@ procedure CmathLib_Get_cmul(var ResultPtr: PDouble; ResultCount: PAPISize; a1, b
cTemp: Complex;
begin
Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2);
- cTemp := cmul(cmplx(a1, b1), cmplx(a2, b2));
+ cTemp := cmplx(a1, b1) * cmplx(a2, b2);
Result[0] := cTemp.re;
Result[1] := cTemp.im;
end;
@@ -114,7 +114,7 @@ procedure CmathLib_Get_cdiv(var ResultPtr: PDouble; ResultCount: PAPISize; a1, b
cTemp: Complex;
begin
Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2);
- cTemp := cdiv(cmplx(a1, b1), cmplx(a2, b2));
+ cTemp := cmplx(a1, b1) / cmplx(a2, b2);
Result[0] := cTemp.re;
Result[1] := cTemp.im;
end;
diff --git a/src/CAPI/CAPI_CtrlQueue.pas b/src/CAPI/CAPI_CtrlQueue.pas
index 1e4490e76..1e16f8c6e 100644
--- a/src/CAPI/CAPI_CtrlQueue.pas
+++ b/src/CAPI/CAPI_CtrlQueue.pas
@@ -53,7 +53,7 @@ procedure CtrlQueue_Delete(ActionHandle: Integer); CDECL;
function CtrlQueue_Get_ActionCode(): Integer; CDECL;
begin
Result := 0;
- if InvalidCircuit(DSSPrime) then
+ if InvalidCircuit(DSSPrime) or (DSSPrime.ActiveAction = NIL) then
Exit;
Result := DSSPrime.ActiveAction^.ActionCode;
end;
@@ -61,7 +61,7 @@ function CtrlQueue_Get_ActionCode(): Integer; CDECL;
function CtrlQueue_Get_DeviceHandle(): Integer; CDECL;
begin
Result := 0;
- if InvalidCircuit(DSSPrime) then
+ if InvalidCircuit(DSSPrime) or (DSSPrime.ActiveAction = NIL) then
Exit;
Result := DSSPrime.ActiveAction^.DeviceHandle;
end;
@@ -87,7 +87,7 @@ procedure CtrlQueue_Show(); CDECL;
begin
if InvalidCircuit(DSSPrime) then
Exit;
- DSSPrime.ActiveCircuit.ControlQueue.ShowQueue(DSSPrime.OutputDirectory + 'COMProxy_ControlQueue.CSV');
+ DSSPrime.ActiveCircuit.ControlQueue.ShowQueue(DSSPrime.OutputDirectory + 'COMProxy_ControlQueue.csv');
end;
//------------------------------------------------------------------------------
procedure CtrlQueue_ClearActions(); CDECL;
diff --git a/src/CAPI/CAPI_DSS.pas b/src/CAPI/CAPI_DSS.pas
index 4f8e43ae0..11dcc97bd 100644
--- a/src/CAPI/CAPI_DSS.pas
+++ b/src/CAPI/CAPI_DSS.pas
@@ -40,8 +40,8 @@ function DSS_Get_AllowChangeDir(): TAPIBoolean; CDECL;
procedure DSS_Set_AllowChangeDir(Value: TAPIBoolean); CDECL;
procedure DSS_RegisterPlotCallback(cb: dss_callback_plot_t); CDECL;
procedure DSS_RegisterMessageCallback(cb: dss_callback_message_t); CDECL;
-
-
+function DSS_Get_AllowDOScmd(): TAPIBoolean; CDECL;
+procedure DSS_Set_AllowDOScmd(Value: TAPIBoolean); CDECL;
implementation
@@ -52,7 +52,6 @@ implementation
Exechelper,
sysUtils,
Executive,
- ParserDel,
CmdForms,
DSSHelper;
@@ -71,7 +70,7 @@ procedure DSS_Set_AllowForms(Value: TAPIBoolean); CDECL;
{$IFDEF WINDOWS}
if (Value) and (GetConsoleWindow() = 0) and ((@DSSPrime.DSSMessageCallback) = NIL) then
begin
- DoSimplemsg(DSSPrime, 'Cannot activate output with no console available! If you want to use a message output callback, register it before enabling AllowForms.', 5096);
+ DoSimplemsg(DSSPrime, _('Cannot activate output with no console available! If you want to use a message output callback, register it before enabling AllowForms.'), 5096);
Exit;
end;
{$ENDIF}
@@ -99,7 +98,11 @@ function DSS_Get_NumCircuits(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure DSS_ClearAll(); CDECL;
begin
+{$IFDEF DSS_CAPI_PM}
+ DSSPrime.DSSExecutive.DoClearAllCmd;
+{$ELSE}
DSSPrime.DSSExecutive.DoClearCmd;
+{$ENDIF}
end;
//------------------------------------------------------------------------------
function DSS_Get_Version(): PAnsiChar; CDECL;
@@ -118,7 +121,6 @@ procedure DSS_Get_Classes(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDE
i, k: Integer;
begin
-
Result := DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, DSSPrime.NumIntrinsicClasses);
k := 0;
for i := 1 to DSSPrime.NumIntrinsicClasses do
@@ -165,6 +167,7 @@ function DSS_Get_DataPath(): PAnsiChar; CDECL;
//------------------------------------------------------------------------------
procedure DSS_Set_DataPath(const Value: PAnsiChar); CDECL;
begin
+ DSSPrime.SetCurrentDSSDir(Value);
SetDataPath(DSSPrime, Value);
end;
//------------------------------------------------------------------------------
@@ -187,7 +190,7 @@ function DSS_SetActiveClass(const ClassName: PAnsiChar): Integer; CDECL;
DevClassIndex := DSSPrime.ClassNames.Find(ClassName);
if DevClassIndex = 0 then
begin
- DoSimplemsg(DSSPrime, 'Error: Class ' + ClassName + ' not found.', 5016);
+ DoSimpleMsg(DSSPrime, 'Class %s not found.', [ClassName], 5016);
Exit;
end;
@@ -257,4 +260,14 @@ procedure DSS_Set_COMErrorResults(Value: TAPIBoolean); CDECL;
DSS_CAPI_COM_DEFAULTS := Value;
end;
//------------------------------------------------------------------------------
+function DSS_Get_AllowDOScmd(): TAPIBoolean; CDECL;
+begin
+ Result := DSS_CAPI_ALLOW_DOSCMD;
+end;
+//------------------------------------------------------------------------------
+procedure DSS_Set_AllowDOScmd(Value: TAPIBoolean); CDECL;
+begin
+ DSS_CAPI_ALLOW_DOSCMD := Value;
+end;
+//------------------------------------------------------------------------------
end.
diff --git a/src/CAPI/CAPI_DSSElement.pas b/src/CAPI/CAPI_DSSElement.pas
index 28f63c34e..9d0eddd04 100644
--- a/src/CAPI/CAPI_DSSElement.pas
+++ b/src/CAPI/CAPI_DSSElement.pas
@@ -55,7 +55,7 @@ function DSSElement_Get_Name(): PAnsiChar; CDECL;
if (InvalidCircuit(DSSPrime)) or (DSSPrime.ActiveDSSObject = NIL) then
Exit;
with DSSPrime.ActiveDSSObject do
- Result := DSS_GetAsPAnsiChar(DSSPrime, ParentClass.Name + '.' + Name);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, FullName);
end;
//------------------------------------------------------------------------------
function DSSElement_Get_NumProperties(): Integer; CDECL;
diff --git a/src/CAPI/CAPI_DSSProperty.pas b/src/CAPI/CAPI_DSSProperty.pas
index 4968d4a22..e6fafab70 100644
--- a/src/CAPI/CAPI_DSSProperty.pas
+++ b/src/CAPI/CAPI_DSSProperty.pas
@@ -30,9 +30,9 @@ function IsPropIndexInvalid(DSS: TDSSContext; errorNum: Integer): Boolean;
if (DSS.FPropIndex > DSS.ActiveDSSObject.ParentClass.NumProperties) or (DSS.FPropIndex < 1) then
begin
- DoSimpleMsg(DSS, Format(
- 'Invalid property index "%d" for "%s.%s"',
- [DSS.FPropIndex, DSS.ActiveDSSObject.ParentClass.Name, DSS.ActiveDSSObject.Name]),
+ DoSimpleMsg(DSS,
+ 'Invalid property index "%d" for "%s"',
+ [DSS.FPropIndex, DSS.ActiveDSSObject.FullName],
errorNum
);
Result := TRUE;
@@ -47,13 +47,13 @@ function DSSProperty_Get_Description(): PAnsiChar; CDECL;
if DSSPrime.ActiveDSSObject = NIL then
begin
- DoSimpleMsg(DSSPrime, 'No active DSS object found! Activate one and try again.', 33100);
+ DoSimpleMsg(DSSPrime, _('No active DSS object found! Activate one and try again.'), 33100);
Exit;
end;
with DSSPrime.ActiveDSSObject.ParentClass do
if not IsPropIndexInvalid(DSSPrime, 33006) then
- Result := DSS_GetAsPAnsiChar(DSSPrime, PropertyHelp^[DSSPrime.FPropIndex]);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, GetPropertyHelp(DSSPrime.FPropIndex));
end;
//------------------------------------------------------------------------------
function DSSProperty_Get_Name(): PAnsiChar; CDECL;
@@ -66,7 +66,7 @@ function DSSProperty_Get_Name(): PAnsiChar; CDECL;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSSPrime, 'No active DSS object found! Activate one and try again.', 33101);
+ DoSimpleMsg(DSSPrime, _('No active DSS object found! Activate one and try again.'), 33101);
end;
Exit;
end;
@@ -87,7 +87,7 @@ function DSSProperty_Get_Val(): PAnsiChar; CDECL;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSSPrime, 'No active DSS object found! Activate one and try again.', 33102);
+ DoSimpleMsg(DSSPrime, _('No active DSS object found! Activate one and try again.'), 33102);
end;
Exit;
end;
@@ -96,7 +96,7 @@ function DSSProperty_Get_Val(): PAnsiChar; CDECL;
Exit;
with DSSPrime.ActiveDSSObject do
- Result := DSS_GetAsPAnsiChar(DSSPrime, PropertyValue[ParentClass.PropertyIdxMap[DSSPrime.FPropIndex]]);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, PropertyValue[DSSPrime.FPropIndex]);
end;
//------------------------------------------------------------------------------
@@ -109,7 +109,7 @@ procedure DSSProperty_Set_Val(const Value: PAnsiChar); CDECL;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSSPrime, 'No active DSS object found! Activate one and try again.', 33103);
+ DoSimpleMsg(DSSPrime, _('No active DSS object found! Activate one and try again.'), 33103);
end;
Exit;
end;
@@ -118,7 +118,7 @@ procedure DSSProperty_Set_Val(const Value: PAnsiChar); CDECL;
Exit;
with DSSPrime.ActiveDSSObject do
- DSSPrime.DSSExecutive.Command := 'Edit ' + ParentClass.Name + '.' + Name + ' ' + ParentClass.PropertyName^[DSSPrime.FPropIndex] + '=' + (Value);
+ DSSPrime.DSSExecutive.Command := 'Edit ' + FullName + ' ' + ParentClass.PropertyName^[DSSPrime.FPropIndex] + '=' + (Value);
end;
//------------------------------------------------------------------------------
procedure DSSProperty_Set_Index(const Value: Integer); CDECL;
@@ -130,7 +130,7 @@ procedure DSSProperty_Set_Index(const Value: Integer); CDECL;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSSPrime, 'No active DSS object found! Activate one and try again.', 33104);
+ DoSimpleMsg(DSSPrime, _('No active DSS object found! Activate one and try again.'), 33104);
end;
Exit;
end;
@@ -152,7 +152,7 @@ procedure DSSProperty_Set_Name(const Value: PAnsiChar); CDECL;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSSPrime, 'No active DSS object found! Activate one and try again.', 33105);
+ DoSimpleMsg(DSSPrime, _('No active DSS object found! Activate one and try again.'), 33105);
end;
Exit;
end;
@@ -171,9 +171,9 @@ procedure DSSProperty_Set_Name(const Value: PAnsiChar); CDECL;
end;
end;
- DoSimpleMsg(DSSPrime, Format(
- 'Invalid property name "%s" for "%s.%s"',
- [String(Value), DSSPrime.FPropClass.Name, DSSPrime.ActiveDSSObject.Name]),
+ DoSimpleMsg(DSSPrime,
+ 'Invalid property name "%s" for "%s"',
+ [String(Value), DSSPrime.ActiveDSSObject.FullName],
33003
);
end;
diff --git a/src/CAPI/CAPI_DSS_Executive.pas b/src/CAPI/CAPI_DSS_Executive.pas
index 445461118..8e2867201 100644
--- a/src/CAPI/CAPI_DSS_Executive.pas
+++ b/src/CAPI/CAPI_DSS_Executive.pas
@@ -48,12 +48,12 @@ function DSS_Executive_Get_Option(i: Integer): PAnsiChar; CDECL;
//------------------------------------------------------------------------------
function DSS_Executive_Get_CommandHelp(i: Integer): PAnsiChar; CDECL;
begin
- Result := DSS_GetAsPAnsiChar(DSSPrime, CommandHelp[i]);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, DSSHelp('Command.' + ExecCommand[i]));
end;
//------------------------------------------------------------------------------
function DSS_Executive_Get_OptionHelp(i: Integer): PAnsiChar; CDECL;
begin
- Result := DSS_GetAsPAnsiChar(DSSPrime, OptionHelp[i]);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, DSSHelp('Executive.' + ExecOption[i]));
end;
//------------------------------------------------------------------------------
function DSS_Executive_Get_OptionValue(i: Integer): PAnsiChar; CDECL;
diff --git a/src/CAPI/CAPI_DSSimComs.pas b/src/CAPI/CAPI_DSSimComs.pas
index 69b83587a..743427230 100644
--- a/src/CAPI/CAPI_DSSimComs.pas
+++ b/src/CAPI/CAPI_DSSimComs.pas
@@ -5,7 +5,7 @@ interface
uses
CAPI_Utils,
CAPI_Types,
- UComplex;
+ UComplex, DSSUcomplex;
procedure DSSimComs_BusVoltagepu(var ResultPtr: PDouble; ResultCount: PAPISize; Index: PtrUInt); CDECL;
procedure DSSimComs_BusVoltagepu_GR(Index: PtrUInt); CDECL;
diff --git a/src/CAPI/CAPI_Fuses.pas b/src/CAPI/CAPI_Fuses.pas
index 0840bfc20..8e61af392 100644
--- a/src/CAPI/CAPI_Fuses.pas
+++ b/src/CAPI/CAPI_Fuses.pas
@@ -50,10 +50,14 @@ implementation
DSSPointerlist,
DSSGlobals,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ DSSObjectHelper;
+
+type
+ TObj = TFuseObj;
//------------------------------------------------------------------------------
-function _activeObj(DSS: TDSSContext; out obj: TFuseObj): Boolean; inline;
+function _activeObj(DSS: TDSSContext; out obj: TObj): Boolean; inline;
begin
Result := False;
obj := NIL;
@@ -65,7 +69,7 @@ function _activeObj(DSS: TDSSContext; out obj: TFuseObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active Fuse object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['Fuse'], 8989);
end;
Exit;
end;
@@ -73,17 +77,34 @@ function _activeObj(DSS: TDSSContext; out obj: TFuseObj): Boolean; inline;
Result := True;
end;
//------------------------------------------------------------------------------
-procedure Set_Parameter(DSS: TDSSContext; const parm: String; const val: String);
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: String); overload;
var
- cmd: String;
- obj: TFuseObj;
+ elem: TObj;
begin
- if not _activeObj(DSS, obj) then
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.ParsePropertyValue(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Double); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.SetDouble(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Integer); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
Exit;
-
DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
- cmd := Format('Fuse.%s.%s=%s', [obj.Name, parm, val]);
- DSS.DSSExecutive.Command := cmd;
+ elem.SetInteger(idx, val);
end;
//------------------------------------------------------------------------------
procedure Fuses_Get_AllNames(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
@@ -127,7 +148,7 @@ function Fuses_Get_Next(): Integer; CDECL;
//------------------------------------------------------------------------------
function Fuses_Get_Name(): PAnsiChar; CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
@@ -148,24 +169,25 @@ procedure Fuses_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'Fuse "' + Value + '" Not Found in Active Circuit.', 77003);
+ DoSimpleMsg(DSSPrime, 'Fuse "%s" not found in Active Circuit.', [Value], 77003);
end;
end;
//------------------------------------------------------------------------------
function Fuses_Get_MonitoredObj(): PAnsiChar; CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.MonitoredElementName);
+ if elem.MonitoredElement <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(elem.MonitoredElement.FullName));
end;
//------------------------------------------------------------------------------
function Fuses_Get_MonitoredTerm(): Integer; CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -176,48 +198,49 @@ function Fuses_Get_MonitoredTerm(): Integer; CDECL;
//------------------------------------------------------------------------------
function Fuses_Get_SwitchedObj(): PAnsiChar; CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.ElementName);
+ if elem.ControlledElement <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(elem.ControlledElement.FullName));
end;
//------------------------------------------------------------------------------
procedure Fuses_Set_MonitoredObj(const Value: PAnsiChar); CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- Set_Parameter(DSSPrime, 'monitoredObj', Value);
+ Set_Parameter(DSSPrime, ord(TFuseProp.monitoredObj), Value);
end;
//------------------------------------------------------------------------------
procedure Fuses_Set_MonitoredTerm(Value: Integer); CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- Set_Parameter(DSSPrime, 'monitoredterm', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TFuseProp.monitoredterm), Value);
end;
//------------------------------------------------------------------------------
procedure Fuses_Set_SwitchedObj(const Value: PAnsiChar); CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- Set_Parameter(DSSPrime, 'SwitchedObj', Value);
+ Set_Parameter(DSSPrime, ord(TFuseProp.SwitchedObj), Value);
end;
//------------------------------------------------------------------------------
function Fuses_Get_SwitchedTerm(): Integer; CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -228,17 +251,17 @@ function Fuses_Get_SwitchedTerm(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Fuses_Set_SwitchedTerm(Value: Integer); CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- Set_Parameter(DSSPrime, 'SwitchedTerm', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TFuseProp.SwitchedTerm), Value);
end;
//------------------------------------------------------------------------------
function Fuses_Get_TCCcurve(): PAnsiChar; CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
begin
@@ -246,22 +269,25 @@ function Fuses_Get_TCCcurve(): PAnsiChar; CDECL;
Exit;
end;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.FuseCurve.Name);
+ If elem.FuseCurve <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.FuseCurve.Name)
+ else
+ Result := NIL;
end;
//------------------------------------------------------------------------------
procedure Fuses_Set_TCCcurve(const Value: PAnsiChar); CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- Set_Parameter(DSSPrime, 'FuseCurve', Value);
+ Set_Parameter(DSSPrime, ord(TFuseProp.FuseCurve), Value);
end;
//------------------------------------------------------------------------------
function Fuses_Get_RatedCurrent(): Double; CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
Result := -1.0;
if not _activeObj(DSSPrime, elem) then
@@ -272,17 +298,17 @@ function Fuses_Get_RatedCurrent(): Double; CDECL;
//------------------------------------------------------------------------------
procedure Fuses_Set_RatedCurrent(Value: Double); CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- Set_Parameter(DSSPrime, 'RatedCurrent', Format('%.8g ', [Value]));
+ Set_Parameter(DSSPrime, ord(TFuseProp.RatedCurrent), Format('%.8g ', [Value]));
end;
//------------------------------------------------------------------------------
function Fuses_Get_Delay(): Double; CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
Result := -1.0;
if not _activeObj(DSSPrime, elem) then
@@ -293,29 +319,35 @@ function Fuses_Get_Delay(): Double; CDECL;
//------------------------------------------------------------------------------
procedure Fuses_Open(); CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
+ i: Integer;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
elem.ControlledElement.Closed[0] := FALSE; // Open all phases
+
+ // TODO: check why original code doesn't do this (see related issue with GetPropertyValue)
+ for i := 1 to elem.ControlledElement.NPhases do
+ elem.FPresentState[i] := CTRL_OPEN; // Open all phases
end;
//------------------------------------------------------------------------------
procedure Fuses_Close(); CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
i: Integer;
begin
if (not _activeObj(DSSPrime, elem)) or (elem.ControlledElement = NIL) then
Exit;
for i := 1 to elem.ControlledElement.NPhases do
- elem.States[i] := CTRL_CLOSE; // Close all phases
+ elem.FPresentState[i] := CTRL_CLOSE; // Close all phases
+ elem.PropertySideEffects(ord(TFuseProp.State));
end;
//------------------------------------------------------------------------------
procedure Fuses_Reset(); CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -325,18 +357,18 @@ procedure Fuses_Reset(); CDECL;
//------------------------------------------------------------------------------
procedure Fuses_Set_Delay(Value: Double); CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- Set_Parameter(DSSPrime, 'Delay', Format('%.8g ', [Value]));
+ Set_Parameter(DSSPrime, ord(TFuseProp.Delay), Format('%.8g ', [Value]));
end;
//------------------------------------------------------------------------------
function Fuses_IsBlown(): TAPIBoolean; CDECL;
// Return TRUE if any phase blown
var
- elem: TFuseObj;
+ elem: TObj;
i: Integer;
begin
Result := FALSE;
@@ -358,14 +390,14 @@ function Fuses_Get_idx(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Fuses_Set_idx(Value: Integer); CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
if InvalidCircuit(DSSPrime) then
Exit;
elem := DSSPrime.ActiveCircuit.Fuses.Get(Value);
if elem = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Fuse index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['Fuse', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := elem;
@@ -373,7 +405,7 @@ procedure Fuses_Set_idx(Value: Integer); CDECL;
//------------------------------------------------------------------------------
function Fuses_Get_NumPhases(): Integer; CDECL;
var
- elem: TFuseObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -385,7 +417,7 @@ function Fuses_Get_NumPhases(): Integer; CDECL;
procedure Fuses_Get_NormalState(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
var
Result: PPAnsiCharArray0;
- elem: TFuseObj;
+ elem: TObj;
i: Integer;
begin
if (not _activeObj(DSSPrime, elem)) or (elem.ControlledElement = NIL) then
@@ -396,7 +428,7 @@ procedure Fuses_Get_NormalState(var ResultPtr: PPAnsiChar; ResultCount: PAPISize
Result := DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, elem.ControlledElement.NPhases);
for i := 1 to elem.ControlledElement.NPhases do
- if elem.NormalStates[i] = CTRL_CLOSE then
+ if elem.FNormalState[i] = CTRL_CLOSE then
Result[i - 1] := 'closed'
else
Result[i - 1] := 'open';
@@ -405,7 +437,7 @@ procedure Fuses_Get_NormalState(var ResultPtr: PPAnsiChar; ResultCount: PAPISize
procedure Fuses_Get_State(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
var
Result: PPAnsiCharArray0;
- elem: TFuseObj;
+ elem: TObj;
i: Integer;
begin
if (not _activeObj(DSSPrime, elem)) or (elem.ControlledElement = NIL) then
@@ -427,7 +459,7 @@ procedure Fuses_Set_State(ValuePtr: PPAnsiChar; ValueCount: TAPISize); CDECL;
Value: PPAnsiCharArray0;
i: Integer;
Count: Integer;
- elem: TFuseObj;
+ elem: TObj;
begin
if (not _activeObj(DSSPrime, elem)) or (elem.ControlledElement = NIL) then
Exit;
@@ -438,9 +470,9 @@ procedure Fuses_Set_State(ValuePtr: PPAnsiChar; ValueCount: TAPISize); CDECL;
if (Count <> elem.ControlledElement.NPhases) AND (DSS_CAPI_EXT_ERRORS) then
begin
DoSimpleMsg(DSSPrime,
- Format('The number of states provided (%d) does not match the number of phases (%d).',
- [ValueCount, Integer(elem.ControlledElement.NPhases)]
- ), 97896
+ 'The number of states provided (%d) does not match the number of phases (%d).',
+ [ValueCount, Integer(elem.ControlledElement.NPhases)],
+ 97896
);
Exit;
end;
@@ -451,11 +483,12 @@ procedure Fuses_Set_State(ValuePtr: PPAnsiChar; ValueCount: TAPISize); CDECL;
for i := 1 to Count Do
begin
if Length(Value[i - 1]) > 0 then
- case LowerCase(Value[i - 1])[1] of
- 'o': elem.States[i] := CTRL_OPEN;
- 'c': elem.States[i] := CTRL_CLOSE;
+ case AnsiLowerCase(Value[i - 1])[1] of
+ 'o': elem.FPresentState[i] := CTRL_OPEN;
+ 'c': elem.FPresentState[i] := CTRL_CLOSE;
end;
end;
+ elem.PropertySideEffects(ord(TFuseProp.State));
end;
//------------------------------------------------------------------------------
procedure Fuses_Set_NormalState(ValuePtr: PPAnsiChar; ValueCount: TAPISize); CDECL;
@@ -463,7 +496,7 @@ procedure Fuses_Set_NormalState(ValuePtr: PPAnsiChar; ValueCount: TAPISize); CDE
Value: PPAnsiCharArray0;
i: Integer;
Count: Integer;
- elem: TFuseObj;
+ elem: TObj;
begin
if (not _activeObj(DSSPrime, elem)) or (elem.ControlledElement = NIL) then
Exit;
@@ -474,9 +507,9 @@ procedure Fuses_Set_NormalState(ValuePtr: PPAnsiChar; ValueCount: TAPISize); CDE
if (Count <> elem.ControlledElement.NPhases) AND (DSS_CAPI_EXT_ERRORS) then
begin
DoSimpleMsg(DSSPrime,
- Format('The number of states provided (%d) does not match the number of phases (%d).',
- [ValueCount, Integer(elem.ControlledElement.NPhases)]
- ), 97897
+ 'The number of states provided (%d) does not match the number of phases (%d).',
+ [ValueCount, Integer(elem.ControlledElement.NPhases)],
+ 97897
);
Exit;
end;
@@ -487,11 +520,12 @@ procedure Fuses_Set_NormalState(ValuePtr: PPAnsiChar; ValueCount: TAPISize); CDE
for i := 1 to Count Do
begin
if Length(Value[i - 1]) > 0 then
- case LowerCase(Value[i - 1])[1] of
- 'o': elem.NormalStates[i] := CTRL_OPEN;
- 'c': elem.NormalStates[i] := CTRL_CLOSE;
+ case AnsiLowerCase(Value[i - 1])[1] of
+ 'o': elem.FNormalState[i] := CTRL_OPEN;
+ 'c': elem.FNormalState[i] := CTRL_CLOSE;
end;
end;
+ elem.NormalStateSet := True;
end;
//------------------------------------------------------------------------------
end.
diff --git a/src/CAPI/CAPI_GICSources.pas b/src/CAPI/CAPI_GICSources.pas
index a5ee1ecc6..8b1737e47 100644
--- a/src/CAPI/CAPI_GICSources.pas
+++ b/src/CAPI/CAPI_GICSources.pas
@@ -59,7 +59,7 @@ function _activeObj(DSS: TDSSContext; out obj: TGICSourceObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active GICSource object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['GICSource'], 8989);
end;
Exit;
end;
@@ -126,7 +126,7 @@ procedure GICSources_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'GICSource "' + Value + '" Not Found in Active Circuit.', 77003);
+ DoSimpleMsg(DSSPrime, 'GICSource "%s" not found in Active Circuit.', [Value], 77003);
end;
end;
//------------------------------------------------------------------------------
@@ -148,7 +148,12 @@ procedure GICSources_Set_Phases(Value: Integer); CDECL;
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.nphases := Value;
+ if Value < 1 then
+ begin
+ DoSimpleMsg(DSSPrime, '%s: Number of phases must be a positive integer!', [elem.FullName], 6568);
+ Exit;
+ end;
+ elem.Fnphases := Value;
Elem.NConds := Value; // Force reallocation of terminal info
end;
//------------------------------------------------------------------------------
@@ -344,7 +349,7 @@ procedure GICSources_Set_idx(Value: Integer); CDECL;
elem := DSSPrime.GICSourceClass.ElementList.Get(Value);
if elem = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid GICSource index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['GICSource', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := elem;
diff --git a/src/CAPI/CAPI_Generators.pas b/src/CAPI/CAPI_Generators.pas
index 40fd948ec..e010aa713 100644
--- a/src/CAPI/CAPI_Generators.pas
+++ b/src/CAPI/CAPI_Generators.pas
@@ -65,7 +65,7 @@ function _activeObj(DSS: TDSSContext; out obj: TGeneratorObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active Generator object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['Generator'], 8989);
end;
Exit;
end;
@@ -194,7 +194,7 @@ procedure Generators_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'Generator "' + Value + '" Not Found in Active Circuit.', 5003);
+ DoSimpleMsg(DSSPrime, 'Generator "%s" not found in Active Circuit.', [Value], 5003);
end;
end;
//------------------------------------------------------------------------------
@@ -270,7 +270,8 @@ procedure Generators_Set_kvar(Value: Double); CDECL;
if not _activeObj(DSSPrime, pGen) then
Exit;
- pGen.Presentkvar := Value;
+ pGen.kvarBase := Value;
+ pGen.PropertySideEffects(ord(TGeneratorProp.kvar))
end;
//------------------------------------------------------------------------------
procedure Generators_Set_kW(Value: Double); CDECL;
@@ -295,12 +296,18 @@ procedure Generators_Set_PF(Value: Double); CDECL;
//------------------------------------------------------------------------------
procedure Generators_Set_Phases(Value: Integer); CDECL;
var
- pGen: TGeneratorObj;
+ elem: TGeneratorObj;
begin
- if not _activeObj(DSSPrime, pGen) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pGen.Nphases := Value;
+ if Value < 1 then
+ begin
+ DoSimpleMsg(DSSPrime, '%s: Number of phases must be a positive integer!', [elem.FullName], 6568);
+ Exit;
+ end;
+ elem.FNphases := Value;
+ //TODO: missing side-effects?
end;
//------------------------------------------------------------------------------
function Generators_Get_Count(): Integer; CDECL;
@@ -328,7 +335,7 @@ procedure Generators_Set_idx(Value: Integer); CDECL;
pGen := DSSPrime.ActiveCircuit.Generators.Get(Value);
if pGen = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Generator index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['Generator', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pGen;
diff --git a/src/CAPI/CAPI_Isources.pas b/src/CAPI/CAPI_Isources.pas
index 06d781dd5..cf8f3c29d 100644
--- a/src/CAPI/CAPI_Isources.pas
+++ b/src/CAPI/CAPI_Isources.pas
@@ -49,7 +49,7 @@ function _activeObj(DSS: TDSSContext; out obj: TIsourceObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active ISource object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['ISource'], 8989);
end;
Exit;
end;
@@ -121,7 +121,7 @@ procedure ISources_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'ISource "' + Value + '" Not Found in Active Circuit.', 77003);
+ DoSimpleMsg(DSSPrime, 'ISource "%s" not found in Active Circuit.', [Value], 77003);
end;
end;
//------------------------------------------------------------------------------
@@ -199,7 +199,7 @@ procedure ISources_Set_idx(Value: Integer); CDECL;
pISource := DSSPrime.ISourceClass.ElementList.Get(Value);
if pISource = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid ISource index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['ISource', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pISource;
diff --git a/src/CAPI/CAPI_LineCodes.pas b/src/CAPI/CAPI_LineCodes.pas
index 1ec11abd0..e1f7af020 100644
--- a/src/CAPI/CAPI_LineCodes.pas
+++ b/src/CAPI/CAPI_LineCodes.pas
@@ -55,10 +55,10 @@ implementation
sysutils,
DSSGlobals,
LineUnits,
- ParserDel,
- Ucomplex,
+ UComplex, DSSUcomplex,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ DSSObjectHelper;
//------------------------------------------------------------------------------
function _activeObj(DSS: TDSSContext; out obj: TLineCodeObj): Boolean; inline;
@@ -73,7 +73,7 @@ function _activeObj(DSS: TDSSContext; out obj: TLineCodeObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active LineCode object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['LineCode'], 8989);
end;
Exit;
end;
@@ -123,7 +123,7 @@ procedure LineCodes_Set_Name(const Value: PAnsiChar); CDECL;
Exit;
if not DSSPrime.LineCodeClass.SetActive(Value) then
- DoSimpleMsg(DSSPrime, 'LineCode "' + Value + '" Not Found in Active Circuit.', 51008);
+ DoSimpleMsg(DSSPrime, 'LineCode "%s" not found in Active Circuit.', [Value], 51008);
// Still same active object if not found
end;
@@ -160,12 +160,9 @@ procedure LineCodes_Set_Units(Value: Integer); CDECL;
with pLineCode do
begin
if Value < dssLineUnitsMaxnum then
- begin
- DSSPrime.Parser.CmdString := Format('units=%s', [LineUnitsStr(Value)]);
- Edit;
- end
+ SetInteger(ord(TLineCodeProp.Units), Value)
else
- DoSimpleMsg('Invalid line units integer. Please enter a value within range.', 183);
+ DoSimpleMsg(_('Invalid line units integer. Please enter a value within range.'), 183);
end;
end;
//------------------------------------------------------------------------------
@@ -208,8 +205,7 @@ procedure LineCodes_Set_R1(Value: Double); CDECL;
if not _activeObj(DSSPrime, pLineCode) then
Exit;
- DSSPrime.Parser.CmdString := Format('R1=%g', [Value]);
- pLineCode.Edit;
+ pLineCode.SetDouble(ord(TLineCodeProp.R1), Value)
end;
//------------------------------------------------------------------------------
function LineCodes_Get_X1(): Double; CDECL;
@@ -230,8 +226,7 @@ procedure LineCodes_Set_X1(Value: Double); CDECL;
if not _activeObj(DSSPrime, pLineCode) then
Exit;
- DSSPrime.Parser.CmdString := Format('X1=%g', [Value]);
- pLineCode.Edit;
+ pLineCode.SetDouble(ord(TLineCodeProp.X1), Value);
end;
//------------------------------------------------------------------------------
function LineCodes_Get_R0(): Double; CDECL;
@@ -263,8 +258,7 @@ procedure LineCodes_Set_R0(Value: Double); CDECL;
if not _activeObj(DSSPrime, pLineCode) then
Exit;
- DSSPrime.Parser.CmdString := Format('R0=%g', [Value]);
- pLineCode.Edit;
+ pLineCode.SetDouble(ord(TLineCodeProp.R0), Value);
end;
//------------------------------------------------------------------------------
procedure LineCodes_Set_X0(Value: Double); CDECL;
@@ -274,8 +268,7 @@ procedure LineCodes_Set_X0(Value: Double); CDECL;
if not _activeObj(DSSPrime, pLineCode) then
Exit;
- DSSPrime.Parser.CmdString := Format('X0=%g', [Value]);
- pLineCode.Edit;
+ pLineCode.SetDouble(ord(TLineCodeProp.X0), Value);
end;
//------------------------------------------------------------------------------
function LineCodes_Get_C0(): Double; CDECL;
@@ -307,8 +300,7 @@ procedure LineCodes_Set_C0(Value: Double); CDECL;
if not _activeObj(DSSPrime, pLineCode) then
Exit;
- DSSPrime.Parser.CmdString := Format('C0=%g', [Value]);
- pLineCode.Edit;
+ pLineCode.SetDouble(ord(TLineCodeProp.C0), Value);
end;
//------------------------------------------------------------------------------
procedure LineCodes_Set_C1(Value: Double); CDECL;
@@ -318,8 +310,7 @@ procedure LineCodes_Set_C1(Value: Double); CDECL;
if not _activeObj(DSSPrime, pLineCode) then
Exit;
- DSSPrime.Parser.CmdString := Format('C1=%g', [Value]);
- pLineCode.Edit;
+ pLineCode.SetDouble(ord(TLineCodeProp.C1), Value);
end;
//------------------------------------------------------------------------------
procedure LineCodes_Get_Cmatrix(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
@@ -438,10 +429,10 @@ procedure LineCodes_Set_Cmatrix(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
begin
if (FNPhases * FNPhases) <> ValueCount then
begin
- DoSimpleMsg(Format(
+ DoSimpleMsg(
'The number of values provided (%d) does not match the expected (%d).',
- [ValueCount, FNPhases * FNPhases]
- ), 183);
+ [ValueCount, FNPhases * FNPhases],
+ 183);
Exit;
end;
@@ -473,10 +464,10 @@ procedure LineCodes_Set_Rmatrix(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
begin
if (FNPhases * FNPhases) <> ValueCount then
begin
- DoSimpleMsg(Format(
+ DoSimpleMsg(
'The number of values provided (%d) does not match the expected (%d).',
- [ValueCount, FNPhases * FNPhases]
- ), 183);
+ [ValueCount, FNPhases * FNPhases],
+ 183);
Exit;
end;
@@ -508,10 +499,10 @@ procedure LineCodes_Set_Xmatrix(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
begin
if (FNPhases * FNPhases) <> ValueCount then
begin
- DoSimpleMsg(Format(
+ DoSimpleMsg(
'The number of values provided (%d) does not match the expected (%d).',
- [ValueCount, FNPhases * FNPhases]
- ), 183);
+ [ValueCount, FNPhases * FNPhases],
+ 183);
Exit;
end;
@@ -594,7 +585,7 @@ function LineCodes_Get_idx(): Integer; CDECL;
procedure LineCodes_Set_idx(Value: Integer); CDECL;
begin
if DSSPrime.LineCodeClass.ElementList.Get(Value) = NIL then
- DoSimpleMsg(DSSPrime, 'Invalid LineCode index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['LineCode', Value], 656565);
end;
//------------------------------------------------------------------------------
end.
diff --git a/src/CAPI/CAPI_LineGeometries.pas b/src/CAPI/CAPI_LineGeometries.pas
index 3122b18da..cad666627 100644
--- a/src/CAPI/CAPI_LineGeometries.pas
+++ b/src/CAPI/CAPI_LineGeometries.pas
@@ -57,7 +57,7 @@ implementation
sysutils,
DSSGlobals,
LineUnits,
- Ucomplex,
+ UComplex, DSSUcomplex,
Line,
UcMatrix,
DSSClass,
@@ -77,7 +77,7 @@ function _activeObj(DSS: TDSSContext; out obj: TLineGeometryObj): Boolean; inlin
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active LineGeometry object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['LineGeometry'], 8989);
end;
Exit;
end;
@@ -126,7 +126,7 @@ procedure LineGeometries_Set_Name(const Value: PAnsiChar); CDECL;
if InvalidCircuit(DSSPrime) then
Exit;
if not DSSPrime.LineGeometryClass.SetActive(Value) then
- DoSimpleMsg(DSSPrime, 'LineGeometry "' + Value + '" Not Found in Active Circuit.', 51008);
+ DoSimpleMsg(DSSPrime, 'LineGeometry "%s" not found in Active Circuit.', [Value], 51008);
// Still same active object if not found
end;
@@ -148,7 +148,7 @@ procedure LineGeometries_Set_Nconds(Value: Integer); CDECL;
begin
if (Value < 1) then
begin
- DoSimpleMsg(DSSPrime, Format('Invalid number of conductors (%d). Please use a value within the valid range (>0).', [Value]), 183);
+ DoSimpleMsg(DSSPrime, 'Invalid number of conductors (%d). Please use a value within the valid range (>0).', [Value], 183);
Exit;
end;
if not _activeObj(DSSPrime, pLineGeometry) then
@@ -175,7 +175,7 @@ procedure LineGeometries_Set_Phases(Value: Integer); CDECL;
begin
if (Value < 1) then
begin
- DoSimpleMsg(DSSPrime, 'Invalid number of phases sent via C-API. Please enter a value within range.', 184);
+ DoSimpleMsg(DSSPrime, _('Invalid number of phases sent via C-API. Please enter a value within range.'), 184);
end;
if not _activeObj(DSSPrime, pLineGeometry) then
@@ -398,7 +398,7 @@ procedure LineGeometries_Set_Units(ValuePtr: PInteger; ValueCount: TAPISize); CD
begin
if Nconds <> ValueCount then
begin
- DoSimpleMsg(Format('The number of values provided (%d) does not match the number of conductors (%d).', [ValueCount, NConds]), 183);
+ DoSimpleMsg('The number of values provided (%d) does not match the number of conductors (%d).', [ValueCount, NConds], 183);
Exit;
end;
Move(ValuePtr[0], FUnits[1], ValueCount * SizeOf(Double));
@@ -440,7 +440,7 @@ procedure LineGeometries_Set_Ycoords(ValuePtr: PDouble; ValueCount: TAPISize); C
begin
if Nconds <> ValueCount then
begin
- DoSimpleMsg(Format('The number of values provided (%d) does not match the number of conductors (%d).', [ValueCount, NConds]), 188);
+ DoSimpleMsg('The number of values provided (%d) does not match the number of conductors (%d).', [ValueCount, NConds], 188);
Exit;
end;
Move(ValuePtr[0], FY[1], ValueCount * SizeOf(Double));
@@ -482,7 +482,7 @@ procedure LineGeometries_Set_Xcoords(ValuePtr: PDouble; ValueCount: TAPISize); C
begin
if Nconds <> ValueCount then
begin
- DoSimpleMsg(Format('The number of values provided (%d) does not match the number of conductors (%d).', [ValueCount, NConds]), 187);
+ DoSimpleMsg('The number of values provided (%d) does not match the number of conductors (%d).', [ValueCount, NConds], 187);
Exit;
end;
Move(ValuePtr[0], FX[1], ValueCount * SizeOf(Double));
@@ -523,6 +523,7 @@ procedure LineGeometries_Get_Conductors(var ResultPtr: PPAnsiChar; ResultCount:
if not _activeObj(DSSPrime, pLineGeometry) then
begin
DefaultResult(ResultPtr, ResultCount);
+ Exit;
end;
with pLineGeometry do
@@ -568,7 +569,7 @@ procedure LineGeometries_Set_idx(Value: Integer); CDECL;
if InvalidCircuit(DSSPrime) then
Exit;
if DSSPrime.LineGeometryClass.ElementList.Get(Value) = NIL then
- DoSimpleMsg(DSSPrime, 'Invalid LineGeometry index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['LineGeometry', Value], 656565);
end;
//------------------------------------------------------------------------------
end.
diff --git a/src/CAPI/CAPI_LineSpacings.pas b/src/CAPI/CAPI_LineSpacings.pas
index 57b680682..67d6bbd32 100644
--- a/src/CAPI/CAPI_LineSpacings.pas
+++ b/src/CAPI/CAPI_LineSpacings.pas
@@ -38,8 +38,7 @@ implementation
sysutils,
DSSGlobals,
LineUnits,
- ParserDel,
- Ucomplex,
+ UComplex, DSSUcomplex,
Line,
UcMatrix,
DSSClass,
@@ -58,7 +57,7 @@ function _activeObj(DSS: TDSSContext; out obj: TLineSpacingObj): Boolean; inline
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active LineSpacing object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['LineSpacing'], 8989);
end;
Exit;
end;
@@ -107,7 +106,7 @@ procedure LineSpacings_Set_Name(const Value: PAnsiChar); CDECL;
Exit;
if not DSSPrime.LineSpacingClass.SetActive(Value) then
- DoSimpleMsg(DSSPrime, 'LineSpacing "' + Value + '" Not Found in Active Circuit.', 51008);
+ DoSimpleMsg(DSSPrime, 'LineSpacing "%s" not found in Active Circuit.', [Value], 51008);
// Still same active object if not found
end;
@@ -129,12 +128,12 @@ procedure LineSpacings_Set_Nconds(Value: Integer); CDECL;
begin
if (Value < 1) then
begin
- DoSimpleMsg(DSSPrime, Format('Invalid number of conductors (%d) sent via C-API. Please use a value within the valid range (>0).', [Value]), 183);
+ DoSimpleMsg(DSSPrime, 'Invalid number of conductors (%d) sent via C-API. Please use a value within the valid range (>0).', [Value], 183);
end;
if not _activeObj(DSSPrime, pLineSpacing) then
Exit;
- pLineSpacing.DataChanged := TRUE;
- pLineSpacing.NWires := Value;
+ pLineSpacing.FNConds := Value;
+ pLineSpacing.PropertySideEffects(ord(TLineSpacingProp.NConds), 0);
end;
//------------------------------------------------------------------------------
function LineSpacings_Get_Phases(): Integer; CDECL;
@@ -193,10 +192,10 @@ procedure LineSpacings_Set_Ycoords(ValuePtr: PDouble; ValueCount: TAPISize); CDE
begin
if NWires <> ValueCount then
begin
- DoSimpleMsg(Format(
+ DoSimpleMsg(
'The number of values provided (%d) does not match the number of wires (%d).',
- [ValueCount, NWires]
- ), 183);
+ [ValueCount, NWires],
+ 183);
Exit;
end;
Move(ValuePtr^, FY[1], ValueCount * SizeOf(Double));
@@ -239,10 +238,10 @@ procedure LineSpacings_Set_Xcoords(ValuePtr: PDouble; ValueCount: TAPISize); CDE
begin
if NWires <> ValueCount then
begin
- DoSimpleMsg(Format(
+ DoSimpleMsg(
'The number of values provided (%d) does not match the number of wires (%d).',
- [ValueCount, NWires]
- ), 183);
+ [ValueCount, NWires],
+ 183);
Exit;
end;
Move(ValuePtr^, FX[1], ValueCount * SizeOf(Double));
@@ -296,7 +295,7 @@ function LineSpacings_Get_idx(): Integer; CDECL;
procedure LineSpacings_Set_idx(Value: Integer); CDECL;
begin
if DSSPrime.LineSpacingClass.ElementList.Get(Value) = NIL then
- DoSimpleMsg(DSSPrime, 'Invalid LineSpacing index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['LineSpacing', Value], 656565);
end;
//------------------------------------------------------------------------------
end.
diff --git a/src/CAPI/CAPI_Lines.pas b/src/CAPI/CAPI_Lines.pas
index 15ecffe81..4a98232b1 100644
--- a/src/CAPI/CAPI_Lines.pas
+++ b/src/CAPI/CAPI_Lines.pas
@@ -83,15 +83,15 @@ implementation
DSSClassDefs,
DSSGlobals,
CktElement,
- uComplex,
+ UComplex, DSSUcomplex,
ExecHelper,
Sysutils,
- ParserDel,
Math,
LineUnits,
XYCurve,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ DSSObjectHelper;
//------------------------------------------------------------------------------
function _activeObj(DSS: TDSSContext; out obj: TLineObj): Boolean; inline;
@@ -111,7 +111,7 @@ function _activeObj(DSS: TDSSContext; out obj: TLineObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active Line object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['Line'], 8989);
end;
Exit;
end;
@@ -121,8 +121,8 @@ function _activeObj(DSS: TDSSContext; out obj: TLineObj): Boolean; inline;
if obj = NIL {((CktElem.DssObjtype and CLASSMASK) <> LINE_ELEMENT)} then
begin
- DoSimpleMsg(DSS, 'Line Type Expected, but another found. DSS Class=' + CktElem.DSSClassName + CRLF +
- 'Element name=' + CktElem.Name, 5007);
+ DoSimpleMsg(DSS, 'Line Type Expected, but another found. DSS Class=%s, Element Name="%s"',
+ [CktElem.DSSClassName, CktElem.Name], 5007);
Exit;
end;
@@ -197,7 +197,8 @@ function Lines_Get_LineCode(): PAnsiChar; CDECL;
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.CondCode);
+ if elem.LineCodeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.LineCodeObj.Name);
end;
//------------------------------------------------------------------------------
function Lines_Get_Name(): PAnsiChar; CDECL;
@@ -242,7 +243,7 @@ function Lines_Get_X1(): Double; CDECL;
//------------------------------------------------------------------------------
function Lines_New(const Name: PAnsiChar): Integer; CDECL;
begin
- Result := DSSPrime.DSSExecutive.AddObject('line', Name); // Returns handle to object
+ DSSPrime.LineClass.NewObject(Name, True, Result);
end;
//------------------------------------------------------------------------------
procedure Lines_Set_Bus1(const Value: PAnsiChar); CDECL;
@@ -279,7 +280,14 @@ procedure Lines_Set_LineCode(const Value: PAnsiChar); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.FetchLineCode(Value);
+
+ elem.LineCodeObj := DSSPrime.LineCodeClass.Find(Value);
+ if elem.LineCodeObj = NIL then
+ begin
+ DoSimpleMsg(DSSPrime, 'LineCode "%s" not found.', [Value], 5009);
+ Exit;
+ end;
+ elem.FetchLineCode(); // Note: original didn't reproduce all side-effects from parser
elem.YprimInvalid := TRUE;
end;
//------------------------------------------------------------------------------
@@ -294,7 +302,7 @@ procedure Lines_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'Line "' + Value + '" Not Found in Active Circuit.', 5008);
+ DoSimpleMsg(DSSPrime, 'Line "%s" not found in Active Circuit.', [Value], 5008);
end;
end;
//------------------------------------------------------------------------------
@@ -304,7 +312,12 @@ procedure Lines_Set_Phases(Value: Integer); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.Nphases := Value;
+ if Value < 1 then
+ begin
+ DoSimpleMsg(DSSPrime, '%s: Number of phases must be a positive integer!', [elem.FullName], 6568);
+ Exit;
+ end;
+ elem.FNphases := Value;
elem.YprimInvalid := TRUE;
end;
//------------------------------------------------------------------------------
@@ -314,7 +327,7 @@ procedure Lines_Set_R1(Value: Double); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.R1 := Value;
+ elem.R1 := Value * elem.UnitsConvert;
elem.SymComponentsChanged := TRUE;
elem.YprimInvalid := TRUE;
end;
@@ -325,7 +338,7 @@ procedure Lines_Set_X1(Value: Double); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.X1 := Value;
+ elem.X1 := Value * elem.UnitsConvert;
elem.SymComponentsChanged := TRUE;
elem.YprimInvalid := TRUE;
end;
@@ -484,7 +497,7 @@ procedure Lines_Set_C0(Value: Double); CDECL;
Exit;
with elem do
begin
- C0 := Value * 1.0e-9;
+ C0 := Value * 1.0e-9 * UnitsConvert;
SymComponentsChanged := TRUE;
YprimInvalid := TRUE;
end;
@@ -498,7 +511,7 @@ procedure Lines_Set_C1(Value: Double); CDECL;
Exit;
with elem do
begin
- C1 := Value * 1.0e-9;
+ C1 := Value * 1.0e-9 * UnitsConvert;
SymComponentsChanged := TRUE;
YprimInvalid := TRUE;
end;
@@ -518,10 +531,10 @@ procedure Lines_Set_Cmatrix(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
begin
if (NPhases * NPhases) <> ValueCount then
begin
- DoSimpleMsg(Format(
+ DoSimpleMsg(
'The number of values provided (%d) does not match the expected (%d).',
- [ValueCount, NPhases * NPhases]
- ), 183);
+ [ValueCount, NPhases * NPhases],
+ 183);
Exit;
end;
@@ -545,7 +558,7 @@ procedure Lines_Set_R0(Value: Double); CDECL;
Exit;
with elem do
begin
- R0 := Value;
+ R0 := Value * UnitsConvert;
SymComponentsChanged := TRUE;
YprimInvalid := TRUE;
end;
@@ -565,10 +578,10 @@ procedure Lines_Set_Rmatrix(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
begin
if (NPhases * NPhases) <> ValueCount then
begin
- DoSimpleMsg(Format(
+ DoSimpleMsg(
'The number of values provided (%d) does not match the expected (%d).',
- [ValueCount, NPhases * NPhases]
- ), 183);
+ [ValueCount, NPhases * NPhases],
+ 183);
Exit;
end;
@@ -592,7 +605,7 @@ procedure Lines_Set_X0(Value: Double); CDECL;
Exit;
with elem do
begin
- X0 := Value;
+ X0 := Value * UnitsConvert;
SymComponentsChanged := TRUE;
YprimInvalid := TRUE;
end;
@@ -613,9 +626,9 @@ procedure Lines_Set_Xmatrix(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
begin
if (NPhases * NPhases) <> ValueCount then
begin
- DoSimpleMsg(Format('The number of values provided (%d) does not match the expected (%d).',
- [ValueCount, NPhases * NPhases]
- ), 183);
+ DoSimpleMsg('The number of values provided (%d) does not match the expected (%d).',
+ [ValueCount, NPhases * NPhases],
+ 183);
Exit;
end;
@@ -676,7 +689,8 @@ function Lines_Get_Geometry(): PAnsiChar; CDECL;
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.GeometryCode);
+ if elem.LineGeometryObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.LineGeometryObj.Name);
end;
//------------------------------------------------------------------------------
procedure Lines_Set_Geometry(const Value: PAnsiChar); CDECL;
@@ -685,12 +699,7 @@ procedure Lines_Set_Geometry(const Value: PAnsiChar); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- with elem do
- begin
- DSSPrime.Parser.CmdString := 'geometry=' + Value;
- Edit;
- YprimInvalid := TRUE;
- end;
+ elem.ParsePropertyValue(ord(TLineProp.geometry), Value); // calls FetchGeometryCode and sets YPrimInvalid
end;
//------------------------------------------------------------------------------
function Lines_Get_Rg(): Double; CDECL;
@@ -729,12 +738,8 @@ procedure Lines_Set_Rg(Value: Double); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- with elem do
- begin
- DSSPrime.Parser.CmdString := Format('rg=%.7g', [Value]);
- Edit;
- YprimInvalid := TRUE;
- end;
+ elem.SetDouble(ord(TLineProp.Rg), Value); //TODO: it doesn't seem to set YPrimInvalid
+ elem.YprimInvalid := TRUE;
end;
//------------------------------------------------------------------------------
procedure Lines_Set_Rho(Value: Double); CDECL;
@@ -743,12 +748,8 @@ procedure Lines_Set_Rho(Value: Double); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- with elem do
- begin
- DSSPrime.Parser.CmdString := Format('rho=%.7g', [Value]);
- Edit;
- YprimInvalid := TRUE;
- end;
+ elem.SetDouble(ord(TLineProp.rho), Value); //TODO: it doesn't seem to set YPrimInvalid
+ elem.YprimInvalid := TRUE;
end;
//------------------------------------------------------------------------------
procedure Lines_Set_Xg(Value: Double); CDECL;
@@ -757,16 +758,12 @@ procedure Lines_Set_Xg(Value: Double); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- with elem do
- begin
- DSSPrime.Parser.CmdString := Format('xg=%.7g', [Value]);
- Edit;
- YprimInvalid := TRUE;
- end;
+ elem.SetDouble(ord(TLineProp.xg), Value); //TODO: it doesn't seem to set YPrimInvalid
+ elem.YprimInvalid := TRUE;
end;
//------------------------------------------------------------------------------
procedure Lines_Get_Yprim(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
-{ Return the YPrim matrix for this element }
+// Return the YPrim matrix for this element
var
NValues: Integer;
cValues: pComplexArray;
@@ -806,9 +803,9 @@ procedure Lines_Set_Yprim(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
if not _activeObj(DSSPrime, elem) then
Exit;
- {Do Nothing for now}
+ // Do Nothing for now
- DoSimpleMsg(DSSPrime, 'Setting Yprim is currently not allowed.', 1833);
+ DoSimpleMsg(DSSPrime, _('Setting Yprim is currently not allowed.'), 1833);
end;
//------------------------------------------------------------------------------
function Lines_Get_NumCust(): Integer; CDECL;
@@ -866,7 +863,8 @@ function Lines_Get_Spacing(): PAnsiChar; CDECL;
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.SpacingCode);
+ if elem.LineSpacingObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.LineSpacingObj.Name);
end;
//------------------------------------------------------------------------------
procedure Lines_Set_Spacing(const Value: PAnsiChar); CDECL;
@@ -875,12 +873,7 @@ procedure Lines_Set_Spacing(const Value: PAnsiChar); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- with elem do
- begin
- DSSPrime.Parser.CmdString := 'spacing=' + Value;
- Edit;
- YprimInvalid := TRUE;
- end;
+ elem.ParsePropertyValue(ord(TLineProp.spacing), Value); // Sets YprimInvalid
end;
//------------------------------------------------------------------------------
function Lines_Get_Units(): Integer; CDECL;
@@ -894,10 +887,8 @@ function Lines_Get_Units(): Integer; CDECL;
end;
//------------------------------------------------------------------------------
procedure Lines_Set_Units(Value: Integer); CDECL;
-{
- This code assumes the present value of line units is NONE.
- The Set functions in this interface all set values in this length unit.
-}
+// This code assumes the present value of line units is NONE.
+// The Set functions in this interface all set values in this length unit.
var
elem: TLineObj;
begin
@@ -907,12 +898,11 @@ procedure Lines_Set_Units(Value: Integer); CDECL;
begin
if (Value >= dssLineUnitsNone) and (Value < dssLineUnitsMaxnum) then
begin
- DSSPrime.Parser.CmdString := Format('units=%s', [LineUnitsStr(Value)]);
- Edit;
+ ParsePropertyValue(ord(TLineProp.units), LineUnitsStr(Value));
YprimInvalid := TRUE;
end
else
- DoSimpleMsg('Invalid line units code. Please enter a value within range.', 183);
+ DoSimpleMsg(_('Invalid line units code. Please enter a value within range.'), 183);
end;
end;
//------------------------------------------------------------------------------
@@ -933,7 +923,7 @@ procedure Lines_Set_idx(Value: Integer); CDECL;
pLine := DSSPrime.ActiveCircuit.Lines.Get(Value);
if pLine = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Line index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['Line', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pLine;
@@ -983,8 +973,8 @@ procedure Lines_Set_IsSwitch(Value: TAPIBoolean); CDECL;
// Side effects from Line.pas
SymComponentsChanged := TRUE;
YprimInvalid := TRUE;
- GeometrySpecified := FALSE;
- SpacingSpecified := FALSE;
+ KillGeometrySpecified();
+ KillSpacingSpecified();
r1 := 1.0;
x1 := 1.0;
r0 := 1.0;
diff --git a/src/CAPI/CAPI_LoadShapes.pas b/src/CAPI/CAPI_LoadShapes.pas
index 0e57dcafc..3c78cd0f8 100644
--- a/src/CAPI/CAPI_LoadShapes.pas
+++ b/src/CAPI/CAPI_LoadShapes.pas
@@ -74,7 +74,7 @@ function _activeObj(DSS: TDSSContext; out obj: TLoadshapeObj): Boolean; inline;
obj := DSS.LoadshapeClass.GetActiveObj;
if obj = NIL then
begin
- DoSimpleMsg(DSS, 'No active Loadshape Object found.', 61001);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['Loadshape'], 61001);
Exit;
end;
@@ -98,7 +98,7 @@ procedure LoadShapes_Set_Name(const Value: PAnsiChar); CDECL;
Exit;
if DSSPrime.LoadshapeClass.SetActive(Value) then
Exit;
- DoSimpleMsg(DSSPrime, 'LoadShape "' + Value + '" Not Found in Active Circuit.', 77003);
+ DoSimpleMsg(DSSPrime, 'LoadShape "%s" not found in Active Circuit.', [Value], 77003);
end;
//------------------------------------------------------------------------------
function LoadShapes_Get_Count(): Integer; CDECL;
@@ -228,14 +228,14 @@ procedure LoadShapes_Set_Pmult(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
begin
if elem.ExternalMemory then
begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61101);
+ DoSimpleMsg(_('Data cannot be changed for LoadShapes with external memory! Reset the data first.'), 61101);
Exit;
end;
// Only accept the new data when the number of points match
if ValueCount <> NumPoints then
begin
- DoSimpleMsg(Format('The number of values (%d) does not match the current Npts (%d)!', [ValueCount, NumPoints]), 61100);
+ DoSimpleMsg('The number of values (%d) does not match the current Npts (%d)!', [ValueCount, NumPoints], 61100);
Exit;
end;
ReallocMem(sP, 0);
@@ -256,14 +256,14 @@ procedure LoadShapes_Set_Qmult(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
begin
if ExternalMemory then
begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61101);
+ DoSimpleMsg(_('Data cannot be changed for LoadShapes with external memory! Reset the data first.'), 61101);
Exit;
end;
// Only accept the new data when the number of points match
if ValueCount <> NumPoints then
begin
- DoSimpleMsg(Format('The number of values (%d) does not match the current Npts (%d)!', [ValueCount, NumPoints]), 61101);
+ DoSimpleMsg('The number of values (%d) does not match the current Npts (%d)!', [ValueCount, NumPoints], 61101);
Exit;
end;
ReallocMem(sQ, 0);
@@ -317,14 +317,14 @@ procedure LoadShapes_Set_TimeArray(ValuePtr: PDouble; ValueCount: TAPISize); CDE
begin
if elem.ExternalMemory then
begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61101);
+ DoSimpleMsg(_('Data cannot be changed for LoadShapes with external memory! Reset the data first.'), 61101);
Exit;
end;
// Only accept the new data when the number of points match
if ValueCount <> NumPoints then
begin
- DoSimpleMsg(Format('The number of values (%d) does not match the current Npts (%d)!', [ValueCount, NumPoints]), 61102);
+ DoSimpleMsg('The number of values (%d) does not match the current Npts (%d)!', [ValueCount, NumPoints], 61102);
Exit;
end;
ReallocMem(sH, 0);
@@ -393,7 +393,7 @@ procedure LoadShapes_Set_SInterval(Value: Double); CDECL;
//------------------------------------------------------------------------------
function LoadShapes_New(const Name: PAnsiChar): Integer; CDECL;
begin
- Result := DSSPrime.DSSExecutive.AddObject('loadshape', Name); // Returns handle to object
+ DSSPrime.LoadShapeClass.NewObject(Name, True, Result);
end;
//------------------------------------------------------------------------------
function LoadShapes_Get_PBase(): Double; CDECL;
@@ -461,7 +461,7 @@ function LoadShapes_Get_idx(): Integer; CDECL;
procedure LoadShapes_Set_idx(Value: Integer); CDECL;
begin
if DSSPrime.LoadShapeClass.ElementList.Get(Value) = NIL then
- DoSimpleMsg(DSSPrime, 'Invalid LoadShape index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['LoadShape', Value], 656565);
end;
//------------------------------------------------------------------------------
procedure LoadShapes_Set_Points(Npts: TAPISize; HoursPtr: Pointer; PMultPtr: Pointer; QMultPtr: Pointer; ExternalMemory: TAPIBoolean; IsFloat32: TAPIBoolean; Stride: Integer); CDECL;
diff --git a/src/CAPI/CAPI_Loads.pas b/src/CAPI/CAPI_Loads.pas
index fbc59761d..fb4d46d34 100644
--- a/src/CAPI/CAPI_Loads.pas
+++ b/src/CAPI/CAPI_Loads.pas
@@ -103,103 +103,13 @@ implementation
Executive,
Load,
SysUtils,
- math;
-
+ math,
+ DSSObjectHelper,
+ PCClass;
type
- LoadProps = (
- phases = 1, bus1 = 2, kV = 3, kW = 4, pf = 5, model = 6, yearly = 7, daily = 8, duty = 9, growth = 10, conn = 11, kvar = 12,
- Rneut = 13, Xneut = 14, status = 15, cls = 16, Vminpu = 17, Vmaxpu = 18, Vminnorm = 19, Vminemerg = 20, xfkVA = 21,
- allocationfactor = 22, kVA = 23, pctmean = 24, pctstddev = 25, CVRwatts = 26, CVRvars = 27, kwh = 28, kwhdays = 29,
- Cfactor = 30, CVRcurve = 31, NumCust = 32, ZIPV = 33, pctSeriesRL = 34, RelWeight = 35, Vlowpu = 36,
- puXharm = 37, XRhar = 38
- );
-
-//------------------------------------------------------------------------------
-procedure LoadPropSideEffects(DSS: TDSSContext; prop: LoadProps; load: TLoadObj); //incomplete
-begin
- with load do
- begin
- // << SIDE EFFECTS >>
- // keep kvar nominal up to date WITH kW and PF
- case prop of
- LoadProps.phases:
- begin
- // -> SetNcondsForConnection // Force Reallocation of terminal info
- case Connection of
- TLoadConnection.Wye:
- NConds := Fnphases + 1;
- TLoadConnection.Delta:
- case Fnphases of
- 1, 2:
- NConds := Fnphases + 1; // L-L and Open-delta
- else
- NConds := Fnphases;
- end;
- else {nada}
- end;
- // <- SetNcondsForConnection
- UpdateVoltageBases;
- end;
-
- LoadProps.kV:
- UpdateVoltageBases;
-
- LoadProps.kW:
- LoadSpecType := TLoadSpec.kW_PF;
-
- LoadProps.pf:
- begin
- PFChanged := TRUE;
- PFSpecified := TRUE;
- end;
- {Set shape objects; returns nil if not valid}
- {Sets the kW and kvar properties to match the peak kW demand from the Loadshape}
- LoadProps.yearly:
- begin
- YearlyShapeObj := DSS.LoadShapeClass.Find(YearlyShape);
- if Assigned(YearlyShapeObj) then
- with YearlyShapeObj do
- if UseActual then
- SetkWkvar(MaxP, MaxQ);
- end;
- LoadProps.daily:
- begin
- DailyShapeObj := DSS.LoadShapeClass.Find(DailyShape);
- if Assigned(DailyShapeObj) then
- with DailyShapeObj do
- if UseActual then
- SetkWkvar(MaxP, MaxQ);
- {If Yearly load shape is not yet defined, make it the same as Daily}
- if YearlyShapeObj = NIL then
- YearlyShapeObj := DailyShapeObj;
- end;
- LoadProps.duty:
- begin
- DutyShapeObj := DSS.LoadShapeClass.Find(DutyShape);
- if Assigned(DutyShapeObj) then
- with DutyShapeObj do
- if UseActual then
- SetkWkvar(MaxP, MaxQ);
- end;
- LoadProps.growth:
- GrowthShapeObj := DSS.GrowthShapeClass.Find(GrowthShape);
-
- LoadProps.kvar:
- begin
- LoadSpecType := TLoadSpec.kW_kvar;
- PFSpecified := FALSE;
- end;// kW, kvar
- {*** see set_xfkva, etc 21, 22: LoadSpectype := 3; // XFKVA*AllocationFactor, PF }
- LoadProps.pctMean:
- LoadSpecType := TLoadSpec.kVA_PF; // kVA, PF
- {*** see set_kwh, etc 28..30: LoadSpecType := 4; // kWh, days, cfactor, PF }
- LoadProps.CVRCurve:
- CVRShapeObj := DSS.LoadShapeClass.Find(CVRshape);
- end;
- end;
-end;
-//------------------------------------------------------------------------------
-function _activeObj(DSS: TDSSContext; out obj: TLoadObj): Boolean; inline;
+ TObj = TLoadObj;
+//---------------------------------------------------------
+function _activeObj(DSS: TDSSContext; out obj: TObj): Boolean; inline;
begin
Result := False;
obj := NIL;
@@ -211,7 +121,7 @@ function _activeObj(DSS: TDSSContext; out obj: TLoadObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active Load object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['Load'], 8989);
end;
Exit;
end;
@@ -219,17 +129,34 @@ function _activeObj(DSS: TDSSContext; out obj: TLoadObj): Boolean; inline;
Result := True;
end;
//------------------------------------------------------------------------------
-procedure Set_Parameter(DSS: TDSSContext; const parm: String; const val: String);
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: String); overload;
var
- cmd: String;
- elem: TLoadObj;
+ elem: TObj;
begin
if not _activeObj(DSS, elem) then
Exit;
-
DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
- cmd := Format('load.%s.%s=%s', [elem.Name, parm, val]);
- DSS.DSSExecutive.Command := cmd;
+ elem.ParsePropertyValue(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Double); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.SetDouble(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Integer); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.SetInteger(idx, val);
end;
//------------------------------------------------------------------------------
procedure Loads_Get_AllNames(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
@@ -273,14 +200,14 @@ function Loads_Get_idx(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Loads_Set_idx(Value: Integer); CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
if InvalidCircuit(DSSPrime) then
Exit;
pLoad := DSSPrime.ActiveCircuit.Loads.Get(Value);
if pLoad = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Load index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['Load', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pLoad;
@@ -288,7 +215,7 @@ procedure Loads_Set_idx(Value: Integer); CDECL;
//------------------------------------------------------------------------------
function Loads_Get_Name(): PAnsiChar; CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, pLoad) then
@@ -308,13 +235,13 @@ procedure Loads_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'Load "' + Value + '" Not Found in Active Circuit.', 5003);
+ DoSimpleMsg(DSSPrime, 'Load "%s" not found in Active Circuit.', [Value], 5003);
end;
end;
//------------------------------------------------------------------------------
function Loads_Get_kV(): Double; CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, pLoad) then
@@ -325,7 +252,7 @@ function Loads_Get_kV(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_kvar(): Double; CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, pLoad) then
@@ -336,7 +263,7 @@ function Loads_Get_kvar(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_kW(): Double; CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, pLoad) then
@@ -347,7 +274,7 @@ function Loads_Get_kW(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_PF(): Double; CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, pLoad) then
@@ -358,18 +285,18 @@ function Loads_Get_PF(): Double; CDECL;
//------------------------------------------------------------------------------
procedure Loads_Set_kV(Value: Double); CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
if not _activeObj(DSSPrime, pLoad) then
Exit;
pLoad.kVLoadBase := Value;
- pLoad.UpdateVoltageBases; // side effects
+ pload.PropertySideEffects(ord(TLoadProp.kV));
end;
//------------------------------------------------------------------------------
procedure Loads_Set_kvar(Value: Double); CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
if not _activeObj(DSSPrime, pLoad) then
Exit;
@@ -381,7 +308,7 @@ procedure Loads_Set_kvar(Value: Double); CDECL;
//------------------------------------------------------------------------------
procedure Loads_Set_kW(Value: Double); CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
if not _activeObj(DSSPrime, pLoad) then
Exit;
@@ -393,7 +320,7 @@ procedure Loads_Set_kW(Value: Double); CDECL;
//------------------------------------------------------------------------------
procedure Loads_Set_PF(Value: Double); CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
if not _activeObj(DSSPrime, pLoad) then
Exit;
@@ -412,7 +339,7 @@ function Loads_Get_Count(): Integer; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_AllocationFactor(): Double; CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, pLoad) then
@@ -423,7 +350,7 @@ function Loads_Get_AllocationFactor(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_Cfactor(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -434,7 +361,7 @@ function Loads_Get_Cfactor(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_Class_(): Integer; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -445,18 +372,19 @@ function Loads_Get_Class_(): Integer; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_CVRcurve(): PAnsiChar; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.CVRshape);
+ if elem.CVRshapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.CVRshapeObj.Name);
end;
//------------------------------------------------------------------------------
function Loads_Get_CVRvars(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -467,7 +395,7 @@ function Loads_Get_CVRvars(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_CVRwatts(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -478,40 +406,43 @@ function Loads_Get_CVRwatts(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_daily(): PAnsiChar; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.DailyShape);
+ if elem.DailyShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.DailyShapeObj.Name);
end;
//------------------------------------------------------------------------------
function Loads_Get_duty(): PAnsiChar; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.DailyShape);
+ if elem.DutyShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.DutyShapeObj.Name);
end;
//------------------------------------------------------------------------------
function Loads_Get_Growth(): PAnsiChar; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.GrowthShape);
+ if elem.GrowthShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.GrowthShapeObj.Name);
end;
//------------------------------------------------------------------------------
function Loads_Get_IsDelta(): TAPIBoolean; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := FALSE;
if not _activeObj(DSSPrime, elem) then
@@ -521,7 +452,7 @@ function Loads_Get_IsDelta(): TAPIBoolean; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_kva(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -531,7 +462,7 @@ function Loads_Get_kva(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_kwh(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -541,7 +472,7 @@ function Loads_Get_kwh(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_kwhdays(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -551,7 +482,7 @@ function Loads_Get_kwhdays(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_Model(): Integer; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := dssLoadConstPQ;
if not _activeObj(DSSPrime, elem) then
@@ -579,7 +510,7 @@ function Loads_Get_Model(): Integer; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_NumCust(): Integer; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -589,7 +520,7 @@ function Loads_Get_NumCust(): Integer; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_PctMean(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -599,7 +530,7 @@ function Loads_Get_PctMean(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_PctStdDev(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -609,7 +540,7 @@ function Loads_Get_PctStdDev(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_Rneut(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -619,31 +550,32 @@ function Loads_Get_Rneut(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_Spectrum(): PAnsiChar; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.Spectrum);
+ if elem.SpectrumObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.SpectrumObj.Name);
end;
//------------------------------------------------------------------------------
function Loads_Get_Status(): Integer; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := dssLoadVariable;
if not _activeObj(DSSPrime, elem) then
Exit;
- if elem.ExemptLoad then
+ if elem.status = TLoadStatus.Exempt then
Result := dssLoadExempt
- else if elem.FixedLoad then
+ else if elem.status = TLoadStatus.Fixed then
Result := dssLoadFixed;
end;
//------------------------------------------------------------------------------
function Loads_Get_Vmaxpu(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -653,7 +585,7 @@ function Loads_Get_Vmaxpu(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_Vminemerg(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -663,7 +595,7 @@ function Loads_Get_Vminemerg(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_Vminnorm(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -673,7 +605,7 @@ function Loads_Get_Vminnorm(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_Vminpu(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -683,7 +615,7 @@ function Loads_Get_Vminpu(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_xfkVA(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -693,7 +625,7 @@ function Loads_Get_xfkVA(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_Xneut(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -703,81 +635,83 @@ function Loads_Get_Xneut(): Double; CDECL;
//------------------------------------------------------------------------------
function Loads_Get_Yearly(): PAnsiChar; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.YearlyShape);
+
+ if elem.YearlyShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.YearlyShapeObj.Name);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_AllocationFactor(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'AllocationFactor', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.AllocationFactor), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_Cfactor(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Cfactor', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.Cfactor), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_Class_(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'Class', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.cls), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_CVRcurve(const Value: PAnsiChar); CDECL;
begin
- Set_Parameter(DSSPrime, 'CVRcurve', Value);
+ Set_Parameter(DSSPrime, ord(TLoadProp.CVRcurve), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_CVRvars(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'CVRvars', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.CVRvars), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_CVRwatts(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'CVRwatts', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.CVRwatts), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_daily(const Value: PAnsiChar); CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.DailyShape := Value;
- LoadPropSideEffects(DSSPrime, LoadProps.daily, elem);
+ elem.DailyShapeObj := DSSPrime.LoadShapeClass.Find(Value);
+ elem.PropertySideEffects(ord(TLoadProp.daily));
end;
//------------------------------------------------------------------------------
procedure Loads_Set_duty(const Value: PAnsiChar); CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.DutyShape := Value;
- LoadPropSideEffects(DSSPrime, LoadProps.duty, elem);
+ elem.DutyShapeObj := DSSPrime.LoadShapeClass.Find(Value);
+ elem.PropertySideEffects(ord(TLoadProp.duty));
end;
//------------------------------------------------------------------------------
procedure Loads_Set_Growth(const Value: PAnsiChar); CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.GrowthShape := Value;
- LoadPropSideEffects(DSSPrime, LoadProps.growth, elem);
+ elem.GrowthShapeObj := DSSPrime.GrowthShapeClass.Find(Value);
+ elem.PropertySideEffects(ord(TLoadProp.growth));
end;
//------------------------------------------------------------------------------
procedure Loads_Set_IsDelta(Value: TAPIBoolean); CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -789,27 +723,27 @@ procedure Loads_Set_IsDelta(Value: TAPIBoolean); CDECL;
//------------------------------------------------------------------------------
procedure Loads_Set_kva(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'kva', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.kva), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_kwh(Value: Double); CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.Set_kWh(Value);
- //LoadPropSideEffects(DSSPrime, LoadProps.kwh, elem);
+ elem.FkWh := Value;
+ elem.PropertySideEffects(ord(TLoadProp.kwh));
end;
//------------------------------------------------------------------------------
procedure Loads_Set_kwhdays(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'kwhdays', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.kwhdays), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_Model(Value: Integer); CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -817,89 +751,93 @@ procedure Loads_Set_Model(Value: Integer); CDECL;
if (Value >= Ord(Low(TLoadModel))) and (Value <= Ord(High(TLoadModel))) then
elem.FLoadModel := TLoadModel(Value)
else
- DoSimpleMsg(DSSPrime, Format('Invalid load model (%d).', [Value]), 5004);
+ DoSimpleMsg(DSSPrime, 'Invalid load model (%d).', [Value], 5004);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_NumCust(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'NumCust', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.NumCust), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_PctMean(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, '%mean', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.pctmean), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_PctStdDev(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, '%stddev', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.pctstddev), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_Rneut(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Rneut', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.Rneut), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_Spectrum(const Value: PAnsiChar); CDECL;
begin
- Set_Parameter(DSSPrime, 'Spectrum', Value);
+ Set_Parameter(DSSPrime, ord(High(TLoadProp)) + ord(TPCElementProp.Spectrum), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_Status(Value: Integer); CDECL;
+var
+ elem: TObj;
begin
+ if not _activeObj(DSSPrime, elem) then
+ Exit;
case Value of
dssLoadVariable:
- Set_Parameter(DSSPrime, 'status', 'v');
+ elem.status := TLoadStatus.Variable;
dssLoadFixed:
- Set_Parameter(DSSPrime, 'status', 'f');
+ elem.status := TLoadStatus.Fixed;
dssLoadExempt:
- Set_Parameter(DSSPrime, 'status', 'e');
+ elem.status := TLoadStatus.Exempt;
end;
end;
//------------------------------------------------------------------------------
procedure Loads_Set_Vmaxpu(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'VmaxPu', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.VmaxPu), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_Vminemerg(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'VminEmerg', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.VminEmerg), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_Vminnorm(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'VminNorm', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.VminNorm), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_Vminpu(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'VminPu', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.VminPu), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_xfkVA(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'XfKVA', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.XfKVA), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_Xneut(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Xneut', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TLoadProp.Xneut), Value);
end;
//------------------------------------------------------------------------------
procedure Loads_Set_Yearly(const Value: PAnsiChar); CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.YearlyShape := Value;
- LoadPropSideEffects(DSSPrime, LoadProps.yearly, elem);
+ elem.YearlyShapeObj := DSSPrime.LoadShapeClass.Find(Value);
+ elem.PropertySideEffects(ord(TLoadProp.yearly));
end;
//------------------------------------------------------------------------------
procedure Loads_Get_ZIPV(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
begin
@@ -918,11 +856,11 @@ procedure Loads_Get_ZIPV_GR(); CDECL;
//------------------------------------------------------------------------------
procedure Loads_Set_ZIPV(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
if ValueCount <> 7 then
begin
- DoSimpleMsg(DSSPrime, Format('ZIPV requires 7 elements, %d were provided!', [ValueCount]), 5890);
+ DoSimpleMsg(DSSPrime, 'ZIPV requires 7 elements, %d were provided!', [ValueCount], 5890);
Exit;
end;
@@ -935,7 +873,7 @@ procedure Loads_Set_ZIPV(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
//------------------------------------------------------------------------------
function Loads_Get_pctSeriesRL(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := -1.0; // signify bad request
if not _activeObj(DSSPrime, elem) then
@@ -945,7 +883,7 @@ function Loads_Get_pctSeriesRL(): Double; CDECL;
//------------------------------------------------------------------------------
procedure Loads_Set_pctSeriesRL(Value: Double); CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -954,7 +892,7 @@ procedure Loads_Set_pctSeriesRL(Value: Double); CDECL;
//------------------------------------------------------------------------------
function Loads_Get_RelWeight(): Double; CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -964,7 +902,7 @@ function Loads_Get_RelWeight(): Double; CDECL;
//------------------------------------------------------------------------------
procedure Loads_Set_RelWeight(Value: Double); CDECL;
var
- elem: TLoadObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -973,7 +911,7 @@ procedure Loads_Set_RelWeight(Value: Double); CDECL;
//------------------------------------------------------------------------------
function Loads_Get_Phases(): Integer; CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, pLoad) then
@@ -983,21 +921,28 @@ function Loads_Get_Phases(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Loads_Set_Phases(Value: Integer); CDECL;
var
- pLoad: TLoadObj;
+ elem: TObj;
+ prevVal: Integer;
begin
- if not _activeObj(DSSPrime, pLoad) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- if (Value <> pLoad.NPhases) then
+ if Value < 1 then
+ begin
+ DoSimpleMsg(DSSPrime, '%s: Number of phases must be a positive integer!', [elem.FullName], 6568);
+ Exit;
+ end;
+ if (Value <> elem.NPhases) then
begin
- pLoad.NPhases := Value;
- LoadPropSideEffects(DSSPrime, LoadProps.phases, pLoad);
+ prevVal := elem.FNPhases;
+ elem.FNPhases := Value;
+ elem.PropertySideEffects(ord(TLoadProp.phases), prevVal);
end;
end;
//------------------------------------------------------------------------------
function Loads_Get_Bus1(): PAnsiChar; CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, pLoad) then
@@ -1007,24 +952,24 @@ function Loads_Get_Bus1(): PAnsiChar; CDECL;
//------------------------------------------------------------------------------
procedure Loads_Set_Bus1(const Value: PAnsiChar); CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
if not _activeObj(DSSPrime, pLoad) then
Exit;
pLoad.SetBus(1, Value);
- // LoadPropSideEffects(DSSPrime, LoadProps.bus1, pLoad); -- Nothing
+ pLoad.PropertySideEffects(ord(TLoadProp.bus1));
end;
//------------------------------------------------------------------------------
function Loads_Get_Sensor(): PAnsiChar; CDECL;
var
- pLoad: TLoadObj;
+ pLoad: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, pLoad) then
Exit;
- if pLoad.SensorObj <> NIL then
- Result := DSS_GetAsPAnsiChar(DSSPrime, pLoad.SensorObj.ElementName);
+ if (pLoad.SensorObj <> NIL) and (pLoad.SensorObj.MeteredElement <> NIL) then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(pLoad.SensorObj.MeteredElement.FullName));
end;
//------------------------------------------------------------------------------
end.
diff --git a/src/CAPI/CAPI_Meters.pas b/src/CAPI/CAPI_Meters.pas
index 1e00ab037..12d5426ea 100644
--- a/src/CAPI/CAPI_Meters.pas
+++ b/src/CAPI/CAPI_Meters.pas
@@ -80,12 +80,13 @@ implementation
EnergyMeter,
DSSGlobals,
SysUtils,
- ucomplex,
+ UComplex, DSSUcomplex,
CktElement,
PDElement,
CktTree,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ DSSObjectHelper;
//------------------------------------------------------------------------------
function _activeObj(DSS: TDSSContext; out obj: TEnergyMeterObj): Boolean; inline;
@@ -100,7 +101,7 @@ function _activeObj(DSS: TDSSContext; out obj: TEnergyMeterObj): Boolean; inline
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active EnergyMeter object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['EnergyMeter'], 8989);
end;
Exit;
end;
@@ -110,7 +111,7 @@ function _activeObj(DSS: TDSSContext; out obj: TEnergyMeterObj): Boolean; inline
//------------------------------------------------------------------------------
procedure InvalidActiveSection(DSS: TDSSContext); inline;
begin
- DoSimpleMsg(DSS, 'Invalid active section. Has SetActiveSection been called?', 5055);
+ DoSimpleMsg(DSS, _('Invalid active section. Has SetActiveSection been called?'), 5055);
end;
//------------------------------------------------------------------------------
procedure Meters_Get_AllNames(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
@@ -250,7 +251,7 @@ procedure Meters_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'EnergyMeter "' + Value + '" Not Found in Active Circuit.', 5005);
+ DoSimpleMsg(DSSPrime, 'EnergyMeter "%s" not found in Active Circuit.', [Value], 5005);
end;
end;
//------------------------------------------------------------------------------
@@ -307,7 +308,7 @@ procedure Meters_Set_Peakcurrent(ValuePtr: PDouble; ValueCount: TAPISize); CDECL
if ValueCount <> pMeterObj.NPhases then
begin
- DoSimpleMsg(DSSPrime, 'The provided number of values does not match the element''s number of phases.', 5026);
+ DoSimpleMsg(DSSPrime, _('The provided number of values does not match the element''s number of phases.'), 5026);
Exit;
end;
Move(ValuePtr^, pMeterObj.SensorCurrent[1], ValueCount * SizeOf(Double));
@@ -348,7 +349,7 @@ procedure Meters_Set_CalcCurrent(ValuePtr: PDouble; ValueCount: TAPISize); CDECL
if ValueCount <> pMeterObj.NPhases then
begin
- DoSimpleMsg(DSSPrime, 'The provided number of values does not match the element''s number of phases.', 5025);
+ DoSimpleMsg(DSSPrime, _('The provided number of values does not match the element''s number of phases.'), 5025);
Exit;
end;
@@ -390,7 +391,7 @@ procedure Meters_Set_AllocFactors(ValuePtr: PDouble; ValueCount: TAPISize); CDEC
Value := PDoubleArray0(ValuePtr);
if ValueCount <> pMeterObj.NPhases then
begin
- DoSimpleMsg(DSSPrime, 'The provided number of values does not match the element''s number of phases.', 5026);
+ DoSimpleMsg(DSSPrime, _('The provided number of values does not match the element''s number of phases.'), 5026);
Exit;
end;
@@ -408,7 +409,8 @@ function Meters_Get_MeteredElement(): PAnsiChar; CDECL;
if not _activeObj(DSSPrime, pMeterObj) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, pMeterObj.ElementName);
+ if pMeterObj.MeteredElement <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(pMeterObj.MeteredElement.FullName));
end;
//------------------------------------------------------------------------------
function Meters_Get_MeteredTerminal(): Integer; CDECL;
@@ -429,7 +431,7 @@ procedure Meters_Set_MeteredElement(const Value: PAnsiChar); CDECL;
if not _activeObj(DSSPrime, pMeterObj) then
Exit;
- pMeterObj.elementName := Value;
+ pMeterObj.ParsePropertyValue(ord(TEnergyMeterProp.element), Value);
pMeterObj.MeteredElementChanged := TRUE;
pMeterObj.RecalcElementData;
end;
@@ -506,7 +508,7 @@ procedure Meters_Get_AllEndElements(var ResultPtr: PPAnsiChar; ResultCount: PAPI
begin
pMeterObj.BranchList.ZoneEndsList.Get(k + 1, node);
elem := node.CktObject;
- Result[k] := DSS_CopyStringAsPChar(Format('%s.%s', [elem.ParentClass.Name, elem.Name]));
+ Result[k] := DSS_CopyStringAsPChar(elem.FullName);
end;
end;
@@ -567,7 +569,7 @@ procedure Meters_Get_AllBranchesInZone(var ResultPtr: PPAnsiChar; ResultCount: P
k := 0;
while pElem <> NIL do
begin
- Result[k] := DSS_CopyStringAsPChar(Format('%s.%s', [pElem.ParentClass.Name, pElem.Name]));
+ Result[k] := DSS_CopyStringAsPChar(pElem.FullName);
inc(k);
pElem := pMeterObj.BranchList.GoForward;
end;
@@ -636,7 +638,7 @@ procedure Meters_Set_SequenceIndex(Value: Integer); CDECL;
if (Value > 0) and (Value <= SequenceList.Count) then
DSSPrime.ActiveCircuit.ActiveCktElement := SequenceList.Get(Value)
else
- DoSimpleMsg(Format('Invalid index for SequenceList: %d. List size is %d.', [Value, SequenceList.Count]), 500501);
+ DoSimpleMsg('Invalid index for SequenceList: %d. List size is %d.', [Value, SequenceList.Count], 500501);
end;
end;
//------------------------------------------------------------------------------
@@ -899,7 +901,7 @@ procedure Meters_Set_idx(Value: Integer); CDECL;
pEnergyMeter := DSSPrime.ActiveCircuit.EnergyMeters.Get(Value);
if pEnergyMeter = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Meter index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['Meter', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pEnergyMeter;
diff --git a/src/CAPI/CAPI_Monitors.pas b/src/CAPI/CAPI_Monitors.pas
index 2d86ac062..84a26c777 100644
--- a/src/CAPI/CAPI_Monitors.pas
+++ b/src/CAPI/CAPI_Monitors.pas
@@ -1,7 +1,5 @@
unit CAPI_Monitors;
-//TODO: remove AuxParser usage
-
interface
uses
@@ -81,7 +79,7 @@ function _activeObj(DSSPrime: TDSSContext; out obj: TMonitorObj): Boolean; inlin
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSSPrime, 'No active Monitor object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['Monitor'], 8989);
end;
Exit;
end;
@@ -215,7 +213,7 @@ procedure Monitors_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'Monitor "' + Value + '" Not Found in Active Circuit.', 5004);
+ DoSimpleMsg(DSSPrime, 'Monitor "%s" not found in Active Circuit.', [Value], 5004);
end;
end;
//------------------------------------------------------------------------------
@@ -311,10 +309,10 @@ procedure Monitors_Get_Channel(var ResultPtr: PDouble; ResultCount: PAPISize; In
if (Index < 1) or (Index > pMon.RecordSize {NumChannels}) then
begin
- DoSimpleMsg(DSSPrime, Format(
+ DoSimpleMsg(DSSPrime,
'Monitors.Channel: invalid channel index (%d), monitor "%s" has %d channels.',
- [Index, pMon.Name, pMon.RecordSize]
- ), 5888);
+ [Index, pMon.Name, pMon.RecordSize],
+ 5888);
Exit;
end;
Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, pMon.SampleCount);
@@ -518,7 +516,9 @@ function Monitors_Get_Element(): PAnsiChar; CDECL;
Result := NIL;
if not _activeObj(DSSPrime, pMon) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, pMon.ElementName);
+
+ if pMon.MeteredElement <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(pMon.MeteredElement.FullName));
end;
//------------------------------------------------------------------------------
procedure Monitors_Set_Element(const Value: PAnsiChar); CDECL;
@@ -527,8 +527,8 @@ procedure Monitors_Set_Element(const Value: PAnsiChar); CDECL;
begin
if not _activeObj(DSSPrime, pMon) then
Exit;
- pMon.ElementName := Value;
- pMon.PropertyValue[1] := Value;
+ pMon.ParsePropertyValue(ord(TMonitorProp.element), Value);
+ pMon.SetAsNextSeq(ord(TMonitorProp.Element));
pMon.RecalcElementData;
end;
//------------------------------------------------------------------------------
@@ -570,7 +570,7 @@ procedure Monitors_Set_idx(Value: Integer); CDECL;
pMonitor := DSSPrime.ActiveCircuit.Monitors.Get(Value);
if pMonitor = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Monitor index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['Monitor', Value], 656565);
Exit;
end;
diff --git a/src/CAPI/CAPI_NoParallel.pas b/src/CAPI/CAPI_NoParallel.pas
new file mode 100644
index 000000000..7914bbdbe
--- /dev/null
+++ b/src/CAPI/CAPI_NoParallel.pas
@@ -0,0 +1,135 @@
+unit CAPI_NoParallel;
+
+interface
+
+uses
+ CAPI_Utils,
+ CAPI_Types;
+
+{$IFNDEF DSS_CAPI_PM}
+function Parallel_Get_NumCPUs(): Integer; CDECL;
+function Parallel_Get_NumCores(): Integer; CDECL;
+function Parallel_Get_ActiveActor(): Integer; CDECL;
+procedure Parallel_Set_ActiveActor(Value: Integer); CDECL;
+procedure Parallel_CreateActor(); CDECL;
+function Parallel_Get_ActorCPU(): Integer; CDECL;
+procedure Parallel_Set_ActorCPU(Value: Integer); CDECL;
+function Parallel_Get_NumOfActors(): Integer; CDECL;
+procedure Parallel_Wait(); CDECL;
+procedure Parallel_Get_ActorProgress(var ResultPtr: PInteger; ResultCount: PAPISize); CDECL;
+procedure Parallel_Get_ActorProgress_GR(); CDECL;
+procedure Parallel_Get_ActorStatus(var ResultPtr: PInteger; ResultCount: PAPISize); CDECL;
+procedure Parallel_Get_ActorStatus_GR(); CDECL;
+function Parallel_Get_ActiveParallel(): Integer; CDECL;
+procedure Parallel_Set_ActiveParallel(Value: Integer); CDECL;
+function Parallel_Get_ConcatenateReports(): Integer; CDECL;
+procedure Parallel_Set_ConcatenateReports(Value: Integer); CDECL;
+{$ENDIF}
+
+implementation
+
+uses
+ CAPI_Constants,
+ DSSGlobals,
+ Executive,
+ SysUtils,
+ solution,
+ CktElement,
+ KLUSolve,
+ Classes,
+ DSSClass,
+ DSSHelper;
+
+{$IFNDEF DSS_CAPI_PM}
+function NotAvailable(DSS: TDSSContext): Integer;
+begin
+ DoSimpleMsg(DSS, _('Parallel machine functions were not compiled'), 7982);
+ Result := -1;
+end;
+
+function Parallel_Get_NumCPUs(): Integer; CDECL;
+begin
+ Result := NotAvailable(DSSPrime);
+end;
+
+function Parallel_Get_NumCores(): Integer; CDECL;
+begin
+ Result := NotAvailable(DSSPrime);
+end;
+
+function Parallel_Get_ActiveActor(): Integer; CDECL;
+begin
+ Result := NotAvailable(DSSPrime);
+end;
+
+procedure Parallel_Set_ActiveActor(Value: Integer); CDECL;
+begin
+ NotAvailable(DSSPrime);
+end;
+
+procedure Parallel_CreateActor(); CDECL;
+begin
+ NotAvailable(DSSPrime);
+end;
+
+function Parallel_Get_ActorCPU(): Integer; CDECL;
+begin
+ Result := NotAvailable(DSSPrime);
+end;
+
+procedure Parallel_Set_ActorCPU(Value: Integer); CDECL;
+begin
+ NotAvailable(DSSPrime);
+end;
+
+function Parallel_Get_NumOfActors(): Integer; CDECL;
+begin
+ Result := NotAvailable(DSSPrime);
+end;
+
+procedure Parallel_Wait(); CDECL;
+begin
+ NotAvailable(DSSPrime);
+end;
+
+procedure Parallel_Get_ActorProgress(var ResultPtr: PInteger; ResultCount: PAPISize); CDECL;
+begin
+ NotAvailable(DSSPrime);
+end;
+
+procedure Parallel_Get_ActorProgress_GR(); CDECL;
+begin
+ NotAvailable(DSSPrime);
+end;
+
+procedure Parallel_Get_ActorStatus(var ResultPtr: PInteger; ResultCount: PAPISize); CDECL;
+begin
+ NotAvailable(DSSPrime);
+end;
+
+procedure Parallel_Get_ActorStatus_GR(); CDECL;
+begin
+ NotAvailable(DSSPrime);
+end;
+
+function Parallel_Get_ActiveParallel(): Integer; CDECL;
+begin
+ Result := NotAvailable(DSSPrime);
+end;
+
+procedure Parallel_Set_ActiveParallel(Value: Integer); CDECL;
+begin
+ NotAvailable(DSSPrime);
+end;
+
+function Parallel_Get_ConcatenateReports(): Integer; CDECL;
+begin
+ Result := NotAvailable(DSSPrime);
+end;
+
+procedure Parallel_Set_ConcatenateReports(Value: Integer); CDECL;
+begin
+ NotAvailable(DSSPrime);
+end;
+{$ENDIF}
+end.
diff --git a/src/CAPI/CAPI_Obj.pas b/src/CAPI/CAPI_Obj.pas
new file mode 100644
index 000000000..1e2531e3a
--- /dev/null
+++ b/src/CAPI/CAPI_Obj.pas
@@ -0,0 +1,1758 @@
+unit CAPI_Obj;
+
+// Copyright (c) 2020-2022, DSS C-API contributors
+// Copyright (c) 2020-2022, Paulo Meira
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// * Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+interface
+
+uses
+ CAPI_Utils,
+ CAPI_Types,
+ DSSObject;
+
+//TODO: decise if we want to expose the metadata (property index, name and type) now or later
+
+// The classic API keeps the string buffer in the global state,
+// but since this new API wants to avoid that, users must dispose
+// the string copies themselves.
+//TODO: consider using the same API as numeric arrays for string
+procedure DSS_Dispose_String(S: PAnsiChar); CDECL;
+
+function DSS_ExtractSchema(DSS: TDSSContext): PAnsiChar; CDECL;
+
+function Obj_New(DSS: TDSSContext; ClsIdx: Integer; Name: PAnsiChar; Activate: TAPIBoolean; BeginEdit: TAPIBoolean): Pointer; CDECL;
+function Obj_GetHandleByName(DSS: TDSSContext; ClsIdx: Integer; Name: PAnsiChar): Pointer; CDECL;
+function Obj_GetHandleByIdx(DSS: TDSSContext; ClsIdx: Integer; Idx: Integer): Pointer; CDECL;
+
+function Obj_GetName(Handle: Pointer): PAnsiChar; CDECL;
+function Obj_GetIdx(Handle: Pointer): Integer; CDECL;
+function Obj_GetClassName(Handle: Pointer): PAnsiChar; CDECL;
+function Obj_GetClassIdx(Handle: Pointer): Integer; CDECL;
+function Obj_PropertySideEffects(Handle: Pointer; Index: Integer; PreviousInt: Integer): TAPIBoolean; CDECL;
+procedure Obj_BeginEdit(Handle: Pointer); CDECL;
+procedure Obj_EndEdit(Handle: Pointer; NumChanges: Integer); CDECL;
+procedure Obj_Activate(Handle: Pointer; AllLists: TAPIBoolean); CDECL;
+function Obj_GetPropSeqPtr(Handle: Pointer; CurrentCount: PInteger): PInteger; CDECL;
+
+function Obj_GetFloat64(obj: TDSSObject; Index: Integer): Double; CDECL;
+function Obj_GetInt32(obj: TDSSObject; Index: Integer): Integer; CDECL;
+function Obj_GetString(obj: TDSSObject; Index: Integer): PAnsiChar; CDECL;
+function Obj_GetObject(obj: TDSSObject; Index: Integer): Pointer; CDECL;
+function Obj_GetAsString(obj: TDSSObject; Index: Integer): PAnsiChar; CDECL;
+procedure Obj_GetFloat64Array(var ResultPtr: PDouble; ResultCount: PAPISize; obj: TDSSObject; Index: Integer); CDECL;
+procedure Obj_GetInt32Array(var ResultPtr: PInteger; ResultCount: PAPISize; obj: TDSSObject; Index: Integer); CDECL;
+procedure Obj_GetStringArray(var ResultPtr: PPAnsiChar; ResultCount: PAPISize; obj: TDSSObject; Index: Integer); CDECL;
+procedure Obj_GetObjectArray(var ResultPtr: PPointer; ResultCount: PAPISize; obj: TDSSObject; Index: Integer); CDECL;
+
+procedure Obj_SetAsString(obj: TDSSObject; Index: Integer; Value: PAnsiChar); CDECL;
+procedure Obj_SetFloat64(obj: TDSSObject; Index: Integer; Value: Double); CDECL;
+procedure Obj_SetInt32(obj: TDSSObject; Index: Integer; Value: Integer); CDECL;
+procedure Obj_SetString(obj: TDSSObject; Index: Integer; Value: PAnsiChar); CDECL;
+procedure Obj_SetObject(obj: TDSSObject; Index: Integer; Value: TDSSObject); CDECL;
+
+procedure Obj_SetFloat64Array(obj: TDSSObject; Index: Integer; Value: PDouble; ValueCount: Integer); CDECL;
+procedure Obj_SetInt32Array(obj: TDSSObject; Index: Integer; Value: PInteger; ValueCount: Integer); CDECL;
+procedure Obj_SetStringArray(obj: TDSSObject; Index: Integer; Value: PPAnsiChar; ValueCount: Integer); CDECL;
+procedure Obj_SetObjectArray(obj: TDSSObject; Index: Integer; Value: TDSSObjectPtr; ValueCount: Integer); CDECL;
+
+
+// Batch: creation and state setup
+procedure Batch_CreateFromNew(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; ClsIdx: Integer; Names: PPAnsiChar; Count: Integer; BeginEdit: TAPIBoolean); CDECL;
+procedure Batch_Dispose(batch: Pointer); CDECL;
+procedure Batch_BeginEdit(batch: TDSSObjectPtr; batchSize: Integer); CDECL;
+procedure Batch_EndEdit(batch: TDSSObjectPtr; batchSize: Integer; NumEdits: Integer); CDECL;
+procedure Batch_GetPropSeq(var ResultPtr: PInteger; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer); CDECL;
+
+// Batch -- using class and property indices
+procedure Batch_CreateByClass(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsIdx: Integer); CDECL;
+procedure Batch_CreateByRegExp(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsIdx: Integer; re: PAnsiChar); CDECL;
+procedure Batch_CreateByIndex(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; ClsIdx: Integer; Value: PInteger; ValueCount: Integer); CDECL;
+procedure Batch_CreateByInt32Property(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; ClsIdx: Integer; propidx: Integer; value: Integer); CDECL;
+
+procedure Batch_GetFloat64(var ResultPtr: PDouble; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Index: Integer); CDECL;
+procedure Batch_GetInt32(var ResultPtr: PInteger; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Index: Integer); CDECL;
+procedure Batch_GetString(var ResultPtr: PPAnsiChar; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Index: Integer); CDECL;
+procedure Batch_GetAsString(var ResultPtr: PPAnsiChar; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Index: Integer); CDECL;
+procedure Batch_GetObject(var ResultPtr: PPointer; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Index: Integer); CDECL;
+
+// procedure Batch_SetAsString(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: PAnsiChar); CDECL;
+procedure Batch_Float64(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Operation: Integer; Value: Double); CDECL;
+procedure Batch_Int32(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Operation: Integer; Value: Integer); CDECL;
+procedure Batch_SetString(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: PAnsiChar); CDECL;
+procedure Batch_SetObject(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: TDSSObject); CDECL;
+
+procedure Batch_SetFloat64Array(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: PDouble); CDECL;
+procedure Batch_SetInt32Array(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: PInteger); CDECL;
+procedure Batch_SetStringArray(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: PPAnsiChar); CDECL;
+procedure Batch_SetObjectArray(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: TDSSObjectPtr); CDECL;
+
+// Batch -- using class and property names
+procedure Batch_CreateFromNewS(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsName: String; Names: PPAnsiChar; Count: Integer; BeginEdit: TAPIBoolean); CDECL;
+procedure Batch_CreateByClassS(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsName: PAnsiChar); CDECL;
+procedure Batch_CreateByRegExpS(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsname: PAnsiChar; re: PAnsiChar); CDECL;
+procedure Batch_CreateByIndexS(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsname: PAnsiChar; Value: PInteger; ValueCount: Integer); CDECL;
+procedure Batch_CreateByInt32PropertyS(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsname: PAnsiChar; propname: PAnsiChar; value: Integer); CDECL;
+
+procedure Batch_GetFloat64S(var ResultPtr: PDouble; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Name: PChar); CDECL;
+procedure Batch_GetInt32S(var ResultPtr: PInteger; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Name: PChar); CDECL;
+procedure Batch_GetStringS(var ResultPtr: PPAnsiChar; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Name: PChar); CDECL;
+procedure Batch_GetAsStringS(var ResultPtr: PPAnsiChar; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Name: PChar); CDECL;
+procedure Batch_GetObjectS(var ResultPtr: PPointer; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Name: PChar); CDECL;
+
+// procedure Batch_SetAsStringS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: PAnsiChar); CDECL;
+procedure Batch_Float64S(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Operation: Integer; Value: Double); CDECL;
+procedure Batch_Int32S(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Operation: Integer; Value: Integer); CDECL;
+procedure Batch_SetStringS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: PAnsiChar); CDECL;
+procedure Batch_SetObjectS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: TDSSObject); CDECL;
+
+procedure Batch_SetFloat64ArrayS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: PDouble); CDECL;
+procedure Batch_SetInt32ArrayS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: PInteger); CDECL;
+procedure Batch_SetStringArrayS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: PPAnsiChar); CDECL;
+procedure Batch_SetObjectArrayS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: TDSSObjectPtr); CDECL;
+
+
+
+implementation
+
+uses
+ fpjson,
+ HashList,
+ CAPI_metadata,
+ StrUtils,
+ Utilities,
+ RegExpr,
+ DSSGlobals,
+ SysUtils,
+ CktElement,
+ DSSClass,
+ DSSPointerList,
+ DSSClassDefs,
+ DSSHelper,
+ DSSObjectHelper,
+ TypInfo;
+
+const
+ // TODO: enum?
+ Batch_Set = 0;
+ Batch_Multiply = 1;
+ Batch_Increment = 2;
+
+procedure DSS_Dispose_String(S: PAnsiChar); CDECL;
+begin
+ FreeMem(S);
+end;
+
+function flagsToArray(flags: TPropertyFlags): TJSONArray;
+begin
+ Result := TJSONArray.Create();
+ if TPropertyFlag.CustomSet in flags then Result.Add('CustomSet');
+ if TPropertyFlag.CustomSetRaw in flags then Result.Add('CustomSetRaw');
+ if TPropertyFlag.CustomGet in flags then Result.Add('CustomGet');
+ if TPropertyFlag.IsFilename in flags then Result.Add('IsFilename');
+ if TPropertyFlag.IgnoreInvalid in flags then Result.Add('IgnoreInvalid');
+ if TPropertyFlag.NonPositive in flags then Result.Add('NonPositive');
+ if TPropertyFlag.NonNegative in flags then Result.Add('NonNegative');
+ if TPropertyFlag.NonZero in flags then Result.Add('NonZero');
+ if TPropertyFlag.Transform_Abs in flags then Result.Add('Transform_Abs');
+ if TPropertyFlag.Transform_LowerCase in flags then Result.Add('Transform_LowerCase');
+ if TPropertyFlag.ScaledByFunction in flags then Result.Add('ScaledByFunction');
+ if TPropertyFlag.WriteByFunction in flags then Result.Add('WriteByFunction');
+ if TPropertyFlag.ReadByFunction in flags then Result.Add('ReadByFunction');
+ if TPropertyFlag.RealPart in flags then Result.Add('RealPart');
+ if TPropertyFlag.ImagPart in flags then Result.Add('ImagPart');
+ if TPropertyFlag.GreaterThanOne in flags then Result.Add('GreaterThanOne');
+ if TPropertyFlag.IntegerStructIndex in flags then Result.Add('IntegerStructIndex');
+ if TPropertyFlag.OnArray in flags then Result.Add('OnArray');
+ if TPropertyFlag.IntervalUnits in flags then Result.Add('IntervalUnits');
+ if TPropertyFlag.AltIndex in flags then Result.Add('AltIndex');
+ if TPropertyFlag.SizeIsFunction in flags then Result.Add('SizeIsFunction');
+ if TPropertyFlag.SilentReadOnly in flags then Result.Add('SilentReadOnly');
+ if TPropertyFlag.ConditionalReadOnly in flags then Result.Add('ConditionalReadOnly');
+ if TPropertyFlag.IntegerToDouble in flags then Result.Add('IntegerToDouble');
+ if TPropertyFlag.CheckForVar in flags then Result.Add('CheckForVar');
+ if TPropertyFlag.AllowNone in flags then Result.Add('AllowNone');
+ if TPropertyFlag.ArrayMaxSize in flags then Result.Add('ArrayMaxSize');
+ if TPropertyFlag.ValueOffset in flags then Result.Add('ValueOffset');
+ if TPropertyFlag.Redundant in flags then Result.Add('Redundant');
+ if TPropertyFlag.Unused in flags then Result.Add('Unused');
+ if TPropertyFlag.ConditionalValue in flags then Result.Add('ConditionalValue');
+ if TPropertyFlag.FullNameAsArray in flags then Result.Add('FullNameAsArray');
+ if TPropertyFlag.Util in flags then Result.Add('Util');
+end;
+
+function prepareEnum(e: TDSSEnum; enumIds: TClassNamesHashListType): TJSONObject;
+var
+ names, values: TJSONArray;
+ i: Integer;
+begin
+ names := TJSONArray.Create();
+ values := TJSONArray.Create();
+ for i := 0 to High(e.Names) do
+ begin
+ names.Add(e.Names[i]);
+ values.Add(e.Ordinals[i]);
+ end;
+ enumIds.Add(e.Name);
+ Result := TJSONObject.Create([
+ 'name', e.Name,
+ 'id', Integer(enumIds.Count),
+ 'names', names,
+ 'values', values,
+ 'sequential', e.Sequential,
+ 'hybrid', e.Hybrid,
+ 'useFirstFound', e.UseFirstFound,
+ 'allowLonger', e.AllowLonger
+ ]);
+end;
+
+function prepareClassSchema(clsidx: Integer; cls: TDSSClass; enumIds: TClassNamesHashListType): TJSONObject;
+var
+ i: Integer;
+ props: TJSONArray;
+ parents, localEnums: TJSONArray;
+ aenum: TDSSENum;
+ stype: String;
+ poffset2: PtrInt;
+ param2: TJSONData;
+ prop: TJSONObject;
+begin
+ props := TJSONArray.Create();
+ localEnums := TJSONArray.Create();
+ parents := TJSONArray.Create();
+
+ with cls do
+ begin
+ for i := 1 to ClassParents.Count do
+ parents.Add(ClassParents.Strings[i - 1]);
+
+ for i := 1 to NumProperties do
+ begin
+ Str(PropertyType[i], stype);
+ stype := Copy(stype, 1, Length(stype) - Length('Property'));
+
+ poffset2 := PropertyOffset2[i];
+ if PropertyType[i] in [
+ TPropertyType.StringEnumActionProperty,
+ TPropertyType.MappedStringEnumProperty,
+ TPropertyType.MappedIntEnumProperty,
+ TPropertyType.MappedStringEnumArrayProperty,
+ TPropertyType.MappedStringEnumOnStructArrayProperty,
+ TPropertyType.MappedStringEnumArrayOnStructArrayProperty
+ ] then
+ begin
+ aenum := TDSSEnum(Pointer(PropertyOffset2[i]));
+ if enumIds.Find(aenum.Name) = 0 then
+ localEnums.Add(prepareEnum(aenum, enumIds));
+
+ param2 := CreateJSON(enumIds.Find(aenum.Name));
+ end
+ else if PropertyType[i] = TPropertyType.DSSObjectReferenceProperty then
+ begin
+ if poffset2 = 0 then
+ param2 := CreateJSON('CktElement')
+ else
+ param2 := CreateJSON(TDSSClass(poffset2).Name);
+ end
+ else
+ param2 := CreateJSON(poffset2);
+
+ prop := TJSONObject.Create([
+ 'name', PropertyName[i],
+ 'index', i,
+ 'sourceClass', PropertySource[i],
+ 'scale', PropertyScale[i],
+ 'valueOffset', PropertyValueOffset[i],
+ 'trapZero', PropertyTrapZero[i],
+ 'inverse', PropertyInverse[i],
+ 'type', stype,
+ 'params', TJSONArray.Create([PropertyOffset[i], param2, PropertyOffset3[i]]),
+ 'flags', flagsToArray(PropertyFlags[i])
+ ]);
+ if TPropertyFlag.Redundant in PropertyFlags[i] then
+ prop.Add('redundantWith', PropertyRedundantWith[i]);
+
+ props.Add(prop);
+ end;
+
+ Result := TJSONObject.Create([
+ 'name', cls.Class_Name,
+ 'index', clsidx,
+ 'parents', parents,
+ 'structArrayIndexOffset1', CreateJSON(PropertyStructArrayIndexOffset),
+ 'structArrayIndexOffset2', CreateJSON(PropertyStructArrayIndexOffset2),
+ 'properties', props,
+ 'classEnums', localEnums
+ ]);
+ end;
+end;
+
+function DSS_ExtractSchema(DSS: TDSSContext): PAnsiChar; CDECL;
+// - Enums are mapped to a sequencial integer id
+// - Object references are translated to (class) names
+var
+ schema: TJSONObject;
+ classes: TJSONArray;
+ enums: TJSONArray;
+ enumIds: TClassNamesHashListType;
+ i: Integer;
+begin
+ Result := NIL;
+ classes := TJSONArray.Create();
+ enums := TJSONArray.Create();
+ enumIds := TClassNamesHashListType.Create(100);
+
+ for i := 1 to DSS.Enums.Count do
+ enums.Add(prepareEnum(TDSSEnum(DSS.Enums[i - 1]), enumIds));
+
+ for i := 1 to DSS.DSSClassList.Count do
+ classes.Add(prepareClassSchema(i, DSS.DSSClassList.At(i), enumIds));
+
+ schema := TJSONObject.Create([
+ 'version', DSS_CAPI_VERSION,
+ 'commit', DSS_CAPI_REV,
+ 'classes', classes,
+ 'globalEnums', enums
+ // TODO: options and commands when redone
+ ]);
+ Result := DSS_CopyStringAsPChar(schema.FormatJSON());
+ schema.Free();
+ enumIds.Free();
+end;
+
+function Obj_New(DSS: TDSSContext; ClsIdx: Integer; Name: PAnsiChar; Activate: TAPIBoolean; BeginEdit: TAPIBoolean): Pointer; CDECL;
+var
+ Obj: TDSSObject;
+ Cls: TDSSClass;
+begin
+ Result := NIL;
+ Cls := DSS.DSSClassList.At(ClsIdx);
+ if Cls = NIL then
+ Exit;
+
+ Obj := Cls.NewObject(Name, Activate);
+ if BeginEdit then
+ Cls.BeginEdit(Obj, False);
+
+ Result := Obj;
+end;
+
+function Obj_GetHandleByName(DSS: TDSSContext; ClsIdx: Integer; Name: PAnsiChar): Pointer; CDECL;
+var
+ Cls: TDSSClass;
+begin
+ Result := NIL;
+ Cls := DSS.DSSClassList.At(ClsIdx);
+ if Cls = NIL then
+ Exit;
+
+ Result := Cls.Find(Name, False);
+end;
+
+function Obj_GetHandleByIdx(DSS: TDSSContext; ClsIdx: Integer; Idx: Integer): Pointer; CDECL;
+var
+ Cls: TDSSClass;
+begin
+ Result := NIL;
+ Cls := DSS.DSSClassList.At(ClsIdx);
+ if Cls = NIL then
+ Exit;
+
+ Result := Cls.ElementList.At(Idx);
+end;
+
+procedure Obj_BeginEdit(Handle: Pointer); CDECL;
+begin
+ TDSSObject(Handle).ParentClass.BeginEdit(Handle, False);
+end;
+
+procedure Obj_EndEdit(Handle: Pointer; NumChanges: Integer); CDECL;
+begin
+ TDSSObject(Handle).ParentClass.EndEdit(Handle, NumChanges);
+end;
+
+procedure activateOnList(Obj: TDSSObject; List: TDSSPointerList);
+var
+ prev: Integer;
+ p: TDSSObject;
+begin
+ if List.Active = Obj then
+ Exit;
+
+ prev := List.ActiveIndex;
+ p := List.First;
+ while p <> NIL do
+ begin
+ if List.Active = Obj then
+ Exit;
+
+ p := List.Next;
+ end;
+
+ // Restore previous position if not found
+ List.Get(prev);
+end;
+
+procedure Obj_Activate(Handle: Pointer; AllLists: TAPIBoolean); CDECL;
+var
+ obj: TDSSObject;
+begin
+ obj := TDSSObject(Handle);
+
+ if obj is TDSSCktElement then
+ obj.DSS.ActiveCircuit.ActiveCktElement := TDSSCktElement(obj)
+ else
+ obj.DSS.ActiveDSSObject := obj;
+
+ obj.ParentClass.ElementList.Get(obj.ClassIndex);
+
+ if not AllLists then
+ Exit;
+
+ // Adapted from TDSSCircuit.AddCktElement
+ with obj.DSS.ActiveCircuit do
+ begin
+ // Update lists of PC and PD elements
+ case (Obj.DSSObjType and BaseClassMask) of
+ PD_ELEMENT:
+ activateOnList(obj, PDElements);
+ PC_ELEMENT:
+ activateOnList(obj, PCElements);
+ CTRL_ELEMENT:
+ activateOnList(obj, DSSControls);
+ METER_ELEMENT:
+ activateOnList(obj, MeterElements);
+ end;
+
+ // Update lists of special elements and generic types
+
+ //TODO: note that most of these lists are kind of redundant
+ // with our current implementation
+
+ case (Obj.DSSObjType and CLASSMASK) of
+ MON_ELEMENT:
+ activateOnList(obj, Monitors);
+ ENERGY_METER:
+ activateOnList(obj, EnergyMeters);
+ SENSOR_ELEMENT:
+ activateOnList(obj, Sensors);
+ GEN_ELEMENT:
+ activateOnList(obj, Generators);
+ SOURCE:
+ activateOnList(obj, Sources);
+ CAP_CONTROL:
+ activateOnList(obj, CapControls);
+ SWT_CONTROL:
+ activateOnList(obj, SwtControls);
+ REG_CONTROL:
+ activateOnList(obj, RegControls);
+ LOAD_ELEMENT:
+ activateOnList(obj, Loads);
+ CAP_ELEMENT:
+ activateOnList(obj, ShuntCapacitors);
+ REACTOR_ELEMENT:
+ activateOnList(obj, Reactors);
+ RELAY_CONTROL:
+ activateOnList(obj, Relays);
+ FUSE_CONTROL:
+ activateOnList(obj, Fuses);
+ RECLOSER_CONTROL:
+ activateOnList(obj, Reclosers);
+
+ // Keep Lines, Transformer, and Lines and Faults in PDElements and separate lists
+ // so we can find them quickly.
+ AUTOTRANS_ELEMENT:
+ activateOnList(obj, AutoTransformers);
+ XFMR_ELEMENT:
+ activateOnList(obj, Transformers);
+ LINE_ELEMENT:
+ activateOnList(obj, Lines);
+ FAULTOBJECT:
+ activateOnList(obj, Faults);
+
+ STORAGE_ELEMENT:
+ activateOnList(obj, StorageElements);
+ PVSYSTEM_ELEMENT:
+ activateOnList(obj, PVSystems);
+ INV_CONTROL:
+ activateOnList(obj, InvControls);
+ EXP_CONTROL:
+ activateOnList(obj, ExpControls);
+ end;
+ end;
+end;
+
+function Obj_GetPropSeqPtr(Handle: Pointer; CurrentCount: PInteger): PInteger; CDECL;
+var
+ obj: TDSSObject;
+begin
+ obj := TDSSObject(Handle);
+ Result := PInteger(obj.PrpSequence);
+ if CurrentCount <> NIL then
+ CurrentCount^ := obj.PropSeqCount;
+end;
+
+function Obj_GetName(Handle: Pointer): PAnsiChar; CDECL;
+begin
+ Result := PChar(TDSSObject(Handle).Name);
+end;
+
+function Obj_GetIdx(Handle: Pointer): Integer; CDECL;
+begin
+ Result := TDSSObject(Handle).ClassIndex;
+end;
+
+function Obj_GetClassName(Handle: Pointer): PAnsiChar; CDECL;
+begin
+ Result := PChar(TDSSObject(Handle).ParentClass.Name);
+end;
+
+function Obj_GetClassIdx(Handle: Pointer): Integer; CDECL;
+begin
+ Result := TDSSObject(Handle).ParentClass.DSSClassType;
+end;
+
+function Obj_PropertySideEffects(Handle: Pointer; Index: Integer; PreviousInt: Integer): TAPIBoolean; CDECL;
+begin
+ Result := True;
+ try
+ TDSSObject(Handle).PropertySideEffects(Index, PreviousInt);
+ except
+ Result := False;
+ end;
+end;
+
+function Obj_GetFloat64(obj: TDSSObject; Index: Integer): Double; CDECL;
+begin
+ Result := obj.GetDouble(Index);
+end;
+
+function Obj_GetInt32(obj: TDSSObject; Index: Integer): Integer; CDECL;
+begin
+ Result := obj.GetInteger(Index);
+end;
+
+function Obj_GetString(obj: TDSSObject; Index: Integer): PAnsiChar; CDECL;
+begin
+ Result := DSS_CopyStringAsPChar(obj.GetString(Index));
+end;
+
+function Obj_GetAsString(obj: TDSSObject; Index: Integer): PAnsiChar; CDECL;
+begin
+ Result := DSS_CopyStringAsPChar(obj.GetPropertyValue(Index));
+end;
+
+function Obj_GetObject(obj: TDSSObject; Index: Integer): Pointer; CDECL;
+begin
+ Result := obj.GetObject(Index);
+end;
+
+procedure Obj_SetFloat64(obj: TDSSObject; Index: Integer; Value: Double); CDECL;
+begin
+ obj.SetDouble(Index, Value);
+end;
+
+procedure Obj_SetInt32(obj: TDSSObject; Index: Integer; Value: Integer); CDECL;
+begin
+ obj.SetInteger(Index, Value);
+end;
+
+procedure Obj_SetString(obj: TDSSObject; Index: Integer; Value: PAnsiChar); CDECL;
+begin
+ obj.SetString(Index, Value);
+end;
+
+procedure Obj_SetAsString(obj: TDSSObject; Index: Integer; Value: PAnsiChar); CDECL;
+begin
+ obj.ParsePropertyValue(Index, Value);
+end;
+
+procedure Obj_SetObject(obj: TDSSObject; Index: Integer; Value: TDSSObject); CDECL;
+begin
+ obj.SetObject(Index, Value);
+end;
+
+procedure Obj_SetFloat64Array(obj: TDSSObject; Index: Integer; Value: PDouble; ValueCount: Integer); CDECL;
+begin
+ obj.SetDoubles(Index, Value, ValueCount);
+end;
+
+procedure Obj_SetInt32Array(obj: TDSSObject; Index: Integer; Value: PInteger; ValueCount: Integer); CDECL;
+begin
+ obj.SetIntegers(Index, Value, ValueCount);
+end;
+
+procedure Obj_SetStringArray(obj: TDSSObject; Index: Integer; Value: PPAnsiChar; ValueCount: Integer); CDECL;
+begin
+ obj.SetStrings(Index, Value, ValueCount);
+end;
+
+procedure Obj_SetObjectArray(obj: TDSSObject; Index: Integer; Value: TDSSObjectPtr; ValueCount: Integer); CDECL;
+begin
+ obj.SetObjects(Index, Value, ValueCount);
+end;
+
+procedure Obj_GetFloat64Array(var ResultPtr: PDouble; ResultCount: PAPISize; obj: TDSSObject; Index: Integer); CDECL;
+begin
+ obj.GetDoubles(Index, ResultPtr, ResultCount);
+end;
+
+procedure Obj_GetInt32Array(var ResultPtr: PInteger; ResultCount: PAPISize; obj: TDSSObject; Index: Integer); CDECL;
+begin
+ obj.GetIntegers(Index, ResultPtr, ResultCount);
+end;
+
+procedure Obj_GetStringArray(var ResultPtr: PPAnsiChar; ResultCount: PAPISize; obj: TDSSObject; Index: Integer); CDECL;
+begin
+ obj.GetStrings(Index, ResultPtr, ResultCount);
+end;
+
+procedure Obj_GetObjectArray(var ResultPtr: PPointer; ResultCount: PAPISize; obj: TDSSObject; Index: Integer); CDECL;
+begin
+ obj.GetObjects(Index, ResultPtr, ResultCount);
+end;
+
+
+//------------------------------------------------------------------------------
+
+procedure Batch_Dispose(batch: Pointer); CDECL;
+begin
+ FreeMem(batch);
+end;
+
+procedure Batch_BeginEdit(batch: TDSSObjectPtr; batchSize: Integer); CDECL;
+var
+ i: Integer;
+ cls: TDSSClass;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ cls := batch^.ParentClass;
+ for i := 1 to batchSize do
+ begin
+ cls.BeginEdit(batch^, False);
+ inc(batch);
+ end;
+end;
+
+procedure Batch_EndEdit(batch: TDSSObjectPtr; batchSize: Integer; NumEdits: Integer); CDECL;
+var
+ i: Integer;
+ cls: TDSSClass;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ cls := batch^.ParentClass;
+ for i := 1 to batchSize do
+ begin
+ cls.EndEdit(batch^, NumEdits);
+ inc(batch);
+ end;
+end;
+
+procedure ensureBatchSize(maxSize: Integer; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize);
+begin
+ if (ResultPtr <> NIL) and (ResultCount[1] >= maxSize) then
+ begin
+ ResultCount[0] := 0;
+ Exit;
+ end;
+
+ if ResultPtr <> NIL then
+ Batch_Dispose(ResultPtr);
+
+ ResultPtr := AllocMem(SizeOf(Pointer) * maxSize);
+ ResultCount[0] := 0;
+ ResultCount[1] := maxSize;
+end;
+
+procedure Batch_GetPropSeq(var ResultPtr: PInteger; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer); CDECL;
+var
+ cls: TDSSClass;
+ propOffset: PtrUint;
+ presult: PInteger;
+ i, N: Integer;
+ flags: TPropertyFlags;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ begin
+ Exit;
+ end;
+ cls := batch^.ParentClass;
+ N := cls.NumProperties;
+ DSS_RecreateArray_PInteger(ResultPtr, ResultCount, batchSize * N);
+ presult := ResultPtr;
+ for i := 1 to batchSize do
+ begin
+ Move(batch^.PrpSequence^, presult^, N * SizeOf(Integer));
+ inc(batch);
+ inc(presult, N);
+ end;
+end;
+
+procedure Batch_CreateFromNew(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; ClsIdx: Integer; Names: PPAnsiChar; Count: Integer; BeginEdit: TAPIBoolean); CDECL;
+var
+ Obj: TDSSObject;
+ Cls: TDSSClass;
+ outptr: TDSSObjectPtr;
+ i: Integer;
+ prefix: String;
+begin
+ Cls := DSS.DSSClassList.At(ClsIdx);
+ if Cls = NIL then
+ Exit;
+ ensureBatchSize(Count, ResultPtr, ResultCount);
+ outptr := ResultPtr;
+
+ if Names = NIL then
+ begin
+ // Use a random batch prefix to avoid collisions
+ prefix := Format('%09d_', [Random(1000000000)]);
+ for i := 1 to Count do
+ begin
+ outptr^ := Cls.NewObject(Format('%s_%d', [prefix, i]), False);
+ inc(outptr);
+ end;
+ end
+ else
+ begin
+ for i := 1 to Count do
+ begin
+ outptr^ := Cls.NewObject(Names^, False);
+ inc(outptr);
+ inc(Names);
+ end;
+ end;
+
+ if not BeginEdit then
+ Exit;
+
+ outptr := ResultPtr;
+ for i := 1 to Count do
+ begin
+ Cls.BeginEdit(outptr^, False);
+ inc(outptr);
+ end;
+end;
+
+procedure Batch_CreateByRegExp(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsIdx: Integer; re: PAnsiChar); CDECL;
+var
+ cls: TDSSClass;
+ rex: TRegExpr = NIL;
+ objlist: TDSSObjectPtr;
+ outptr: TDSSObjectPtr;
+ i: Integer;
+begin
+ cls := DSS.DSSClassList.At(clsIdx);
+ if cls = NIL then
+ Exit;
+ ensureBatchSize(cls.ElementList.Count, ResultPtr, ResultCount);
+ objlist := TDSSObjectPtr(cls.ElementList.InternalPointer);
+ outptr := ResultPtr;
+ try
+ rex := TRegExpr.Create();
+ rex.ModifierI := True;
+ rex.Expression:= re;
+ ResultCount[0] := 0;
+ for i := 1 to cls.ElementList.Count do
+ begin
+ if rex.Exec(objlist^.Name) then
+ begin
+ outptr^:= objlist^;
+ inc(outptr);
+ inc(ResultCount[0]);
+ end;
+ inc(objlist);
+ end;
+ finally
+ FreeAndNil(rex);
+ end;
+end;
+
+
+procedure Batch_CreateByClass(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsIdx: Integer); CDECL;
+var
+ cls: TDSSClass;
+ objlist: TDSSObjectPtr;
+ outptr: TDSSObjectPtr;
+ i: Integer;
+begin
+ cls := DSS.DSSClassList.At(clsIdx);
+ if cls = NIL then
+ Exit;
+ ensureBatchSize(cls.ElementList.Count, ResultPtr, ResultCount);
+ ResultCount[0] := cls.ElementList.Count;
+ objlist := TDSSObjectPtr(cls.ElementList.InternalPointer);
+ outptr := ResultPtr;
+ for i := 1 to cls.ElementList.Count do
+ begin
+ outptr^:= objlist^;
+ inc(outptr);
+ inc(objlist);
+ end;
+end;
+
+procedure Batch_CreateByIndex(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; ClsIdx: Integer; Value: PInteger; ValueCount: Integer); CDECL;
+var
+ cls: TDSSClass;
+ list: TDSSPointerList;
+ outptr: TDSSObjectPtr;
+ i: Integer;
+begin
+ cls := DSS.DSSClassList.At(clsIdx);
+ if cls = NIL then
+ Exit;
+ list := cls.ElementList;
+ ensureBatchSize(list.Count, ResultPtr, ResultCount);
+ outptr := ResultPtr;
+ for i := 1 to ValueCount do
+ begin
+ if (Value^ > 0) and (Value^ <= list.Count) then
+ begin
+ outptr^ := list.At(Value^);
+ inc(outptr);
+ inc(ResultCount[0]);
+ end;
+ inc(Value);
+ end;
+end;
+
+procedure Batch_CreateByInt32Property(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; ClsIdx: Integer; propidx: Integer; value: Integer); CDECL;
+var
+ cls: TDSSClass;
+ objlist: TDSSObjectPtr;
+ outptr: TDSSObjectPtr;
+ propOffset: PtrUint;
+ i: Integer;
+ flags: TPropertyFlags;
+begin
+ cls := DSS.DSSClassList.At(clsIdx);
+ if cls = NIL then
+ begin
+ Exit;
+ end;
+
+ if not (cls.PropertyType[propidx] in [
+ TPropertyType.IntegerProperty,
+ TPropertyType.MappedIntEnumProperty,
+ TPropertyType.MappedStringEnumProperty,
+ TPropertyType.BooleanProperty,
+ TPropertyType.IntegerOnStructArrayProperty
+ ]) then
+ begin
+ Exit;
+ end;
+
+ flags := cls.PropertyFlags[propIdx];
+ propOffset := cls.PropertyOffset[propIdx];
+ objlist := TDSSObjectPtr(cls.ElementList.InternalPointer);
+ ensureBatchSize(cls.ElementList.Count, ResultPtr, ResultCount);
+ outptr := ResultPtr;
+ if (cls.PropertyType[propidx] in [
+ TPropertyType.IntegerProperty,
+ TPropertyType.MappedIntEnumProperty,
+ TPropertyType.MappedStringEnumProperty,
+ TPropertyType.BooleanProperty]) and
+ (not (TPropertyFlag.CustomGet in flags)) and
+ (not (TPropertyFlag.ReadByFunction in flags)) and
+ (not (TPropertyFlag.ScaledByFunction in flags)) then
+ begin
+ // 40-50% faster than calling the function
+ for i := 1 to cls.ElementList.Count do
+ begin
+ if (PInteger(PtrUint(objlist^) + propoffset))^ = value then
+ begin
+ outptr^ := objlist^;
+ inc(outptr);
+ inc(ResultCount[0]);
+ end;
+ inc(objlist);
+ end;
+ Exit;
+ end;
+
+ for i := 1 to cls.ElementList.Count do
+ begin
+ if cls.GetObjInteger(objList^, propIdx) = value then
+ begin
+ outptr^ := objlist^;
+ inc(outptr);
+ inc(ResultCount[0]);
+ end;
+ inc(objlist);
+ end;
+end;
+
+procedure Batch_GetFloat64(var ResultPtr: PDouble; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Index: Integer); CDECL;
+var
+ cls: TDSSClass;
+ // propOffset: PtrUint;
+ presult: PDouble;
+ i: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ Exit;
+
+ cls := batch^.ParentClass;
+
+ // propOffset := cls.PropertyOffset[propIdx];
+ DSS_RecreateArray_PDouble(ResultPtr, ResultCount, batchSize);
+ presult := ResultPtr;
+
+ if not (cls.PropertyType[Index] in [
+ TPropertyType.DoubleProperty,
+ TPropertyType.DoubleOnArrayProperty,
+ TPropertyType.DoubleOnStructArrayProperty
+ ]) then
+ Exit;
+
+ for i := 1 to batchSize do
+ begin
+ //p^ := PDouble(PtrUint(batch^) + propOffset))^; // TODO: benchmark
+ presult^ := cls.GetObjDouble(batch^, Index);
+ inc(batch);
+ inc(presult);
+ end;
+end;
+
+procedure Batch_GetInt32(var ResultPtr: PInteger; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Index: Integer); CDECL;
+var
+ cls: TDSSClass;
+ propOffset: PtrUint;
+ presult: PInteger;
+ i: Integer;
+ flags: TPropertyFlags;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ begin
+ Exit;
+ end;
+
+ cls := batch^.ParentClass;
+ flags := cls.PropertyFlags[Index];
+ propOffset := cls.PropertyOffset[Index];
+
+ DSS_RecreateArray_PInteger(ResultPtr, ResultCount, batchSize);
+ presult := ResultPtr;
+
+ if not (cls.PropertyType[Index] in [
+ TPropertyType.IntegerProperty,
+ TPropertyType.MappedIntEnumProperty,
+ TPropertyType.MappedStringEnumProperty,
+ TPropertyType.BooleanProperty,
+ TPropertyType.EnabledProperty,
+ TPropertyType.IntegerOnStructArrayProperty
+ ]) then
+ begin
+ Exit;
+ end;
+
+ if (cls.PropertyType[Index] in [
+ TPropertyType.IntegerProperty,
+ TPropertyType.MappedIntEnumProperty,
+ TPropertyType.MappedStringEnumProperty,
+ TPropertyType.BooleanProperty]) and
+ (not (TPropertyFlag.CustomGet in flags)) and
+ (not (TPropertyFlag.ReadByFunction in flags)) and
+ (not (TPropertyFlag.ScaledByFunction in flags)) then
+ begin
+ for i := 1 to batchSize do
+ begin
+ presult^ := (PInteger(PtrUint(batch^) + propoffset))^;
+ inc(batch);
+ inc(presult);
+ end;
+ Exit;
+ end;
+
+ for i := 1 to batchSize do
+ begin
+ presult^ := cls.GetObjInteger(batch^, Index);
+ inc(batch);
+ inc(presult);
+ end;
+end;
+
+
+procedure Batch_GetString(var ResultPtr: PPAnsiChar; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Index: Integer); CDECL;
+var
+ cls: TDSSClass;
+ // propOffset: PtrUint;
+ presult: PPAnsiChar;
+ i: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ Exit;
+
+ cls := batch^.ParentClass;
+
+ // propOffset := cls.PropertyOffset[propIdx];
+ DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, batchSize);
+ presult := ResultPtr;
+
+ if not (cls.PropertyType[Index] in [
+ TPropertyType.StringSilentROFunctionProperty,
+ TPropertyType.StringProperty,
+ TPropertyType.BusProperty,
+ TPropertyType.StringOnArrayProperty,
+ TPropertyType.StringOnStructArrayProperty,
+ TPropertyType.BusOnStructArrayProperty,
+ TPropertyType.MappedStringEnumProperty,
+ TPropertyType.DSSObjectReferenceProperty
+ ]) then
+ Exit;
+
+ for i := 1 to batchSize do
+ begin
+ //p^ := DSS_CopyStringAsPChar(PString(PtrUint(batch^) + propOffset))^); // TODO: benchmark
+ presult^ := DSS_CopyStringAsPChar(cls.GetObjString(batch^, Index));
+ inc(batch);
+ inc(presult);
+ end;
+end;
+
+procedure Batch_GetAsString(var ResultPtr: PPAnsiChar; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Index: Integer); CDECL;
+var
+ cls: TDSSClass;
+ // propOffset: PtrUint;
+ presult: PPAnsiChar;
+ i: Integer;
+ s: String;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ Exit;
+
+ cls := batch^.ParentClass;
+ DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, batchSize);
+ presult := ResultPtr;
+ for i := 1 to batchSize do
+ begin
+ cls.GetObjPropertyValue(batch^, Index, s);
+ presult^ := DSS_CopyStringAsPChar(s);
+ inc(batch);
+ inc(presult);
+ end;
+end;
+
+procedure Batch_GetObject(var ResultPtr: PPointer; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Index: Integer); CDECL;
+var
+ cls: TDSSClass;
+ // propOffset: PtrUint;
+ presult: TDSSObjectPtr;
+ i: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ Exit;
+
+ cls := batch^.ParentClass;
+
+ // propOffset := cls.PropertyOffset[propIdx];
+ DSS_CreateArray_PPointer(ResultPtr, ResultCount, batchSize);
+ presult := TDSSObjectPtr(ResultPtr);
+
+ if not (cls.PropertyType[Index] in [
+ TPropertyType.DSSObjectReferenceProperty,
+ TPropertyType.DSSObjectReferenceArrayProperty
+ ]) then
+ Exit;
+
+ for i := 1 to batchSize do
+ begin
+ //p^ := TDSSObjectPtr(PtrUint(batch^) + propOffset))^; // TODO: benchmark
+ presult^ := cls.GetObjObject(batch^, Index);
+ inc(batch);
+ inc(presult);
+ end;
+end;
+
+procedure Batch_Float64(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Operation: Integer; Value: Double); CDECL;
+var
+ cls: TDSSClass;
+ propOffset: PtrUint;
+ i: Integer;
+ prev: Double;
+ doublePtr: PDouble;
+ flags: TPropertyFlags;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ Exit;
+
+ cls := batch^.ParentClass;
+ flags := cls.PropertyFlags[Index];
+ propOffset := cls.PropertyOffset[Index];
+
+ if not (cls.PropertyType[Index] in [
+ TPropertyType.DoubleProperty,
+ TPropertyType.DoubleOnStructArrayProperty,
+ TPropertyType.DoubleOnArrayProperty
+ ]) then
+ Exit;
+
+ if (cls.PropertyType[Index] = TPropertyType.DoubleProperty) and
+ (flags = []) and
+ (not cls.PropertyInverse[Index]) and
+ (cls.PropertyScale[Index] = 1) then
+ begin
+ case Operation of
+ Batch_Multiply:
+ for i := 1 to batchSize do
+ begin
+ doublePtr := (PDouble(PtrUint(batch^) + propOffset));
+ prev := doubleptr^;
+ doublePtr^ := doublePtr^ * Value;
+ batch^.PropertySideEffects(Index, Round(prev));
+ inc(batch);
+ end;
+ Batch_Increment:
+ for i := 1 to batchSize do
+ begin
+ doublePtr := (PDouble(PtrUint(batch^) + propOffset));
+ prev := doubleptr^;
+ doublePtr^ := doublePtr^ + Value;
+ batch^.PropertySideEffects(Index, Round(prev));
+ inc(batch);
+ end;
+ else
+ for i := 1 to batchSize do
+ begin
+ doublePtr := (PDouble(PtrUint(batch^) + propOffset));
+ prev := doubleptr^;
+ doublePtr^ := Value;
+ batch^.PropertySideEffects(Index, Round(prev));
+ inc(batch);
+ end;
+ end;
+ Exit;
+ end;
+
+ case Operation of
+ Batch_Multiply:
+ for i := 1 to batchSize do
+ begin
+ batch^.SetDouble(Index, Value * cls.GetObjDouble(batch^, Index));
+ inc(batch);
+ end;
+ Batch_Increment:
+ for i := 1 to batchSize do
+ begin
+ batch^.SetDouble(Index, Value + cls.GetObjDouble(batch^, Index));
+ inc(batch);
+ end;
+ else
+ for i := 1 to batchSize do
+ begin
+ batch^.SetDouble(Index, Value);
+ inc(batch);
+ end;
+ end;
+end;
+
+procedure Batch_Int32(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Operation: Integer; Value: Integer); CDECL;
+var
+ cls: TDSSClass;
+ propOffset: PtrUint;
+ i: Integer;
+ // prev: Integer;
+ // intptr: PInteger;
+ flags: TPropertyFlags;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ Exit;
+
+ cls := batch^.ParentClass;
+ flags := cls.PropertyFlags[Index];
+ propOffset := cls.PropertyOffset[Index];
+
+ if not (cls.PropertyType[Index] in [
+ TPropertyType.IntegerProperty,
+ TPropertyType.MappedIntEnumProperty,
+ TPropertyType.MappedStringEnumProperty,
+ TPropertyType.BooleanProperty,
+ TPropertyType.EnabledProperty,
+ TPropertyType.IntegerOnStructArrayProperty
+ ]) then
+ Exit;
+
+ // if (cls.PropertyType[Index] in [
+ // TPropertyType.IntegerProperty,
+ // TPropertyType.MappedIntEnumProperty,
+ // TPropertyType.MappedStringEnumProperty,
+ // TPropertyType.BooleanProperty]) and
+ // (not (TPropertyFlag.CustomSet in flags)) and
+ // (not (TPropertyFlag.WriteByFunction in flags)) and
+ // (not (TPropertyFlag.ScaledByFunction in flags)) then
+ // begin
+ // for i := 1 to batchSize do
+ // begin
+ // intptr := (PInteger(PtrUint(batch^) + propoffset));
+ // prev := intptr^;
+ // intptr^ := Value;
+
+ // inc(batch);
+ // end;
+ // Exit;
+ // end;
+
+ case Operation of
+ Batch_Multiply:
+ for i := 1 to batchSize do
+ begin
+ batch^.SetInteger(Index, Value * cls.GetObjInteger(batch^, Index));
+ inc(batch);
+ end;
+ Batch_Increment:
+ for i := 1 to batchSize do
+ begin
+ batch^.SetInteger(Index, Value + cls.GetObjInteger(batch^, Index));
+ inc(batch);
+ end;
+ else
+ for i := 1 to batchSize do
+ begin
+ batch^.SetInteger(Index, Value);
+ inc(batch);
+ end;
+ end;
+end;
+
+procedure Batch_SetString(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: PAnsiChar); CDECL;
+var
+ cls: TDSSClass;
+ propOffset: PtrUint;
+ i: Integer;
+ flags: TPropertyFlags;
+ sValue: String;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ Exit;
+
+ cls := batch^.ParentClass;
+ flags := cls.PropertyFlags[Index];
+ propOffset := cls.PropertyOffset[Index];
+
+ if not (cls.PropertyType[Index] in [
+ // TPropertyType.StringEnumActionProperty, --TODO? Not in SetObjString
+ //TPropertyType.MappedStringEnumOnStructArrayProperty, --TODO? Not in SetObjString
+ TPropertyType.StringProperty,
+ TPropertyType.BusProperty,
+ TPropertyType.MappedStringEnumProperty,
+ TPropertyType.StringOnArrayProperty,
+ TPropertyType.StringOnStructArrayProperty,
+ TPropertyType.BusOnStructArrayProperty
+ ]) then
+ Exit;
+
+ sValue := Value;
+ for i := 1 to batchSize do
+ begin
+ batch^.SetString(Index, sValue);
+ inc(batch);
+ end;
+end;
+
+// procedure Batch_SetAsString(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: PAnsiChar); CDECL;
+// var
+ // cls: TDSSClass;
+ // i: Integer;
+ // flags: TPropertyFlags;
+ // sValue: String;
+// begin
+ // if (batch = NIL) or (batch^ = NIL) then
+ // Exit;
+//
+ // cls := batch^.ParentClass;
+ // flags := cls.PropertyFlags[Index];
+ // propOffset := cls.PropertyOffset[Index];
+ // sValue := Value;
+ // for i := 1 to batchSize do
+ // begin
+ // batch^.ParsePropertyValue(Index, Value);
+ // inc(batch);
+ // end;
+// end;
+
+procedure Batch_SetObject(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: TDSSObject); CDECL;
+var
+ cls: TDSSClass;
+ propOffset: PtrUint;
+ i: Integer;
+ flags: TPropertyFlags;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ Exit;
+
+ cls := batch^.ParentClass;
+ flags := cls.PropertyFlags[Index];
+ propOffset := cls.PropertyOffset[Index];
+
+ if cls.PropertyType[Index] <> TPropertyType.DSSObjectReferenceProperty then
+ Exit;
+
+ for i := 1 to batchSize do
+ begin
+ batch^.SetObject(Index, Value);
+ inc(batch);
+ end;
+end;
+
+procedure Batch_SetFloat64Array(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: PDouble); CDECL;
+var
+ cls: TDSSClass;
+ propOffset: PtrUint;
+ i: Integer;
+ prev: Double;
+ doublePtr: PDouble;
+ flags: TPropertyFlags;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ Exit;
+
+ cls := batch^.ParentClass;
+ flags := cls.PropertyFlags[Index];
+ propOffset := cls.PropertyOffset[Index];
+
+ if not (cls.PropertyType[Index] in [
+ TPropertyType.DoubleProperty,
+ TPropertyType.DoubleOnStructArrayProperty,
+ TPropertyType.DoubleOnArrayProperty
+ ]) then
+ Exit;
+
+ if (cls.PropertyType[Index] = TPropertyType.DoubleProperty) and
+ (flags = []) and
+ (not cls.PropertyInverse[Index]) and
+ (cls.PropertyScale[Index] = 1) then
+ begin
+ for i := 1 to batchSize do
+ begin
+ doublePtr := PDouble(PtrUint(batch^) + propOffset);
+ prev := doubleptr^;
+ doublePtr^ := Value^;
+ batch^.PropertySideEffects(Index, Round(prev));
+ inc(batch);
+ inc(Value);
+ end;
+ Exit;
+ end;
+
+ for i := 1 to batchSize do
+ begin
+ batch^.SetDouble(Index, Value^);
+ inc(batch);
+ inc(Value)
+ end;
+end;
+
+procedure Batch_SetInt32Array(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: PInteger); CDECL;
+var
+ cls: TDSSClass;
+ propOffset: PtrUint;
+ i: Integer;
+ prev: Integer;
+ intPtr: PInteger;
+ flags: TPropertyFlags;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ Exit;
+
+ cls := batch^.ParentClass;
+ flags := cls.PropertyFlags[Index];
+ propOffset := cls.PropertyOffset[Index];
+
+ if not (cls.PropertyType[Index] in [
+ TPropertyType.IntegerProperty,
+ TPropertyType.MappedIntEnumProperty,
+ TPropertyType.MappedStringEnumProperty,
+ TPropertyType.BooleanProperty,
+ TPropertyType.IntegerOnStructArrayProperty
+ ]) then
+ Exit;
+
+ if (cls.PropertyType[Index] <> TPropertyType.IntegerOnStructArrayProperty) and
+ (flags = []) and
+ (not cls.PropertyInverse[Index]) and
+ (cls.PropertyScale[Index] = 1) then
+ begin
+ for i := 1 to batchSize do
+ begin
+ intPtr := PInteger(PtrUint(batch^) + propOffset);
+ prev := intPtr^;
+ intPtr^ := Value^;
+ batch^.PropertySideEffects(Index, prev);
+ inc(batch);
+ inc(Value);
+ end;
+ Exit;
+ end;
+
+ for i := 1 to batchSize do
+ begin
+ batch^.SetInteger(Index, Value^);
+ inc(batch);
+ inc(Value)
+ end;
+end;
+
+procedure Batch_SetStringArray(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: PPAnsiChar); CDECL;
+var
+ cls: TDSSClass;
+ propOffset: PtrUint;
+ i: Integer;
+ flags: TPropertyFlags;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ Exit;
+
+ cls := batch^.ParentClass;
+ flags := cls.PropertyFlags[Index];
+ propOffset := cls.PropertyOffset[Index];
+
+ if not (cls.PropertyType[Index] in [
+ // TPropertyType.StringEnumActionProperty, --TODO? Not in SetObjString
+ //TPropertyType.MappedStringEnumOnStructArrayProperty, --TODO? Not in SetObjString
+ TPropertyType.StringProperty,
+ TPropertyType.BusProperty,
+ TPropertyType.MappedStringEnumProperty,
+ TPropertyType.StringOnArrayProperty,
+ TPropertyType.StringOnStructArrayProperty,
+ TPropertyType.BusOnStructArrayProperty
+ ]) then
+ Exit;
+
+ for i := 1 to batchSize do
+ begin
+ batch^.SetString(Index, Value^);
+ inc(batch);
+ inc(Value)
+ end;
+end;
+
+procedure Batch_SetObjectArray(batch: TDSSObjectPtr; batchSize: Integer; Index: Integer; Value: TDSSObjectPtr); CDECL;
+var
+ cls: TDSSClass;
+ propOffset: PtrUint;
+ i: Integer;
+ flags: TPropertyFlags;
+begin
+ if (batch = NIL) or (batch^ = NIL) then
+ Exit;
+
+ cls := batch^.ParentClass;
+ flags := cls.PropertyFlags[Index];
+ propOffset := cls.PropertyOffset[Index];
+
+ if cls.PropertyType[Index] <> TPropertyType.DSSObjectReferenceProperty then
+ Exit;
+
+ for i := 1 to batchSize do
+ begin
+ batch^.SetObject(Index, Value^);
+ inc(batch);
+ inc(Value)
+ end;
+end;
+
+procedure Batch_CreateFromNewS(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsName: String; Names: PPAnsiChar; Count: Integer; BeginEdit: TAPIBoolean); CDECL;
+var
+ clsIdx: Integer;
+begin
+ clsIdx := DSS.ClassNames.Find(clsname);
+ if clsIdx = 0 then
+ Exit;
+ Batch_CreateFromNew(DSS, ResultPtr, ResultCount, clsIdx, Names, Count, BeginEdit);
+end;
+
+procedure Batch_CreateByClassS(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsName: PAnsiChar); CDECL;
+var
+ clsIdx: Integer;
+begin
+ clsIdx := DSS.ClassNames.Find(clsname);
+ if clsIdx = 0 then
+ Exit;
+ Batch_CreateByClass(DSS, ResultPtr, ResultCount, clsIdx);
+end;
+
+procedure Batch_CreateByRegExpS(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsname: PAnsiChar; re: PAnsiChar); CDECL;
+var
+ clsIdx: Integer;
+begin
+ clsIdx := DSS.ClassNames.Find(clsname);
+ if clsIdx = 0 then
+ Exit;
+ Batch_CreateByRegExp(DSS, ResultPtr, ResultCount, clsIdx, re);
+end;
+
+procedure Batch_CreateByIndexS(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsname: PAnsiChar; Value: PInteger; ValueCount: Integer); CDECL;
+var
+ clsIdx: Integer;
+begin
+ clsIdx := DSS.ClassNames.Find(clsname);
+ if clsIdx = 0 then
+ begin
+ //writeln('cls not found, aborting.');
+ Exit;
+ end;
+ Batch_CreateByIndex(DSS, ResultPtr, ResultCount, clsIdx, Value, ValueCount);
+end;
+
+procedure Batch_CreateByInt32PropertyS(DSS: TDSSContext; var ResultPtr: TDSSObjectPtr; ResultCount: PAPISize; clsname: PAnsiChar; propname: PAnsiChar; value: Integer); CDECL;
+var
+ i, clsIdx: Integer;
+ propIdx: Integer = 0;
+ cls: TDSSClass;
+ spropname: String;
+begin
+ clsIdx := DSS.ClassNames.Find(clsname);
+ //WriteLn('clsIdx = ', clsIdx);
+ if clsIdx = 0 then
+ Exit;
+
+ cls := DSS.DSSClassList.At(clsIdx);
+ spropname := propname;
+ for i := 1 to cls.NumProperties do
+ begin
+ if CompareText(spropname, cls.PropertyName[i]) = 0 then
+ begin
+ propIdx := i;
+ break;
+ end;
+ end;
+ if propIdx = 0 then
+ Exit;
+
+ //WriteLn('propIdx = ', propIdx);
+ Batch_CreateByInt32Property(DSS, ResultPtr, ResultCount, clsIdx, propidx, value);
+end;
+
+
+//------------------------------------------------------------------------------
+
+function GetPropIndex(batch: TDSSObjectPtr; Name: String; out propIdx: Integer): Boolean;
+var
+ i: Integer;
+ cls: TDSSClass;
+begin
+ cls := batch^.ParentClass;
+ for i := 1 to cls.NumProperties do
+ begin
+ if CompareText(Name, cls.PropertyName[i]) = 0 then
+ begin
+ propIdx := i;
+ Result := True;
+ Exit;
+ end;
+ end;
+ Result := False;
+end;
+
+procedure Batch_GetFloat64S(var ResultPtr: PDouble; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Name: PChar); CDECL;
+var
+ propIdx: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ if not GetPropIndex(batch, Name, propIdx) then
+ Exit;
+
+ Batch_GetFloat64(ResultPtr, ResultCount, batch, batchSize, propIdx);
+end;
+
+procedure Batch_GetInt32S(var ResultPtr: PInteger; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Name: PChar); CDECL;
+var
+ propIdx: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ if not GetPropIndex(batch, Name, propIdx) then
+ Exit;
+
+ Batch_GetInt32(ResultPtr, ResultCount, batch, batchSize, propIdx);
+end;
+
+procedure Batch_GetStringS(var ResultPtr: PPAnsiChar; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Name: PChar); CDECL;
+var
+ propIdx: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ if not GetPropIndex(batch, Name, propIdx) then
+ Exit;
+
+ Batch_GetString(ResultPtr, ResultCount, batch, batchSize, propIdx);
+end;
+
+procedure Batch_GetAsStringS(var ResultPtr: PPAnsiChar; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Name: PChar); CDECL;
+var
+ propIdx: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ if not GetPropIndex(batch, Name, propIdx) then
+ Exit;
+
+ Batch_GetAsString(ResultPtr, ResultCount, batch, batchSize, propIdx);
+end;
+
+procedure Batch_GetObjectS(var ResultPtr: PPointer; ResultCount: PAPISize; batch: TDSSObjectPtr; batchSize: Integer; Name: PChar); CDECL;
+var
+ propIdx: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ if not GetPropIndex(batch, Name, propIdx) then
+ Exit;
+
+ Batch_GetObject(ResultPtr, ResultCount, batch, batchSize, propIdx);
+end;
+
+// procedure Batch_SetAsStringS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: PAnsiChar); CDECL;
+// var
+// propIdx: Integer;
+// begin
+// if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+// Exit;
+
+// if not GetPropIndex(batch, Name, propIdx) then
+// Exit;
+
+// Batch_SetAsString(batch, batchSize, propIdx, Value);
+// end;
+
+procedure Batch_Float64S(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Operation: Integer; Value: Double); CDECL;
+var
+ propIdx: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ if not GetPropIndex(batch, Name, propIdx) then
+ Exit;
+
+ Batch_Float64(batch, batchSize, propIdx, Operation, Value);
+end;
+
+procedure Batch_Int32S(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Operation: Integer; Value: Integer); CDECL;
+var
+ propIdx: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ if not GetPropIndex(batch, Name, propIdx) then
+ Exit;
+
+ Batch_Int32(batch, batchSize, propIdx, Operation, Value);
+end;
+
+procedure Batch_SetStringS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: PAnsiChar); CDECL;
+var
+ propIdx: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ if not GetPropIndex(batch, Name, propIdx) then
+ Exit;
+
+ Batch_SetString(batch, batchSize, propIdx, Value);
+end;
+
+procedure Batch_SetObjectS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: TDSSObject); CDECL;
+var
+ propIdx: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ if not GetPropIndex(batch, Name, propIdx) then
+ Exit;
+
+ Batch_SetObject(batch, batchSize, propIdx, Value);
+end;
+
+
+procedure Batch_SetFloat64ArrayS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: PDouble); CDECL;
+var
+ propIdx: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ if not GetPropIndex(batch, Name, propIdx) then
+ Exit;
+
+ Batch_SetFloat64Array(batch, batchSize, propIdx, Value);
+end;
+
+procedure Batch_SetInt32ArrayS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: PInteger); CDECL;
+var
+ propIdx: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ if not GetPropIndex(batch, Name, propIdx) then
+ Exit;
+
+ Batch_SetInt32Array(batch, batchSize, propIdx, Value);
+end;
+
+procedure Batch_SetStringArrayS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: PPAnsiChar); CDECL;
+var
+ propIdx: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ if not GetPropIndex(batch, Name, propIdx) then
+ Exit;
+
+ Batch_SetStringArray(batch, batchSize, propIdx, Value);
+end;
+
+procedure Batch_SetObjectArrayS(batch: TDSSObjectPtr; batchSize: Integer; Name: PChar; Value: TDSSObjectPtr); CDECL;
+var
+ propIdx: Integer;
+begin
+ if (batch = NIL) or (batch^ = NIL) or (batchSize <= 0) then
+ Exit;
+
+ if not GetPropIndex(batch, Name, propIdx) then
+ Exit;
+
+ Batch_SetObjectArray(batch, batchSize, propIdx, Value);
+end;
+
+end.
\ No newline at end of file
diff --git a/src/CAPI/CAPI_PDElements.pas b/src/CAPI/CAPI_PDElements.pas
index b0ba85f2a..8a636e726 100644
--- a/src/CAPI/CAPI_PDElements.pas
+++ b/src/CAPI/CAPI_PDElements.pas
@@ -66,7 +66,7 @@ implementation
DSSPointerList,
Bus,
XYCurve,
- ucomplex,
+ UComplex, DSSUcomplex,
ArrayDef,
Utilities,
Math,
@@ -88,7 +88,7 @@ function _activeObj(DSS: TDSSContext; out obj: TPDElement): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active PD Element found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, _('No active PD Element found! Activate one and retry.'), 8989);
end;
Exit;
end;
@@ -97,7 +97,7 @@ function _activeObj(DSS: TDSSContext; out obj: TPDElement): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active PD Element found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, _('No active PD Element found! Activate one and retry.'), 8989);
end;
Exit;
end;
@@ -188,7 +188,7 @@ function PDElements_Get_Name(): PAnsiChar; CDECL;
Result := NIL; // return null if not a PD element
if not _activeObj(DSSPrime, ActivePDElement) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, Format('%s.%s', [ActivePDElement.Parentclass.Name, ActivePDElement.Name])); // full name
+ Result := DSS_GetAsPAnsiChar(DSSPrime, ActivePDElement.FullName); // full name
end;
//------------------------------------------------------------------------------
@@ -208,7 +208,7 @@ procedure PDElements_Set_Name(const Value: PAnsiChar); CDECL; //TODO: rewrite to
while Assigned(ActivePDElement) do
with ActivePDelement do
begin
- if (CompareText(TestString, Format('%s.%s', [Parentclass.Name, Name])) = 0) then
+ if (AnsiCompareText(TestString, FullName) = 0) then
begin
ActiveCktElement := ActivePDElement;
Break;
@@ -361,7 +361,7 @@ procedure PDElements_Get_AllNames(var ResultPtr: PPAnsiChar; ResultCount: PAPISi
begin
// if (elem.Enabled or DSS_CAPI_ITERATE_DISABLED) then
begin
- Result[k] := DSS_CopyStringAsPChar(elem.DSSClassName + '.' + elem.Name);
+ Result[k] := DSS_CopyStringAsPChar(elem.FullName);
Inc(k);
end;
elem := pList.Next;
@@ -882,7 +882,7 @@ procedure PDElements_Get_AllSeqPowers(var ResultPtr: PDouble; ResultCount: PAPIS
k := (j - 1) * NConds;
n := NodeRef^[k + 1];
Vph[1] := Solution.NodeV^[n]; // Get voltage at node
- S := Cmul(Vph[1], conjg(cBuffer^[k + 1])); // Compute power per phase
+ S := Vph[1] * cong(cBuffer^[k + 1]); // Compute power per phase
Result[icount] := S.re * 0.003; // 3-phase kW conversion
Result[icount + 1] := S.im * 0.003; // 3-phase kvar conversion
Inc(icount, 6);
@@ -917,7 +917,7 @@ procedure PDElements_Get_AllSeqPowers(var ResultPtr: PDouble; ResultCount: PAPIS
for i := 1 to 3 do
begin
- S := Cmul(V012[i], conjg(I012[i]));
+ S := V012[i] * cong(I012[i]);
Result[icount] := S.re * 0.003; // 3-phase kW conversion
Result[icount + 1] := S.im * 0.003; // 3-phase kW conversion
Inc(icount, 2);
diff --git a/src/CAPI/CAPI_PVSystems.pas b/src/CAPI/CAPI_PVSystems.pas
index 7867be035..2d36b9e5a 100644
--- a/src/CAPI/CAPI_PVSystems.pas
+++ b/src/CAPI/CAPI_PVSystems.pas
@@ -59,6 +59,21 @@ implementation
DSSClass,
DSSHelper;
+
+function ErrorIfTShapeNil(DSS: TDSSContext; name: String): Pointer;
+begin
+ Result := DSS.TShapeClass.Find(name);
+ if (Result = NIL) and (DSS_CAPI_EXT_ERRORS) then
+ DoSimpleMsg(DSS, 'TShape "%s" not found!', [name], 89891);
+end;
+
+function ErrorIfLoadShapeNil(DSS: TDSSContext; name: String): Pointer;
+begin
+ Result := DSS.LoadShapeClass.Find(name);
+ if (Result = NIL) and (DSS_CAPI_EXT_ERRORS) then
+ DoSimpleMsg(DSS, 'LoadShape "%s" not found!', [name], 89891);
+end;
+
//------------------------------------------------------------------------------
function _activeObj(DSS: TDSSContext; out obj: TPVSystemObj): Boolean; inline;
begin
@@ -72,7 +87,7 @@ function _activeObj(DSS: TDSSContext; out obj: TPVSystemObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active PVSystem object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['PVSystem'], 8989);
end;
Exit;
end;
@@ -92,7 +107,7 @@ function _activeObj2(DSS: TDSSContext; out obj: TPVSystem2Obj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active PVSystem object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['PVSystem'], 8989);
end;
Exit;
end;
@@ -230,7 +245,7 @@ procedure PVSystems_Set_idx(Value: Integer); CDECL;
pPVSystem := DSSPrime.ActiveCircuit.PVSystems.Get(Value);
if pPVSystem = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid PVSystem index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['PVSystem', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pPVSystem;
@@ -268,7 +283,7 @@ procedure PVSystems_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'PVSystem "' + Value + '" Not Found in Active Circuit.', 5003);
+ DoSimpleMsg(DSSPrime, 'PVSystem "%s" not found in Active Circuit.', [Value], 5003);
end;
Exit;
end;
@@ -280,7 +295,7 @@ procedure PVSystems_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'PVSystem "' + Value + '" Not Found in Active Circuit.', 5003);
+ DoSimpleMsg(DSSPrime, 'PVSystem "%s" not found in Active Circuit.', [Value], 5003);
end;
end;
//------------------------------------------------------------------------------
@@ -456,12 +471,14 @@ function PVSystems_Get_daily(): PAnsiChar; CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.DailyShape);
+ if elem.DailyShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.DailyShapeObj.Name);
Exit;
end;
if not _activeObj2(DSSPrime, elem2) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem2.DailyShape);
+ if elem2.DailyShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem2.DailyShapeObj.Name);
end;
//------------------------------------------------------------------------------
procedure PVSystems_Set_daily(const Value: PAnsiChar); CDECL;
@@ -473,14 +490,12 @@ procedure PVSystems_Set_daily(const Value: PAnsiChar); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.DailyShape := Value;
- elem.DailyShapeObj := DSSPrime.LoadShapeClass.Find(elem.DailyShape);
+ elem.DailyShapeObj := ErrorIfLoadShapeNil(DSSPrime, Value);
Exit;
end;
if not _activeObj2(DSSPrime, elem2) then
Exit;
- elem2.DailyShape := Value;
- elem2.DailyShapeObj := DSSPrime.LoadShapeClass.Find(elem2.DailyShape);
+ elem2.DailyShapeObj := ErrorIfLoadShapeNil(DSSPrime, Value);
end;
//------------------------------------------------------------------------------
function PVSystems_Get_duty(): PAnsiChar; CDECL;
@@ -493,12 +508,15 @@ function PVSystems_Get_duty(): PAnsiChar; CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.DutyShape);
+ if elem.DutyShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.DutyShapeObj.Name);
Exit;
end;
if not _activeObj2(DSSPrime, elem2) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem2.DutyShape);
+
+ if elem2.DutyShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem2.DutyShapeObj.Name);
end;
//------------------------------------------------------------------------------
procedure PVSystems_Set_duty(const Value: PAnsiChar); CDECL;
@@ -510,14 +528,12 @@ procedure PVSystems_Set_duty(const Value: PAnsiChar); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.DutyShape := Value;
- elem.DutyShapeObj := DSSPrime.LoadShapeClass.Find(elem.DutyShape);
+ elem.DutyShapeObj := ErrorIfLoadShapeNil(DSSPrime, Value);
Exit;
end;
if not _activeObj2(DSSPrime, elem2) then
Exit;
- elem2.DutyShape := Value;
- elem2.DutyShapeObj := DSSPrime.LoadShapeClass.Find(elem2.DutyShape);
+ elem2.DutyShapeObj := ErrorIfLoadShapeNil(DSSPrime, Value);
end;
//------------------------------------------------------------------------------
function PVSystems_Get_yearly(): PAnsiChar; CDECL;
@@ -530,12 +546,14 @@ function PVSystems_Get_yearly(): PAnsiChar; CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.YearlyShape);
+ if elem.YearlyShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.YearlyShapeObj.Name);
Exit;
end;
if not _activeObj2(DSSPrime, elem2) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem2.YearlyShape);
+ if elem2.YearlyShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem2.YearlyShapeObj.Name);
end;
//------------------------------------------------------------------------------
procedure PVSystems_Set_yearly(const Value: PAnsiChar); CDECL;
@@ -547,14 +565,12 @@ procedure PVSystems_Set_yearly(const Value: PAnsiChar); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.YearlyShape := Value;
- elem.YearlyShapeObj := DSSPrime.LoadShapeClass.Find(elem.YearlyShape);
+ elem.YearlyShapeObj := ErrorIfLoadShapeNil(DSSPrime, Value);
Exit;
end;
if not _activeObj2(DSSPrime, elem2) then
Exit;
- elem2.YearlyShape := Value;
- elem2.YearlyShapeObj := DSSPrime.LoadShapeClass.Find(elem2.YearlyShape);
+ elem2.YearlyShapeObj := ErrorIfLoadShapeNil(DSSPrime, Value);
end;
//------------------------------------------------------------------------------
function PVSystems_Get_Tdaily(): PAnsiChar; CDECL;
@@ -567,12 +583,14 @@ function PVSystems_Get_Tdaily(): PAnsiChar; CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.DailyTShape);
+ if elem.DailyTShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.DailyTShapeObj.Name);
Exit;
end;
if not _activeObj2(DSSPrime, elem2) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem2.DailyTShape);
+ if elem2.DailyTShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem2.DailyTShapeObj.Name);
end;
//------------------------------------------------------------------------------
procedure PVSystems_Set_Tdaily(const Value: PAnsiChar); CDECL;
@@ -584,14 +602,12 @@ procedure PVSystems_Set_Tdaily(const Value: PAnsiChar); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.DailyTShape := Value;
- elem.DailyTShapeObj := DSSPrime.TShapeClass.Find(elem.DailyTShape);
+ elem.DailyTShapeObj := ErrorIfTShapeNil(DSSPrime, Value);
Exit;
end;
if not _activeObj2(DSSPrime, elem2) then
Exit;
- elem2.DailyTShape := Value;
- elem2.DailyTShapeObj := DSSPrime.TShapeClass.Find(elem2.DailyTShape);
+ elem2.DailyTShapeObj := ErrorIfTShapeNil(DSSPrime, Value);
end;
//------------------------------------------------------------------------------
function PVSystems_Get_Tduty(): PAnsiChar; CDECL;
@@ -604,12 +620,15 @@ function PVSystems_Get_Tduty(): PAnsiChar; CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.DutyTShape);
+ if elem.DutyTShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.DutyTShapeObj.Name);
Exit;
end;
if not _activeObj2(DSSPrime, elem2) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem2.DutyTShape);
+
+ if elem2.DutyTShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem2.DutyTShapeObj.Name);
end;
//------------------------------------------------------------------------------
procedure PVSystems_Set_Tduty(const Value: PAnsiChar); CDECL;
@@ -621,14 +640,12 @@ procedure PVSystems_Set_Tduty(const Value: PAnsiChar); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.DutyTShape := Value;
- elem.DutyTShapeObj := DSSPrime.TShapeClass.Find(elem.DutyTShape);
+ elem.DutyTShapeObj := ErrorIfTShapeNil(DSSPrime, Value);
Exit;
end;
if not _activeObj2(DSSPrime, elem2) then
Exit;
- elem2.DutyTShape := Value;
- elem2.DutyTShapeObj := DSSPrime.TShapeClass.Find(elem2.DutyTShape);
+ elem2.DutyTShapeObj := ErrorIfTShapeNil(DSSPrime, Value);
end;
//------------------------------------------------------------------------------
function PVSystems_Get_Tyearly(): PAnsiChar; CDECL;
@@ -641,12 +658,15 @@ function PVSystems_Get_Tyearly(): PAnsiChar; CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.YearlyTShape);
+ if elem.YearlyTShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.YearlyTShapeObj.Name);
Exit;
end;
if not _activeObj2(DSSPrime, elem2) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem2.YearlyTShape);
+
+ if elem2.YearlyTShapeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem2.YearlyTShapeObj.Name);
end;
//------------------------------------------------------------------------------
procedure PVSystems_Set_Tyearly(const Value: PAnsiChar); CDECL;
@@ -658,14 +678,12 @@ procedure PVSystems_Set_Tyearly(const Value: PAnsiChar); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.YearlyTShape := Value;
- elem.YearlyTShapeObj := DSSPrime.TShapeClass.Find(elem.YearlyTShape);
+ elem.YearlyTShapeObj := ErrorIfTShapeNil(DSSPrime, Value);
Exit;
end;
if not _activeObj2(DSSPrime, elem2) then
Exit;
- elem2.YearlyTShape := Value;
- elem2.YearlyTShapeObj := DSSPrime.TShapeClass.Find(elem2.YearlyTShape);
+ elem2.YearlyTShapeObj := ErrorIfTShapeNil(DSSPrime, Value);
end;
//------------------------------------------------------------------------------
function PVSystems_Get_Pmpp(): Double; CDECL;
@@ -731,16 +749,16 @@ function PVSystems_Get_Sensor(): PAnsiChar; CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- if elem.SensorObj <> NIL then
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.SensorObj.ElementName);
+ if (elem.SensorObj <> NIL) and (elem.SensorObj.MeteredElement <> NIL) then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(elem.SensorObj.MeteredElement.FullName));
Exit;
end;
if not _activeObj2(DSSPrime, elem2) then
Exit;
- if elem2.SensorObj <> NIL then
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem2.SensorObj.ElementName);
+ if (elem2.SensorObj <> NIL) and (elem2.SensorObj.MeteredElement <> NIL) then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(elem2.SensorObj.MeteredElement.FullName));
end;
//------------------------------------------------------------------------------
end.
diff --git a/src/CAPI/CAPI_Parallel.pas b/src/CAPI/CAPI_Parallel.pas
index 6c33397d1..673b4efa5 100644
--- a/src/CAPI/CAPI_Parallel.pas
+++ b/src/CAPI/CAPI_Parallel.pas
@@ -6,6 +6,7 @@ interface
CAPI_Utils,
CAPI_Types;
+{$IFDEF DSS_CAPI_PM}
function Parallel_Get_NumCPUs(): Integer; CDECL;
function Parallel_Get_NumCores(): Integer; CDECL;
function Parallel_Get_ActiveActor(): Integer; CDECL;
@@ -23,9 +24,9 @@ function Parallel_Get_ActiveParallel(): Integer; CDECL;
procedure Parallel_Set_ActiveParallel(Value: Integer); CDECL;
function Parallel_Get_ConcatenateReports(): Integer; CDECL;
procedure Parallel_Set_ConcatenateReports(Value: Integer); CDECL;
-
+{$ENDIF}
implementation
-
+{$IFDEF DSS_CAPI_PM}
uses
CAPI_Constants,
DSSGlobals,
@@ -33,14 +34,12 @@ implementation
SysUtils,
solution,
CktElement,
- ParserDel,
KLUSolve,
Classes,
DSSClass,
DSSHelper;
-{$IFDEF DSS_CAPI_PM}
function Parallel_Get_NumCPUs(): Integer; CDECL;
begin
Result := CPU_Cores;
@@ -48,12 +47,12 @@ function Parallel_Get_NumCPUs(): Integer; CDECL;
//------------------------------------------------------------------------------
function Parallel_Get_NumCores(): Integer; CDECL;
begin
- Result := round(CPU_Cores / 2); //TODO: fix
+ Result := CPU_Cores div 2; //TODO: fix
end;
//------------------------------------------------------------------------------
function Parallel_Get_ActiveActor(): Integer; CDECL;
begin
- Result := DSSPrime.ActiveChildIndex;
+ Result := DSSPrime.ActiveChildIndex + 1;
end;
//------------------------------------------------------------------------------
procedure Parallel_Set_ActiveActor(Value: Integer); CDECL;
@@ -64,7 +63,7 @@ procedure Parallel_Set_ActiveActor(Value: Integer); CDECL;
DSSPrime.ActiveChild := DSSPrime.Children[DSSPrime.ActiveChildIndex];
end
else
- DoSimpleMsg(DSSPrime, 'The actor does not exists', 7002);
+ DoSimpleMsg(DSSPrime, _('The actor does not exists'), 7002);
end;
//------------------------------------------------------------------------------
procedure Parallel_CreateActor(); CDECL;
@@ -85,7 +84,7 @@ procedure Parallel_Set_ActorCPU(Value: Integer); CDECL;
if DSSPrime.ActiveChild.ActorThread <> nil then
DSSPrime.ActiveChild.ActorThread.CPU := value;
end
- else DoSimpleMsg(DSSPrime, 'The CPU does not exist', 7004);
+ else DoSimpleMsg(DSSPrime, _('The CPU does not exist'), 7004);
end;
//------------------------------------------------------------------------------
function Parallel_Get_NumOfActors(): Integer; CDECL;
@@ -94,8 +93,6 @@ function Parallel_Get_NumOfActors(): Integer; CDECL;
end;
//------------------------------------------------------------------------------
procedure Parallel_Wait(); CDECL;
-var
- i: Integer;
begin
if DSSPrime.Parallel_enabled then
Wait4Actors(DSSPrime, 0);
@@ -127,12 +124,7 @@ procedure Parallel_Get_ActorStatus(var ResultPtr: PInteger; ResultCount: PAPISiz
begin
Result := DSS_RecreateArray_PInteger(ResultPtr, ResultCount, DSSPrime.NumOfActors);
for idx := 0 to High(DSSPrime.Children) do
- begin
- if DSSPrime.Children[idx].ActorThread.Is_Busy then
- Result[idx] := Ord(TActorStatus.Busy)
- else
- Result[idx] := Ord(TActorStatus.Idle);
- end;
+ Result[idx] := Ord(DSSPrime.Children[idx].ActorStatus);
end;
procedure Parallel_Get_ActorStatus_GR(); CDECL;
@@ -152,7 +144,7 @@ function Parallel_Get_ActiveParallel(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Parallel_Set_ActiveParallel(Value: Integer); CDECL;
begin
- DSSPrime.Parallel_enabled := (Value = 1); //TODO
+ DSSPrime.Parallel_enabled := (Value = 1);
end;
//------------------------------------------------------------------------------
function Parallel_Get_ConcatenateReports(): Integer; CDECL;
@@ -168,96 +160,5 @@ procedure Parallel_Set_ConcatenateReports(Value: Integer); CDECL;
DSSPrime.ConcatenateReports := (Value = 1);
end;
//------------------------------------------------------------------------------
-{$ELSE}
-function NotAvailable(DSS: TDSSContext): Integer;
-begin
- DoSimpleMsg(DSS, 'Parallel machine functions were not compiled', 7982);
- Result := -1;
-end;
-
-function Parallel_Get_NumCPUs(): Integer; CDECL;
-begin
- Result := NotAvailable(DSSPrime);
-end;
-
-function Parallel_Get_NumCores(): Integer; CDECL;
-begin
- Result := NotAvailable(DSSPrime);
-end;
-
-function Parallel_Get_ActiveActor(): Integer; CDECL;
-begin
- Result := NotAvailable(DSSPrime);
-end;
-
-procedure Parallel_Set_ActiveActor(Value: Integer); CDECL;
-begin
- NotAvailable(DSSPrime);
-end;
-
-procedure Parallel_CreateActor(); CDECL;
-begin
- NotAvailable(DSSPrime);
-end;
-
-function Parallel_Get_ActorCPU(): Integer; CDECL;
-begin
- Result := NotAvailable(DSSPrime);
-end;
-
-procedure Parallel_Set_ActorCPU(Value: Integer); CDECL;
-begin
- NotAvailable(DSSPrime);
-end;
-
-function Parallel_Get_NumOfActors(): Integer; CDECL;
-begin
- Result := NotAvailable(DSSPrime);
-end;
-
-procedure Parallel_Wait(); CDECL;
-begin
- NotAvailable(DSSPrime);
-end;
-
-procedure Parallel_Get_ActorProgress(var ResultPtr: PInteger; ResultCount: PAPISize); CDECL;
-begin
- NotAvailable(DSSPrime);
-end;
-
-procedure Parallel_Get_ActorProgress_GR(); CDECL;
-begin
- NotAvailable(DSSPrime);
-end;
-
-procedure Parallel_Get_ActorStatus(var ResultPtr: PInteger; ResultCount: PAPISize); CDECL;
-begin
- NotAvailable(DSSPrime);
-end;
-
-procedure Parallel_Get_ActorStatus_GR(); CDECL;
-begin
- NotAvailable(DSSPrime);
-end;
-
-function Parallel_Get_ActiveParallel(): Integer; CDECL;
-begin
- Result := NotAvailable(DSSPrime);
-end;
-
-procedure Parallel_Set_ActiveParallel(Value: Integer); CDECL;
-begin
- NotAvailable(DSSPrime);
-end;
-
-function Parallel_Get_ConcatenateReports(): Integer; CDECL;
-begin
- Result := NotAvailable(DSSPrime);
-end;
-
-procedure Parallel_Set_ConcatenateReports(Value: Integer); CDECL;
-begin
- NotAvailable(DSSPrime);
-end;
{$ENDIF}
end.
diff --git a/src/CAPI/CAPI_Parser.pas b/src/CAPI/CAPI_Parser.pas
index 741676310..4719fd39f 100644
--- a/src/CAPI/CAPI_Parser.pas
+++ b/src/CAPI/CAPI_Parser.pas
@@ -38,93 +38,90 @@ implementation
ArrayDef,
DSSClass;
-var
- ComParser: ParserDel.TParser;
-
//------------------------------------------------------------------------------
function Parser_Get_CmdString(): PAnsiChar; CDECL;
begin
- Result := DSS_GetAsPAnsiChar(DSSPrime, ComParser.CmdString);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, DSSPrime.ComParser.CmdString);
end;
//------------------------------------------------------------------------------
procedure Parser_Set_CmdString(const Value: PAnsiChar); CDECL;
begin
- ComParser.CmdString := Value;
+ DSSPrime.ComParser.CmdString := Value;
end;
//------------------------------------------------------------------------------
function Parser_Get_NextParam(): PAnsiChar; CDECL;
begin
- Result := DSS_GetAsPAnsiChar(DSSPrime, ComParser.NextParam);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, DSSPrime.ComParser.NextParam);
end;
//------------------------------------------------------------------------------
function Parser_Get_AutoIncrement(): TAPIBoolean; CDECL;
begin
- Result := ComParser.AutoIncrement;
+ Result := DSSPrime.ComParser.AutoIncrement;
end;
//------------------------------------------------------------------------------
procedure Parser_Set_AutoIncrement(Value: TAPIBoolean); CDECL;
begin
- ComParser.AutoIncrement := Value;
+ DSSPrime.ComParser.AutoIncrement := Value;
end;
//------------------------------------------------------------------------------
function Parser_Get_DblValue(): Double; CDECL;
begin
- Result := ComParser.DblValue;
+ Result := DSSPrime.ComParser.DblValue;
end;
//------------------------------------------------------------------------------
function Parser_Get_IntValue(): Integer; CDECL;
begin
- Result := ComParser.IntValue;
+ Result := DSSPrime.ComParser.IntValue;
end;
//------------------------------------------------------------------------------
function Parser_Get_StrValue(): PAnsiChar; CDECL;
begin
- Result := DSS_GetAsPAnsiChar(DSSPrime, ComParser.StrValue);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, DSSPrime.ComParser.StrValue);
end;
//------------------------------------------------------------------------------
function Parser_Get_WhiteSpace(): PAnsiChar; CDECL;
begin
- Result := DSS_GetAsPAnsiChar(DSSPrime, Comparser.Whitespace);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, DSSPrime.ComParser.Whitespace);
end;
//------------------------------------------------------------------------------
procedure Parser_Set_WhiteSpace(const Value: PAnsiChar); CDECL;
begin
- ComParser.Whitespace := Value;
+ DSSPrime.ComParser.Whitespace := Value;
end;
//------------------------------------------------------------------------------
function Parser_Get_BeginQuote(): PAnsiChar; CDECL;
begin
- Result := DSS_GetAsPAnsiChar(DSSPrime, ComParser.BeginQuoteChars);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, DSSPrime.ComParser.BeginQuoteChars);
end;
//------------------------------------------------------------------------------
function Parser_Get_EndQuote(): PAnsiChar; CDECL;
begin
- Result := DSS_GetAsPAnsiChar(DSSPrime, ComParser.EndQuoteChars);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, DSSPrime.ComParser.EndQuoteChars);
end;
//------------------------------------------------------------------------------
procedure Parser_Set_BeginQuote(const Value: PAnsiChar); CDECL;
begin
- ComParser.BeginQuoteChars := Value;
+ DSSPrime.ComParser.BeginQuoteChars := Value;
end;
//------------------------------------------------------------------------------
procedure Parser_Set_EndQuote(const Value: PAnsiChar); CDECL;
begin
- ComParser.EndQuoteChars := Value;
+ DSSPrime.ComParser.EndQuoteChars := Value;
end;
//------------------------------------------------------------------------------
function Parser_Get_Delimiters(): PAnsiChar; CDECL;
begin
- Result := DSS_GetAsPAnsiChar(DSSPrime, ComParser.Delimiters);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, DSSPrime.ComParser.Delimiters);
end;
//------------------------------------------------------------------------------
procedure Parser_Set_Delimiters(const Value: PAnsiChar); CDECL;
begin
- ComParser.Delimiters := Value;
+ DSSPrime.ComParser.Delimiters := Value;
end;
//------------------------------------------------------------------------------
procedure Parser_ResetDelimiters(); CDECL;
begin
- ComParser.ResetDelims;
+ DSSPrime.ComParser.ResetDelims;
end;
//------------------------------------------------------------------------------
procedure Parser_Get_Vector(var ResultPtr: PDouble; ResultCount: PAPISize; ExpectedSize: Integer); CDECL;
@@ -132,7 +129,7 @@ procedure Parser_Get_Vector(var ResultPtr: PDouble; ResultCount: PAPISize; Expec
ActualSize: Integer;
begin
DSS_RecreateArray_PDouble(ResultPtr, ResultCount, ExpectedSize);
- ActualSize := ComParser.ParseAsVector(ResultCount^, ArrayDef.PDoubleArray(ResultPtr));
+ ActualSize := DSSPrime.ComParser.ParseAsVector(ResultCount^, ArrayDef.PDoubleArray(ResultPtr));
ResultCount^ := ActualSize;
end;
@@ -146,7 +143,7 @@ procedure Parser_Get_Vector_GR(ExpectedSize: Integer); CDECL;
procedure Parser_Get_Matrix(var ResultPtr: PDouble; ResultCount: PAPISize; ExpectedOrder: Integer); CDECL;
begin
DSS_RecreateArray_PDouble(ResultPtr, ResultCount, ExpectedOrder * ExpectedOrder);
- ComParser.ParseAsMatrix(ResultCount^, ArrayDef.PDoubleArray(ResultPtr));
+ DSSPrime.ComParser.ParseAsMatrix(ResultCount^, ArrayDef.PDoubleArray(ResultPtr));
end;
procedure Parser_Get_Matrix_GR(ExpectedOrder: Integer); CDECL;
@@ -159,7 +156,7 @@ procedure Parser_Get_Matrix_GR(ExpectedOrder: Integer); CDECL;
procedure Parser_Get_SymMatrix(var ResultPtr: PDouble; ResultCount: PAPISize; ExpectedOrder: Integer); CDECL;
begin
DSS_RecreateArray_PDouble(ResultPtr, ResultCount, ExpectedOrder * ExpectedOrder);
- ComParser.ParseAsSymMatrix(ResultCount^, ArrayDef.PDoubleArray(ResultPtr));
+ DSSPrime.ComParser.ParseAsSymMatrix(ResultCount^, ArrayDef.PDoubleArray(ResultPtr));
end;
procedure Parser_Get_SymMatrix_GR(ExpectedOrder: Integer); CDECL;
@@ -168,8 +165,4 @@ procedure Parser_Get_SymMatrix_GR(ExpectedOrder: Integer); CDECL;
Parser_Get_SymMatrix(DSSPrime.GR_DataPtr_PDouble, DSSPrime.GR_Counts_PDouble, ExpectedOrder)
end;
-//------------------------------------------------------------------------------
-initialization
- ComParser := ParserDel.TParser.Create; // create COM Parser object
-
end.
diff --git a/src/CAPI/CAPI_Reactors.pas b/src/CAPI/CAPI_Reactors.pas
index ad76d8290..97c649efe 100644
--- a/src/CAPI/CAPI_Reactors.pas
+++ b/src/CAPI/CAPI_Reactors.pas
@@ -74,10 +74,7 @@ implementation
SysUtils,
DSSPointerList,
Utilities,
- ucomplex;
-
-type
- ReactorProps = (bus1 = 1, bus2, phases, kvar, kv, conn, Rmatrix, Xmatrix, Parallel, R, X, Rp, Z1, Z2, Z0, Z, RCurve, LCurve, LmH);
+ UComplex, DSSUcomplex;
//------------------------------------------------------------------------------
function _activeObj(DSS: TDSSContext; out obj: TReactorObj): Boolean; inline;
@@ -92,7 +89,7 @@ function _activeObj(DSS: TDSSContext; out obj: TReactorObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active Reactor object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['Reactor'], 8989);
end;
Exit;
end;
@@ -100,84 +97,6 @@ function _activeObj(DSS: TDSSContext; out obj: TReactorObj): Boolean; inline;
Result := True;
end;
//------------------------------------------------------------------------------
-procedure ReactorPropSideEffects(DSS: TDSSContext; prop: ReactorProps; reactor: TReactorObj);
-begin
- with reactor do
- begin
- // Some specials ...
- case prop of
- ReactorProps.bus1:
- begin
- PropertyValue[2] := GetBus(2); // this gets modified
- PrpSequence^[2] := 0; // Reset this for save function
- end;
- ReactorProps.bus2:
- if CompareText(StripExtension(GetBus(1)), StripExtension(GetBus(2))) <> 0 then
- begin
- IsShunt := FALSE;
- Bus2Defined := TRUE;
- end;
- ReactorProps.phases: //IF Fnphases <> Parser.IntValue THEN
- begin
- // Nphases := Parser.IntValue ;
- NConds := nphases; // Force Reallocation of terminal info
- Yorder := Fnterms * Fnconds;
- end;
- ReactorProps.kvar:
- SpecType := 1; // X specified by kvar, kV
- ReactorProps.Rmatrix, ReactorProps.Xmatrix:
- SpecType := 3;
- ReactorProps.X:
- SpecType := 2; // X specified directly rather than computed from kvar
- ReactorProps.Rp:
- RpSpecified := TRUE;
- ReactorProps.Z1:
- begin
- SpecType := 4; // have to set Z1 to get this mode
- if not Z2Specified then
- Z2 := Z1;
- if not Z0Specified then
- Z0 := Z1;
- end;
- ReactorProps.Z2:
- Z2Specified := TRUE;
- ReactorProps.Z0:
- Z0Specified := TRUE;
- ReactorProps.Z:
- begin
- R := Z.re;
- X := Z.im;
- SpecType := 2;
- end;
- ReactorProps.RCurve:
- RCurveObj := DSS.XYCurveClass.Find(RCurve);
- ReactorProps.LCurve:
- LCurveObj := DSS.XYCurveClass.Find(LCurve);
- ReactorProps.LmH:
- begin
- SpecType := 2;
- X := L * TwoPi * BaseFrequency;
- end
- else
- end;
-
- //YPrim invalidation on anything that changes impedance values
- case prop of
- ReactorProps.phases..ReactorProps.Z:
- YprimInvalid := TRUE;
- ReactorProps.RCurve:
- if RCurveObj = NIL then
- DoSimpleMsg('Resistance-frequency curve XYCurve.' + RCurve + ' not Found.', 2301);
- ReactorProps.LCurve:
- if LCurveObj = NIL then
- DoSimpleMsg('Inductance-frequency curve XYCurve.' + LCurve + ' not Found.', 2301);
- ReactorProps.LmH:
- YprimInvalid := TRUE;
- else
- end;
- end;
-end;
-//------------------------------------------------------------------------------
procedure Reactors_Get_AllNames(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
begin
DefaultResult(ResultPtr, ResultCount);
@@ -230,7 +149,7 @@ procedure Reactors_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'Reactor "' + Value + '" Not Found in Active Circuit.', 5003);
+ DoSimpleMsg(DSSPrime, 'Reactor "%s" not found in Active Circuit.', [Value], 5003);
end;
end;
//------------------------------------------------------------------------------
@@ -269,7 +188,9 @@ function Reactors_Get_LCurve(): PAnsiChar; CDECL;
Result := NIL;
if not _activeObj(DSSPrime, pReactor) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, pReactor.LCurve);
+
+ if pReactor.LCurveObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, pReactor.LCurveObj.Name);
end;
//------------------------------------------------------------------------------
function Reactors_Get_RCurve(): PAnsiChar; CDECL;
@@ -279,7 +200,9 @@ function Reactors_Get_RCurve(): PAnsiChar; CDECL;
Result := NIL;
if not _activeObj(DSSPrime, pReactor) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, pReactor.RCurve);
+
+ if pReactor.RCurveObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, pReactor.RCurveObj.Name);
end;
//------------------------------------------------------------------------------
function Reactors_Get_Parallel(): TAPIBoolean; CDECL;
@@ -385,26 +308,31 @@ function Reactors_Get_Rp(): Double; CDECL;
procedure Reactors_Set_IsDelta(Value: TAPIBoolean); CDECL;
var
pReactor: TReactorObj;
+ prevVal: Integer;
begin
if not _activeObj(DSSPrime, pReactor) then
Exit;
+ prevVal := ord(pReactor.Connection);
if Value then
pReactor.Connection := TReactorConnection.Delta
else
pReactor.Connection := TReactorConnection.Wye;
-
- ReactorPropSideEffects(DSSPrime, ReactorProps.conn, pReactor);
+
+ pReactor.PropertySideEffects(ord(TReactorProp.conn), prevVal);
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_Parallel(Value: TAPIBoolean); CDECL;
var
pReactor: TReactorObj;
+ prevVal: Integer;
begin
if not _activeObj(DSSPrime, pReactor) then
Exit;
+
+ prevVal := Integer(pReactor.IsParallel);
pReactor.IsParallel := Value;
- ReactorPropSideEffects(DSSPrime, ReactorProps.Parallel, pReactor);
+ pReactor.PropertySideEffects(ord(TReactorProp.Parallel), prevVal);
end;
//------------------------------------------------------------------------------
procedure _ReactorSetbus1(pReactor: TReactorObj; const s: String);
@@ -441,7 +369,7 @@ procedure Reactors_Set_Bus1(const Value: PAnsiChar); CDECL;
if not _activeObj(DSSPrime, pReactor) then
Exit;
_ReactorSetbus1(pReactor, Value);
- ReactorPropSideEffects(DSSPrime, ReactorProps.bus1, pReactor);
+ pReactor.PropertySideEffects(ord(TReactorProp.bus1));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_Bus2(const Value: PAnsiChar); CDECL;
@@ -451,7 +379,7 @@ procedure Reactors_Set_Bus2(const Value: PAnsiChar); CDECL;
if not _activeObj(DSSPrime, pReactor) then
Exit;
pReactor.SetBus(2, Value);
- ReactorPropSideEffects(DSSPrime, ReactorProps.bus2, pReactor);
+ pReactor.PropertySideEffects(ord(TReactorProp.bus2));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_LCurve(const Value: PAnsiChar); CDECL;
@@ -460,8 +388,9 @@ procedure Reactors_Set_LCurve(const Value: PAnsiChar); CDECL;
begin
if not _activeObj(DSSPrime, pReactor) then
Exit;
- pReactor.LCurve := Value;
- ReactorPropSideEffects(DSSPrime, ReactorProps.LCurve, pReactor);
+
+ pReactor.LCurveObj := DSSPrime.XYCurveClass.Find(Value);
+ pReactor.PropertySideEffects(ord(TReactorProp.LCurve));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_RCurve(const Value: PAnsiChar); CDECL;
@@ -470,8 +399,9 @@ procedure Reactors_Set_RCurve(const Value: PAnsiChar); CDECL;
begin
if not _activeObj(DSSPrime, pReactor) then
Exit;
- pReactor.RCurve := Value;
- ReactorPropSideEffects(DSSPrime, ReactorProps.RCurve, pReactor);
+
+ pReactor.RCurveObj := DSSPrime.XYCurveClass.Find(Value);
+ pReactor.PropertySideEffects(ord(TReactorProp.RCurve));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_kV(Value: Double); CDECL;
@@ -481,7 +411,7 @@ procedure Reactors_Set_kV(Value: Double); CDECL;
if not _activeObj(DSSPrime, pReactor) then
Exit;
pReactor.kvrating := Value;
- ReactorPropSideEffects(DSSPrime, ReactorProps.kv, pReactor);
+ pReactor.PropertySideEffects(ord(TReactorProp.kv));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_kvar(Value: Double); CDECL;
@@ -491,7 +421,7 @@ procedure Reactors_Set_kvar(Value: Double); CDECL;
if not _activeObj(DSSPrime, pReactor) then
Exit;
pReactor.kvarRating := Value;
- ReactorPropSideEffects(DSSPrime, ReactorProps.kvar, pReactor);
+ pReactor.PropertySideEffects(ord(TReactorProp.kvar));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_LmH(Value: Double); CDECL;
@@ -501,19 +431,26 @@ procedure Reactors_Set_LmH(Value: Double); CDECL;
if not _activeObj(DSSPrime, pReactor) then
Exit;
pReactor.L := Value / 1000.0;
- ReactorPropSideEffects(DSSPrime, ReactorProps.LmH, pReactor);
+ pReactor.PropertySideEffects(ord(TReactorProp.LmH));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_Phases(Value: Integer); CDECL;
var
- pReactor: TReactorObj;
+ elem: TReactorObj;
+ prevVal: Integer;
begin
- if not _activeObj(DSSPrime, pReactor) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- if Value = pReactor.NPhases then
+ if Value < 1 then
+ begin
+ DoSimpleMsg(DSSPrime, '%s: Number of phases must be a positive integer!', [elem.FullName], 6568);
+ Exit;
+ end;
+ if Value = elem.NPhases then
Exit;
- pReactor.NPhases := Value;
- ReactorPropSideEffects(DSSPrime, ReactorProps.phases, pReactor);
+ prevVal := elem.FNPhases;
+ elem.FNPhases := Value;
+ elem.PropertySideEffects(ord(TReactorProp.phases), prevVal);
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_R(Value: Double); CDECL;
@@ -523,7 +460,7 @@ procedure Reactors_Set_R(Value: Double); CDECL;
if not _activeObj(DSSPrime, pReactor) then
Exit;
pReactor.R := Value;
- ReactorPropSideEffects(DSSPrime, ReactorProps.R, pReactor);
+ pReactor.PropertySideEffects(ord(TReactorProp.R));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_X(Value: Double); CDECL;
@@ -533,7 +470,7 @@ procedure Reactors_Set_X(Value: Double); CDECL;
if not _activeObj(DSSPrime, pReactor) then
Exit;
pReactor.X := Value;
- ReactorPropSideEffects(DSSPrime, ReactorProps.X, pReactor);
+ pReactor.PropertySideEffects(ord(TReactorProp.X));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_Rp(Value: Double); CDECL;
@@ -543,7 +480,7 @@ procedure Reactors_Set_Rp(Value: Double); CDECL;
if not _activeObj(DSSPrime, pReactor) then
Exit;
pReactor.Rp := Value;
- ReactorPropSideEffects(DSSPrime, ReactorProps.Rp, pReactor);
+ pReactor.PropertySideEffects(ord(TReactorProp.Rp));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_Rmatrix(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
@@ -557,15 +494,12 @@ procedure Reactors_Set_Rmatrix(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSSPrime, Format('The number of values provided (%d) does not match the expected (%d).', [ValueCount, Sqr(pReactor.Nphases)]), 5024);
+ DoSimpleMsg(DSSPrime, 'The number of values provided (%d) does not match the expected (%d).', [ValueCount, Sqr(pReactor.Nphases)], 5024);
end;
Exit;
end;
- with pReactor do
- begin
- Move(ValuePtr^, Rmatrix[1], ValueCount * SizeOf(Double));
- ReactorPropSideEffects(DSSPrime, ReactorProps.Rmatrix, pReactor);
- end;
+ Move(ValuePtr^, pReactor.Rmatrix[1], ValueCount * SizeOf(Double));
+ pReactor.PropertySideEffects(ord(TReactorProp.Rmatrix));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_Xmatrix(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
@@ -578,15 +512,12 @@ procedure Reactors_Set_Xmatrix(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSSPrime, Format('The number of values provided (%d) does not match the expected (%d).', [ValueCount, Sqr(pReactor.Nphases)]), 5024);
+ DoSimpleMsg(DSSPrime, 'The number of values provided (%d) does not match the expected (%d).', [ValueCount, Sqr(pReactor.Nphases)], 5024);
end;
Exit;
end;
- with pReactor do
- begin
- Move(ValuePtr^, Xmatrix[1], ValueCount * SizeOf(Double));
- ReactorPropSideEffects(DSSPrime, ReactorProps.Xmatrix, pReactor);
- end;
+ Move(ValuePtr^, pReactor.Xmatrix[1], ValueCount * SizeOf(Double));
+ pReactor.PropertySideEffects(ord(TReactorProp.Xmatrix));
end;
//------------------------------------------------------------------------------
procedure Reactors_Get_Rmatrix(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
@@ -600,11 +531,8 @@ procedure Reactors_Get_Rmatrix(var ResultPtr: PDouble; ResultCount: PAPISize); C
if pReactor.Rmatrix = NIL then
Exit; //TODO: would an error here be useful?
- with pReactor do
- begin
- DSS_RecreateArray_PDouble(ResultPtr, ResultCount, Sqr(Nphases));
- Move(Rmatrix[1], ResultPtr^, ResultCount^ * SizeOf(Double));
- end;
+ DSS_RecreateArray_PDouble(ResultPtr, ResultCount, Sqr(pReactor.Nphases));
+ Move(pReactor.Rmatrix[1], ResultPtr^, ResultCount^ * SizeOf(Double));
end;
procedure Reactors_Get_Rmatrix_GR(); CDECL;
@@ -625,11 +553,8 @@ procedure Reactors_Get_Xmatrix(var ResultPtr: PDouble; ResultCount: PAPISize); C
if pReactor.Xmatrix = NIL then
Exit;
- with pReactor do
- begin
- DSS_RecreateArray_PDouble(ResultPtr, ResultCount, Sqr(Nphases));
- Move(Xmatrix[1], ResultPtr^, ResultCount^ * SizeOf(Double));
- end;
+ DSS_RecreateArray_PDouble(ResultPtr, ResultCount, Sqr(pReactor.Nphases));
+ Move(pReactor.Xmatrix[1], ResultPtr^, ResultCount^ * SizeOf(Double));
end;
procedure Reactors_Get_Xmatrix_GR(); CDECL;
@@ -647,12 +572,10 @@ procedure Reactors_Get_Z1(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
DefaultResult(ResultPtr, ResultCount);
if not _activeObj(DSSPrime, pReactor) then
Exit;
- with pReactor do
- begin
- Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2);
- Result[0] := Z1.Re;
- Result[1] := Z1.Im;
- end;
+
+ Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2);
+ Result[0] := pReactor.Z1.Re;
+ Result[1] := pReactor.Z1.Im;
end;
procedure Reactors_Get_Z1_GR(); CDECL;
@@ -670,12 +593,10 @@ procedure Reactors_Get_Z2(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
DefaultResult(ResultPtr, ResultCount);
if not _activeObj(DSSPrime, pReactor) then
Exit;
- with pReactor do
- begin
- Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2);
- Result[0] := Z2.Re;
- Result[1] := Z2.Im;
- end;
+
+ Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2);
+ Result[0] := pReactor.Z2.Re;
+ Result[1] := pReactor.Z2.Im;
end;
procedure Reactors_Get_Z2_GR(); CDECL;
@@ -693,12 +614,10 @@ procedure Reactors_Get_Z0(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
DefaultResult(ResultPtr, ResultCount);
if not _activeObj(DSSPrime, pReactor) then
Exit;
- with pReactor do
- begin
- Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2);
- Result[0] := Z0.Re;
- Result[1] := Z0.Im;
- end;
+
+ Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2);
+ Result[0] := pReactor.Z0.Re;
+ Result[1] := pReactor.Z0.Im;
end;
procedure Reactors_Get_Z0_GR(); CDECL;
@@ -716,12 +635,10 @@ procedure Reactors_Get_Z(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
DefaultResult(ResultPtr, ResultCount);
if not _activeObj(DSSPrime, pReactor) then
Exit;
- with pReactor do
- begin
- Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2);
- Result[0] := R;
- Result[1] := X;
- end;
+
+ Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2);
+ Result[0] := pReactor.R;
+ Result[1] := pReactor.X;
end;
procedure Reactors_Get_Z_GR(); CDECL;
@@ -743,11 +660,8 @@ procedure Reactors_Set_Z2(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
if (ValueCount <> 2) then
Exit;
- with pReactor do
- begin
- Z2 := Cmplx(Value[0], Value[1]);
- ReactorPropSideEffects(DSSPrime, ReactorProps.Z2, pReactor);
- end;
+ pReactor.Z2 := Cmplx(Value[0], Value[1]);
+ pReactor.PropertySideEffects(ord(TReactorProp.Z2));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_Z1(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
@@ -762,11 +676,8 @@ procedure Reactors_Set_Z1(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
if (ValueCount <> 2) then
Exit;
- with pReactor do
- begin
- Z1 := Cmplx(Value[0], Value[1]);
- ReactorPropSideEffects(DSSPrime, ReactorProps.Z1, pReactor);
- end;
+ pReactor.Z1 := Cmplx(Value[0], Value[1]);
+ pReactor.PropertySideEffects(ord(TReactorProp.Z1));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_Z0(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
@@ -781,11 +692,8 @@ procedure Reactors_Set_Z0(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
if (ValueCount <> 2) then
Exit;
- with pReactor do
- begin
- Z0 := Cmplx(Value[0], Value[1]);
- ReactorPropSideEffects(DSSPrime, ReactorProps.Z0, pReactor);
- end;
+ pReactor.Z0 := Cmplx(Value[0], Value[1]);
+ pReactor.PropertySideEffects(ord(TReactorProp.Z0));
end;
//------------------------------------------------------------------------------
procedure Reactors_Set_Z(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
@@ -800,11 +708,8 @@ procedure Reactors_Set_Z(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
if (ValueCount <> 2) then
Exit;
- with pReactor do
- begin
- Z := Cmplx(Value[0], Value[1]);
- ReactorPropSideEffects(DSSPrime, ReactorProps.Z, pReactor);
- end;
+ pReactor.Z := Cmplx(Value[0], Value[1]);
+ pReactor.PropertySideEffects(ord(TReactorProp.Z));
end;
//------------------------------------------------------------------------------
function Reactors_Get_idx(): Integer; CDECL;
@@ -824,7 +729,7 @@ procedure Reactors_Set_idx(Value: Integer); CDECL;
pReactor := DSSPrime.ActiveCircuit.Reactors.Get(Value);
if pReactor = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Reactor index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['Reactor', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pReactor;
diff --git a/src/CAPI/CAPI_Reclosers.pas b/src/CAPI/CAPI_Reclosers.pas
index e432f1563..603029e59 100644
--- a/src/CAPI/CAPI_Reclosers.pas
+++ b/src/CAPI/CAPI_Reclosers.pas
@@ -57,10 +57,14 @@ implementation
DSSGlobals,
DSSClassDefs,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ DSSObjectHelper;
+
+type
+ TObj = TRecloserObj;
//------------------------------------------------------------------------------
-function _activeObj(DSS: TDSSContext; out obj: TRecloserObj): Boolean; inline;
+function _activeObj(DSS: TDSSContext; out obj: TObj): Boolean; inline;
begin
Result := False;
obj := NIL;
@@ -72,7 +76,7 @@ function _activeObj(DSS: TDSSContext; out obj: TRecloserObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active Recloser object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['Recloser'], 8989);
end;
Exit;
end;
@@ -80,17 +84,34 @@ function _activeObj(DSS: TDSSContext; out obj: TRecloserObj): Boolean; inline;
Result := True;
end;
//------------------------------------------------------------------------------
-procedure Set_Parameter(DSS: TDSSContext; const parm: String; const val: String);
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: String); overload;
var
- cmd: String;
- elem: TRecloserObj;
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.ParsePropertyValue(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Double); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.SetDouble(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Integer); overload;
+var
+ elem: TObj;
begin
if not _activeObj(DSS, elem) then
Exit;
-
DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
- cmd := Format('recloser.%s.%s=%s', [elem.Name, parm, val]);
- DSS.DSSExecutive.Command := cmd;
+ elem.SetInteger(idx, val);
end;
//------------------------------------------------------------------------------
procedure Reclosers_Get_AllNames(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
@@ -134,7 +155,7 @@ function Reclosers_Get_Next(): Integer; CDECL;
//------------------------------------------------------------------------------
function Reclosers_Get_Name(): PAnsiChar; CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
@@ -154,13 +175,13 @@ procedure Reclosers_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'Recloser "' + Value + '" Not Found in Active Circuit.', 77003);
+ DoSimpleMsg(DSSPrime, 'Recloser "%s" not found in Active Circuit.', [Value], 77003);
end;
end;
//------------------------------------------------------------------------------
function Reclosers_Get_MonitoredTerm(): Integer; CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -170,37 +191,39 @@ function Reclosers_Get_MonitoredTerm(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Reclosers_Set_MonitoredTerm(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'monitoredterm', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRecloserProp.monitoredterm), Value);
end;
//------------------------------------------------------------------------------
function Reclosers_Get_SwitchedObj(): PAnsiChar; CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.ElementName);
+ if elem.ControlledElement <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(elem.ControlledElement.FullName));
end;
//------------------------------------------------------------------------------
procedure Reclosers_Set_SwitchedObj(const Value: PAnsiChar); CDECL;
begin
- Set_Parameter(DSSPrime, 'SwitchedObj', Value);
+ Set_Parameter(DSSPrime, ord(TRecloserProp.SwitchedObj), Value);
end;
//------------------------------------------------------------------------------
function Reclosers_Get_MonitoredObj(): PAnsiChar; CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.MonitoredElementName);
+ if elem.MonitoredElement <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(elem.MonitoredElement.FullName));
end;
//------------------------------------------------------------------------------
function Reclosers_Get_SwitchedTerm(): Integer; CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -210,17 +233,17 @@ function Reclosers_Get_SwitchedTerm(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Reclosers_Set_MonitoredObj(const Value: PAnsiChar); CDECL;
begin
- Set_Parameter(DSSPrime, 'monitoredObj', Value);
+ Set_Parameter(DSSPrime, ord(TRecloserProp.monitoredObj), Value);
end;
//------------------------------------------------------------------------------
procedure Reclosers_Set_SwitchedTerm(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'SwitchedTerm', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRecloserProp.SwitchedTerm), Value);
end;
//------------------------------------------------------------------------------
function Reclosers_Get_NumFast(): Integer; CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -232,7 +255,7 @@ procedure Reclosers_Get_RecloseIntervals(var ResultPtr: PDouble; ResultCount: PA
// return reclose intervals in seconds
var
Result: PDoubleArray0;
- elem: TRecloserObj;
+ elem: TObj;
i, k: Integer;
begin
if not _activeObj(DSSPrime, elem) then
@@ -259,7 +282,7 @@ procedure Reclosers_Get_RecloseIntervals_GR(); CDECL;
//------------------------------------------------------------------------------
function Reclosers_Get_Shots(): Integer; CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -269,17 +292,17 @@ function Reclosers_Get_Shots(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Reclosers_Set_NumFast(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'numfast', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRecloserProp.numfast), Value);
end;
//------------------------------------------------------------------------------
procedure Reclosers_Set_Shots(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'shots', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRecloserProp.shots), Value);
end;
//------------------------------------------------------------------------------
function Reclosers_Get_PhaseTrip(): Double; CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -289,12 +312,12 @@ function Reclosers_Get_PhaseTrip(): Double; CDECL;
//------------------------------------------------------------------------------
procedure Reclosers_Set_PhaseTrip(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'PhaseTrip', Format('%.g', [Value]));
+ Set_Parameter(DSSPrime, ord(TRecloserProp.PhaseTrip), Value);
end;
//------------------------------------------------------------------------------
function Reclosers_Get_GroundInst(): Double; CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -304,7 +327,7 @@ function Reclosers_Get_GroundInst(): Double; CDECL;
//------------------------------------------------------------------------------
function Reclosers_Get_GroundTrip(): Double; CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -314,7 +337,7 @@ function Reclosers_Get_GroundTrip(): Double; CDECL;
//------------------------------------------------------------------------------
function Reclosers_Get_PhaseInst(): Double; CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -324,27 +347,27 @@ function Reclosers_Get_PhaseInst(): Double; CDECL;
//------------------------------------------------------------------------------
procedure Reclosers_Set_GroundInst(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'GroundInst', Format('%.g', [Value]));
+ Set_Parameter(DSSPrime, ord(TRecloserProp.GroundInst), Value);
end;
//------------------------------------------------------------------------------
procedure Reclosers_Set_GroundTrip(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'GroundTrip', Format('%.g', [Value]));
+ Set_Parameter(DSSPrime, ord(TRecloserProp.GroundTrip), Value);
end;
//------------------------------------------------------------------------------
procedure Reclosers_Set_PhaseInst(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Phaseinst', Format('%.g', [Value]));
+ Set_Parameter(DSSPrime, ord(TRecloserProp.PhaseInst), Value);
end;
//------------------------------------------------------------------------------
procedure Reclosers_Close(); CDECL;
begin
- Set_Parameter(DSSPrime, 'Action', 'close');
+ Set_Parameter(DSSPrime, ord(TRecloserProp.Action), 'close');
end;
//------------------------------------------------------------------------------
procedure Reclosers_Open(); CDECL;
begin
- Set_Parameter(DSSPrime, 'Action', 'open');
+ Set_Parameter(DSSPrime, ord(TRecloserProp.Action), 'open');
end;
//------------------------------------------------------------------------------
function Reclosers_Get_idx(): Integer; CDECL;
@@ -357,14 +380,14 @@ function Reclosers_Get_idx(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Reclosers_Set_idx(Value: Integer); CDECL;
var
- pRecloser: TRecloserObj;
+ pRecloser: TObj;
begin
if InvalidCircuit(DSSPrime) then
Exit;
pRecloser := DSSPrime.ActiveCircuit.Reclosers.Get(Value);
if pRecloser = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Recloser index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['Recloser', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pRecloser;
@@ -372,7 +395,7 @@ procedure Reclosers_Set_idx(Value: Integer); CDECL;
//------------------------------------------------------------------------------
procedure Reclosers_Reset(); CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -381,7 +404,7 @@ procedure Reclosers_Reset(); CDECL;
//------------------------------------------------------------------------------
function Reclosers_Get_NormalState(): Integer; CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -391,23 +414,29 @@ function Reclosers_Get_NormalState(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Reclosers_Set_NormalState(Value: Integer); CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
if Value = dssActionOpen then //TODO: use the same enum
- elem.NormalState := CTRL_OPEN
+ begin
+ elem.NormalState := CTRL_OPEN;
+ elem.NormalStateSet := True;
+ end
else if Value = dssActionClose then
- elem.NormalState := CTRL_CLOSE
+ begin
+ elem.NormalState := CTRL_CLOSE;
+ elem.NormalStateSet := True;
+ end
else
begin
- DoSimpleMsg(DSSPrime, 'Invalid Recloser normal state: "' + IntToStr(Value) + '".', 656566);
+ DoSimpleMsg(DSSPrime, 'Invalid Recloser normal state: "%d".', [Value], 656566);
end;
end;
//------------------------------------------------------------------------------
function Reclosers_Get_State(): Integer; CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -417,7 +446,7 @@ function Reclosers_Get_State(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Reclosers_Set_State(Value: Integer); CDECL;
var
- elem: TRecloserObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -427,7 +456,7 @@ procedure Reclosers_Set_State(Value: Integer); CDECL;
elem.PresentState := CTRL_CLOSE
else
begin
- DoSimpleMsg(DSSPrime, 'Invalid Recloser state: "' + IntToStr(Value) + '".', 656567);
+ DoSimpleMsg(DSSPrime, 'Invalid Recloser state: "%d".', [Value], 656567);
end;
end;
//------------------------------------------------------------------------------
diff --git a/src/CAPI/CAPI_RegControls.pas b/src/CAPI/CAPI_RegControls.pas
index 882494bb0..eb5da9a18 100644
--- a/src/CAPI/CAPI_RegControls.pas
+++ b/src/CAPI/CAPI_RegControls.pas
@@ -72,10 +72,14 @@ implementation
SysUtils,
DSSPointerList,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ DSSObjectHelper;
+
+type
+ TObj = TRegControlObj;
//------------------------------------------------------------------------------
-function _activeObj(DSS: TDSSContext; out obj: TRegControlObj): Boolean; inline;
+function _activeObj(DSS: TDSSContext; out obj: TObj): Boolean; inline;
begin
Result := False;
obj := NIL;
@@ -87,7 +91,7 @@ function _activeObj(DSS: TDSSContext; out obj: TRegControlObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active RegControl object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['RegControl'], 8989);
end;
Exit;
end;
@@ -95,16 +99,34 @@ function _activeObj(DSS: TDSSContext; out obj: TRegControlObj): Boolean; inline;
Result := True;
end;
//------------------------------------------------------------------------------
-procedure Set_Parameter(DSS: TDSSContext; const parm: String; const val: String);
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: String); overload;
var
- cmd: String;
- elem: TRegControlObj;
+ elem: TObj;
begin
if not _activeObj(DSS, elem) then
Exit;
DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
- cmd := Format('regcontrol.%s.%s=%s', [elem.Name, parm, val]);
- DSS.DSSExecutive.Command := cmd;
+ elem.ParsePropertyValue(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Double); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.SetDouble(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Integer); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.SetInteger(idx, val);
end;
//------------------------------------------------------------------------------
procedure RegControls_Get_AllNames(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
@@ -126,22 +148,22 @@ procedure RegControls_Get_AllNames_GR(); CDECL;
//------------------------------------------------------------------------------
function RegControls_Get_CTPrimary(): Double; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.CT;
+ Result := elem.CTRating;
end;
//------------------------------------------------------------------------------
function RegControls_Get_Delay(): Double; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.InitialDelay;
+ Result := elem.TimeDelay;
end;
//------------------------------------------------------------------------------
function RegControls_Get_First(): Integer; CDECL;
@@ -162,87 +184,87 @@ function RegControls_Get_Next(): Integer; CDECL;
//------------------------------------------------------------------------------
function RegControls_Get_ForwardBand(): Double; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.BandVoltage;
+ Result := elem.Bandwidth;
end;
//------------------------------------------------------------------------------
function RegControls_Get_ForwardR(): Double; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.LineDropR;
+ Result := elem.R;
end;
//------------------------------------------------------------------------------
function RegControls_Get_ForwardVreg(): Double; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.TargetVoltage;
+ Result := elem.Vreg;
end;
//------------------------------------------------------------------------------
function RegControls_Get_ForwardX(): Double; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.LineDropX;
+ Result := elem.X;
end;
//------------------------------------------------------------------------------
function RegControls_Get_IsInverseTime(): TAPIBoolean; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := FALSE;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.IsInverseTime;
+ Result := elem.InverseTime;
end;
//------------------------------------------------------------------------------
function RegControls_Get_IsReversible(): TAPIBoolean; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := FALSE;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.UseReverseDrop;
+ Result := elem.IsReversible;
end;
//------------------------------------------------------------------------------
function RegControls_Get_MaxTapChange(): Integer; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.MaxTapChange;
+ Result := elem.TapLimitPerChange;
end;
//------------------------------------------------------------------------------
function RegControls_Get_MonitoredBus(): PAnsiChar; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.ControlledBusName);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.RegulatedBus);
end;
//------------------------------------------------------------------------------
function RegControls_Get_Name(): PAnsiChar; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
@@ -252,67 +274,67 @@ function RegControls_Get_Name(): PAnsiChar; CDECL;
//------------------------------------------------------------------------------
function RegControls_Get_PTratio(): Double; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.PT;
+ Result := elem.PTRatio;
end;
//------------------------------------------------------------------------------
function RegControls_Get_ReverseBand(): Double; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.RevBandVoltage;
+ Result := elem.revBandwidth;
end;
//------------------------------------------------------------------------------
function RegControls_Get_ReverseR(): Double; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.RevLineDropR;
+ Result := elem.revR;
end;
//------------------------------------------------------------------------------
function RegControls_Get_ReverseVreg(): Double; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.RevTargetVoltage;
+ Result := elem.revVreg;
end;
//------------------------------------------------------------------------------
function RegControls_Get_ReverseX(): Double; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.RevLineDropX;
+ Result := elem.revX;
end;
//------------------------------------------------------------------------------
function RegControls_Get_TapDelay(): Double; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.SubsequentDelay;
+ Result := elem.TapDelay;
end;
//------------------------------------------------------------------------------
function RegControls_Get_TapWinding(): Integer; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -322,7 +344,7 @@ function RegControls_Get_TapWinding(): Integer; CDECL;
//------------------------------------------------------------------------------
function RegControls_Get_Transformer(): PAnsiChar; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
@@ -332,17 +354,17 @@ function RegControls_Get_Transformer(): PAnsiChar; CDECL;
//------------------------------------------------------------------------------
function RegControls_Get_VoltageLimit(): Double; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.VoltageLimit;
+ Result := elem.Vlimit;
end;
//------------------------------------------------------------------------------
function RegControls_Get_Winding(): Integer; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -352,7 +374,7 @@ function RegControls_Get_Winding(): Integer; CDECL;
//------------------------------------------------------------------------------
function RegControls_Get_TapNumber(): Integer; CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -362,58 +384,52 @@ function RegControls_Get_TapNumber(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure RegControls_Set_CTPrimary(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'CTprim', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.CTprim), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_Delay(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Delay', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.Delay), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_ForwardBand(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Band', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.Band), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_ForwardR(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'R', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.R), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_ForwardVreg(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Vreg', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.Vreg), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_ForwardX(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'X', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.X), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_IsInverseTime(Value: TAPIBoolean); CDECL;
begin
- if Value = TRUE then
- Set_Parameter(DSSPrime, 'InverseTime', 'y')
- else
- Set_Parameter(DSSPrime, 'InverseTime', 'n');
+ Set_Parameter(DSSPrime, ord(TRegControlProp.InverseTime), Integer(Value));
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_IsReversible(Value: TAPIBoolean); CDECL;
begin
- if Value = TRUE then
- Set_Parameter(DSSPrime, 'Reversible', 'y')
- else
- Set_Parameter(DSSPrime, 'Reversible', 'n');
+ Set_Parameter(DSSPrime, ord(TRegControlProp.Reversible), Integer(Value));
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_MaxTapChange(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'MaxTapChange', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.MaxTapChange), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_MonitoredBus(const Value: PAnsiChar); CDECL;
begin
- Set_Parameter(DSSPrime, 'Bus', Value);
+ Set_Parameter(DSSPrime, ord(TRegControlProp.Bus), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_Name(const Value: PAnsiChar); CDECL;
@@ -427,63 +443,63 @@ procedure RegControls_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'RegControl "' + Value + '" Not Found in Active Circuit.', 5003);
+ DoSimpleMsg(DSSPrime, 'RegControl "%s" not found in Active Circuit.', [Value], 5003);
end;
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_PTratio(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'PTratio', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.PTratio), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_ReverseBand(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'RevBand', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.RevBand), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_ReverseR(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'RevR', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.RevR), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_ReverseVreg(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'RevVreg', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.RevVreg), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_ReverseX(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'RevX', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.RevX), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_TapDelay(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'TapDelay', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.TapDelay), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_TapWinding(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'TapWinding', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.TapWinding), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_Transformer(const Value: PAnsiChar); CDECL;
begin
- Set_Parameter(DSSPrime, 'Transformer', Value);
+ Set_Parameter(DSSPrime, ord(TRegControlProp.Transformer), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_VoltageLimit(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Vlimit', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.Vlimit), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_Winding(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'Winding', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.Winding), Value);
end;
//------------------------------------------------------------------------------
procedure RegControls_Set_TapNumber(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'TapNum', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRegControlProp.TapNum), Value);
end;
//------------------------------------------------------------------------------
function RegControls_Get_Count(): Integer; CDECL;
@@ -496,7 +512,7 @@ function RegControls_Get_Count(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure RegControls_Reset(); CDECL;
var
- elem: TRegControlObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -513,14 +529,14 @@ function RegControls_Get_idx(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure RegControls_Set_idx(Value: Integer); CDECL;
var
- pRegControl: TRegControlObj;
+ pRegControl: TObj;
begin
if InvalidCircuit(DSSPrime) then
Exit;
pRegControl := DSSPrime.ActiveCircuit.RegControls.Get(Value);
if pRegControl = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid RegControl index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['RegControl', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pRegControl;
diff --git a/src/CAPI/CAPI_Relays.pas b/src/CAPI/CAPI_Relays.pas
index c3c7379e4..d2c95cb2d 100644
--- a/src/CAPI/CAPI_Relays.pas
+++ b/src/CAPI/CAPI_Relays.pas
@@ -43,10 +43,14 @@ implementation
Sysutils,
DSSPointerList,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ DSSObjectHelper;
+
+type
+ TObj = TRelayObj;
//------------------------------------------------------------------------------
-function _activeObj(DSS: TDSSContext; out obj: TRelayObj): Boolean; inline;
+function _activeObj(DSS: TDSSContext; out obj: TObj): Boolean; inline;
begin
Result := False;
obj := NIL;
@@ -58,7 +62,7 @@ function _activeObj(DSS: TDSSContext; out obj: TRelayObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active Relay object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['Relay'], 8989);
end;
Exit;
end;
@@ -66,16 +70,34 @@ function _activeObj(DSS: TDSSContext; out obj: TRelayObj): Boolean; inline;
Result := True;
end;
//------------------------------------------------------------------------------
-procedure Set_Parameter(DSS: TDSSContext; const parm: String; const val: String);
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: String); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.ParsePropertyValue(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Double); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.SetDouble(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Integer); overload;
var
- cmd: String;
- elem: TRelayObj;
+ elem: TObj;
begin
if not _activeObj(DSS, elem) then
Exit;
DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
- cmd := Format('Relay.%s.%s=%s', [elem.Name, parm, val]);
- DSS.DSSExecutive.Command := cmd;
+ elem.SetInteger(idx, val);
end;
//------------------------------------------------------------------------------
procedure Relays_Get_AllNames(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
@@ -119,7 +141,7 @@ function Relays_Get_Next(): Integer; CDECL;
//------------------------------------------------------------------------------
function Relays_Get_Name(): PAnsiChar; CDECL;
var
- elem: TRelayObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
@@ -129,7 +151,6 @@ function Relays_Get_Name(): PAnsiChar; CDECL;
//------------------------------------------------------------------------------
procedure Relays_Set_Name(const Value: PAnsiChar); CDECL;
// Set element active by name
-
begin
if InvalidCircuit(DSSPrime) then
Exit;
@@ -140,29 +161,30 @@ procedure Relays_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'Relay "' + Value + '" Not Found in Active Circuit.', 77003);
+ DoSimpleMsg(DSSPrime, 'Relay "%s" not found in Active Circuit.', [Value], 77003);
end;
end;
//------------------------------------------------------------------------------
function Relays_Get_MonitoredObj(): PAnsiChar; CDECL;
var
- elem: TRelayObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.MonitoredElementName);
+ if elem.MonitoredElement <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(elem.MonitoredElement.FullName));
end;
//------------------------------------------------------------------------------
procedure Relays_Set_MonitoredObj(const Value: PAnsiChar); CDECL;
begin
- Set_Parameter(DSSPrime, 'monitoredObj', Value);
+ Set_Parameter(DSSPrime, ord(TRelayProp.monitoredObj), Value);
end;
//------------------------------------------------------------------------------
function Relays_Get_MonitoredTerm(): Integer; CDECL;
var
- elem: TRelayObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -172,28 +194,29 @@ function Relays_Get_MonitoredTerm(): Integer; CDECL;
//------------------------------------------------------------------------------
function Relays_Get_SwitchedObj(): PAnsiChar; CDECL;
var
- elem: TRelayObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.ElementName);
+ if elem.ControlledElement <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(elem.ControlledElement.FullName));
end;
//------------------------------------------------------------------------------
procedure Relays_Set_MonitoredTerm(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'monitoredterm', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRelayProp.monitoredterm), Value);
end;
//------------------------------------------------------------------------------
procedure Relays_Set_SwitchedObj(const Value: PAnsiChar); CDECL;
begin
- Set_Parameter(DSSPrime, 'SwitchedObj', Value);
+ Set_Parameter(DSSPrime, ord(TRelayProp.SwitchedObj), Value);
end;
//------------------------------------------------------------------------------
function Relays_Get_SwitchedTerm(): Integer; CDECL;
var
- elem: TRelayObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -203,7 +226,7 @@ function Relays_Get_SwitchedTerm(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Relays_Set_SwitchedTerm(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'SwitchedTerm', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TRelayProp.SwitchedTerm), Value);
end;
//------------------------------------------------------------------------------
function Relays_Get_idx(): Integer; CDECL;
@@ -216,21 +239,21 @@ function Relays_Get_idx(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Relays_Set_idx(Value: Integer); CDECL;
var
- pRelay: TRelayObj;
+ pRelay: TObj;
begin
if InvalidCircuit(DSSPrime) then
Exit;
pRelay := DSSPrime.ActiveCircuit.Relays.Get(Value);
if pRelay = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Relay index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['Relay', Value], 656565);
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pRelay;
end;
//------------------------------------------------------------------------------
procedure Relays_Close(); CDECL;
var
- elem: TRelayObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -239,7 +262,7 @@ procedure Relays_Close(); CDECL;
//------------------------------------------------------------------------------
procedure Relays_Open(); CDECL;
var
- elem: TRelayObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -248,7 +271,7 @@ procedure Relays_Open(); CDECL;
//------------------------------------------------------------------------------
procedure Relays_Reset(); CDECL;
var
- elem: TRelayObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -257,7 +280,7 @@ procedure Relays_Reset(); CDECL;
//------------------------------------------------------------------------------
function Relays_Get_NormalState(): Integer; CDECL;
var
- elem: TRelayObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -267,23 +290,29 @@ function Relays_Get_NormalState(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Relays_Set_NormalState(Value: Integer); CDECL;
var
- elem: TRelayObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
if Value = dssActionOpen then
- elem.NormalState := CTRL_OPEN
+ begin
+ elem.NormalState := CTRL_OPEN;
+ elem.NormalStateSet := True;
+ end
else if Value = dssActionClose then
- elem.NormalState := CTRL_CLOSE
+ begin
+ elem.NormalState := CTRL_CLOSE;
+ elem.NormalStateSet := True;
+ end
else
begin
- DoSimpleMsg(DSSPrime, 'Invalid Relay normal state: "' + IntToStr(Value) + '".', 656569);
+ DoSimpleMsg(DSSPrime, 'Invalid Relay normal state: "%d".', [Value], 656569);
end;
end;
//------------------------------------------------------------------------------
function Relays_Get_State(): Integer; CDECL;
var
- elem: TRelayObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -293,7 +322,7 @@ function Relays_Get_State(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Relays_Set_State(Value: Integer); CDECL;
var
- elem: TRelayObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -303,7 +332,7 @@ procedure Relays_Set_State(Value: Integer); CDECL;
elem.PresentState := CTRL_CLOSE
else
begin
- DoSimpleMsg(DSSPrime, 'Invalid Relay state: "' + IntToStr(Value) + '".', 656568);
+ DoSimpleMsg(DSSPrime, 'Invalid Relay state: "%d".', [Value], 656568);
end;
end;
//------------------------------------------------------------------------------
diff --git a/src/CAPI/CAPI_Sensors.pas b/src/CAPI/CAPI_Sensors.pas
index 83a7243ba..016b8ec3f 100644
--- a/src/CAPI/CAPI_Sensors.pas
+++ b/src/CAPI/CAPI_Sensors.pas
@@ -59,10 +59,14 @@ implementation
Executive,
SysUtils,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ DSSObjectHelper;
+
+type
+ TObj = TSensorObj;
//------------------------------------------------------------------------------
-function _activeObj(DSS: TDSSContext; out obj: TSensorObj): Boolean; inline;
+function _activeObj(DSS: TDSSContext; out obj: TObj): Boolean; inline;
begin
Result := False;
obj := NIL;
@@ -74,7 +78,7 @@ function _activeObj(DSS: TDSSContext; out obj: TSensorObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active Sensor object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['Sensor'], 8989);
end;
Exit;
end;
@@ -82,16 +86,34 @@ function _activeObj(DSS: TDSSContext; out obj: TSensorObj): Boolean; inline;
Result := True;
end;
//------------------------------------------------------------------------------
-procedure Set_Parameter(DSS: TDSSContext; const parm: String; const val: String);
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: String); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.ParsePropertyValue(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Double); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.SetDouble(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Integer); overload;
var
- cmd: String;
- elem: TSensorObj;
+ elem: TObj;
begin
if not _activeObj(DSS, elem) then
Exit;
DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
- cmd := Format('sensor.%s.%s=%s', [elem.Name, parm, val]);
- DSS.DSSExecutive.Command := cmd;
+ elem.SetInteger(idx, val);
end;
//------------------------------------------------------------------------------
procedure Sensors_Get_AllNames(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
@@ -119,7 +141,7 @@ function Sensors_Get_Count(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Sensors_Get_Currents(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
begin
@@ -156,17 +178,17 @@ function Sensors_Get_Next(): Integer; CDECL;
//------------------------------------------------------------------------------
function Sensors_Get_IsDelta(): TAPIBoolean; CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
Result := FALSE;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := (elem.Conn > 0);
+ Result := (elem.FConn > 0);
end;
//------------------------------------------------------------------------------
procedure Sensors_Get_kVARS(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
begin
@@ -186,7 +208,7 @@ procedure Sensors_Get_kVARS_GR(); CDECL;
//------------------------------------------------------------------------------
procedure Sensors_Get_kVS(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
begin
@@ -206,7 +228,7 @@ procedure Sensors_Get_kVS_GR(); CDECL;
//------------------------------------------------------------------------------
procedure Sensors_Get_kWS(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
begin
@@ -226,17 +248,18 @@ procedure Sensors_Get_kWS_GR(); CDECL;
//------------------------------------------------------------------------------
function Sensors_Get_MeteredElement(): PAnsiChar; CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.ElementName);
+ if elem.MeteredElement <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(elem.MeteredElement.FullName));
end;
//------------------------------------------------------------------------------
function Sensors_Get_MeteredTerminal(): Integer; CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -246,7 +269,7 @@ function Sensors_Get_MeteredTerminal(): Integer; CDECL;
//------------------------------------------------------------------------------
function Sensors_Get_Name(): PAnsiChar; CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
@@ -256,7 +279,7 @@ function Sensors_Get_Name(): PAnsiChar; CDECL;
//------------------------------------------------------------------------------
function Sensors_Get_PctError(): Double; CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -266,7 +289,7 @@ function Sensors_Get_PctError(): Double; CDECL;
//------------------------------------------------------------------------------
function Sensors_Get_ReverseDelta(): TAPIBoolean; CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
Result := FALSE;
if not _activeObj(DSSPrime, elem) then
@@ -276,7 +299,7 @@ function Sensors_Get_ReverseDelta(): TAPIBoolean; CDECL;
//------------------------------------------------------------------------------
function Sensors_Get_Weight(): Double; CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -286,7 +309,7 @@ function Sensors_Get_Weight(): Double; CDECL;
//------------------------------------------------------------------------------
procedure Sensors_Reset(); CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -302,14 +325,14 @@ procedure Sensors_ResetAll(); CDECL;
//------------------------------------------------------------------------------
procedure Sensors_Set_Currents(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
if ValueCount <> elem.NPhases then
begin
- DoSimpleMsg(DSSPrime, 'The provided number of values does not match the element''s number of phases.', 5023);
+ DoSimpleMsg(DSSPrime, _('The provided number of values does not match the element''s number of phases.'), 5023);
Exit;
end;
Move(ValuePtr^, elem.SensorCurrent[1], elem.NPhases * SizeOf(Double));
@@ -317,23 +340,24 @@ procedure Sensors_Set_Currents(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
//------------------------------------------------------------------------------
procedure Sensors_Set_IsDelta(Value: TAPIBoolean); CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.Conn := Integer(Value);
+ elem.FConn := Integer(Value);
+ elem.RecalcVbase;
end;
//------------------------------------------------------------------------------
procedure Sensors_Set_kVARS(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
if ValueCount <> elem.NPhases then
begin
- DoSimpleMsg(DSSPrime, 'The provided number of values does not match the element''s number of phases.', 5024);
+ DoSimpleMsg(DSSPrime, _('The provided number of values does not match the element''s number of phases.'), 5024);
Exit;
end;
Move(ValuePtr^, elem.SensorQ[1], elem.NPhases * SizeOf(Double));
@@ -341,14 +365,14 @@ procedure Sensors_Set_kVARS(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
//------------------------------------------------------------------------------
procedure Sensors_Set_kVS(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
if ValueCount <> elem.NPhases then
begin
- DoSimpleMsg(DSSPrime, 'The provided number of values does not match the element''s number of phases.', 5024);
+ DoSimpleMsg(DSSPrime, _('The provided number of values does not match the element''s number of phases.'), 5024);
Exit;
end;
Move(ValuePtr^, elem.SensorVoltage[1], elem.NPhases * SizeOf(Double));
@@ -356,14 +380,14 @@ procedure Sensors_Set_kVS(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
//------------------------------------------------------------------------------
procedure Sensors_Set_kWS(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
if ValueCount <> elem.NPhases then
begin
- DoSimpleMsg(DSSPrime, 'The provided number of values does not match the element''s number of phases.', 5024);
+ DoSimpleMsg(DSSPrime, _('The provided number of values does not match the element''s number of phases.'), 5024);
Exit;
end;
Move(ValuePtr^, elem.SensorP[1], elem.NPhases * SizeOf(Double));
@@ -371,12 +395,12 @@ procedure Sensors_Set_kWS(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
//------------------------------------------------------------------------------
procedure Sensors_Set_MeteredElement(const Value: PAnsiChar); CDECL;
begin
- Set_Parameter(DSSPrime, 'element', Value);
+ Set_Parameter(DSSPrime, ord(TSensorProp.element), Value);
end;
//------------------------------------------------------------------------------
procedure Sensors_Set_MeteredTerminal(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'terminal', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TSensorProp.terminal), Value);
end;
//------------------------------------------------------------------------------
procedure Sensors_Set_Name(const Value: PAnsiChar); CDECL;
@@ -390,31 +414,31 @@ procedure Sensors_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'Sensor "' + Value + '" Not Found in Active Circuit.', 5003);
+ DoSimpleMsg(DSSPrime, 'Sensor "%s" not found in Active Circuit.', [Value], 5003);
end;
end;
//------------------------------------------------------------------------------
procedure Sensors_Set_PctError(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, '%error', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TSensorProp.pcterror), Value);
end;
//------------------------------------------------------------------------------
procedure Sensors_Set_ReverseDelta(Value: TAPIBoolean); CDECL;
begin
- if Value = TRUE then
- Set_Parameter(DSSPrime, 'DeltaDirection', '-1')
+ if Value then
+ Set_Parameter(DSSPrime, ord(TSensorProp.DeltaDirection), -1)
else
- Set_Parameter(DSSPrime, 'DeltaDirection', '1');
+ Set_Parameter(DSSPrime, ord(TSensorProp.DeltaDirection), 1);
end;
//------------------------------------------------------------------------------
procedure Sensors_Set_Weight(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'weight', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TSensorProp.weight), Value);
end;
//------------------------------------------------------------------------------
function Sensors_Get_kVbase(): Double; CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
@@ -424,7 +448,7 @@ function Sensors_Get_kVbase(): Double; CDECL;
//------------------------------------------------------------------------------
procedure Sensors_Set_kVbase(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'kvbase', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TSensorProp.kvbase), Value);
end;
//------------------------------------------------------------------------------
function Sensors_Get_idx(): Integer; CDECL;
@@ -437,14 +461,14 @@ function Sensors_Get_idx(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Sensors_Set_idx(Value: Integer); CDECL;
var
- pSensor: TSensorObj;
+ pSensor: TObj;
begin
if InvalidCircuit(DSSPrime) then
Exit;
pSensor := DSSPrime.ActiveCircuit.Sensors.Get(Value);
if pSensor = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Sensor index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['Sensor', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pSensor;
@@ -452,7 +476,7 @@ procedure Sensors_Set_idx(Value: Integer); CDECL;
//------------------------------------------------------------------------------
procedure Sensors_Get_AllocationFactor(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
var
- elem: TSensorObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
begin
diff --git a/src/CAPI/CAPI_Settings.pas b/src/CAPI/CAPI_Settings.pas
index 9efd6a906..f698e76ac 100644
--- a/src/CAPI/CAPI_Settings.pas
+++ b/src/CAPI/CAPI_Settings.pas
@@ -59,7 +59,8 @@ implementation
ExecHelper,
Executive,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ SysUtils;
function Settings_Get_AllowDuplicates(): TAPIBoolean; CDECL;
begin
@@ -393,7 +394,7 @@ procedure Settings_Set_PriceCurve(const Value: PAnsiChar); CDECL;
PriceCurve := Value;
PriceCurveObj := DSSPrime.LoadShapeClass.Find(Pricecurve);
if PriceCurveObj = NIL then
- DoSimpleMsg(DSSPrime, 'Price Curve: "' + Pricecurve + '" not found.', 5006);
+ DoSimpleMsg(DSSPrime, 'Price Curve: "%s" not found.', [PriceCurve], 5006);
end;
end;
//------------------------------------------------------------------------------
diff --git a/src/CAPI/CAPI_Solution.pas b/src/CAPI/CAPI_Solution.pas
index 3e06c370f..9f3ef8296 100644
--- a/src/CAPI/CAPI_Solution.pas
+++ b/src/CAPI/CAPI_Solution.pas
@@ -166,7 +166,6 @@ function Solution_Get_MaxIterations(): Integer; CDECL;
//------------------------------------------------------------------------------
function Solution_Get_Mode(): Integer; CDECL;
begin
- //If not InvalidCircuit(DSSPrime) Then Result := GetSolutionModeID changed to integer 8/16/00
Result := 0;
if InvalidCircuit(DSSPrime) then
Exit;
@@ -260,7 +259,7 @@ procedure Solution_Set_Mode(Mode: Integer); CDECL;
if (Mode >= Ord(Low(TSolveMode))) and (Mode <= Ord(High(TSolveMode))) then
DSSPrime.ActiveCircuit.Solution.Mode := TSolveMode(Mode)
else
- DoSimpleMsg(DSSPrime, Format('Invalid solution mode (%d).', [Mode]), 5004);
+ DoSimpleMsg(DSSPrime, 'Invalid solution mode (%d).', [Mode], 5004);
end;
//------------------------------------------------------------------------------
procedure Solution_Set_Number(Value: Integer); CDECL;
@@ -323,7 +322,7 @@ function Solution_Get_ModeID(): PAnsiChar; CDECL;
Result := NIL;
if InvalidCircuit(DSSPrime) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, GetSolutionModeID(DSSPrime))
+ Result := DSS_GetAsPAnsiChar(DSSPrime, DSSPrime.SolveModeEnum.OrdinalToString(ord(DSSPrime.ActiveCircuit.Solution.Mode)))
end;
//------------------------------------------------------------------------------
function Solution_Get_LoadModel(): Integer; CDECL;
@@ -362,7 +361,7 @@ procedure Solution_Set_LDCurve(const Value: PAnsiChar); CDECL;
LoadDurCurve := Value;
LoadDurCurveObj := DSSPrime.LoadShapeClass.Find(LoadDurCurve);
if LoadDurCurveObj = NIL then
- DoSimpleMsg(DSSPrime, 'Load-Duration Curve not found.', 5001);
+ DoSimpleMsg(DSSPrime, _('Load-Duration Curve not found.'), 5001);
end;
end;
//------------------------------------------------------------------------------
@@ -866,23 +865,24 @@ procedure Solution_Set_MinIterations(Value: Integer); CDECL;
DSSPrime.ActiveCircuit.Solution.MinIterations := Value;
end;
-{$IFDEF DSS_CAPI_PM}
//------------------------------------------------------------------------------
procedure Solution_SolveAll();cdecl;
+{$IFDEF DSS_CAPI_PM}
var
i : Integer;
+ PMParent: TDSSContext;
begin
- for i := 0 to High(DSSPrime.Children) do
+ PMParent := DSSPrime.GetPrime();
+ for i := 0 to High(PMParent.Children) do
begin
- DSSPrime.CmdResult := DoSetCmd(DSSPrime.Children[i], 1);
+ PMParent.ActiveChild := PMParent.Children[i];
+ DSSPrime.CmdResult := DoSetCmd(PMParent.Children[i], 1);
end;
-end;
{$ELSE}
-procedure Solution_SolveAll();cdecl;
begin
- DoSimpleMsg(DSSPrime, 'Parallel machine functions were not compiled', 7983);
-end;
+ DoSimpleMsg(DSSPrime, _('Parallel machine functions were not compiled'), 7983);
{$ENDIF}
+end;
//------------------------------------------------------------------------------
procedure Solution_Get_Laplacian(var ResultPtr: PInteger; ResultCount: PAPISize); CDECL;
var
@@ -935,7 +935,7 @@ procedure Solution_Get_IncMatrix(var ResultPtr: PInteger; ResultCount: PAPISize)
with DSSPrime.ActiveCircuit.Solution do
begin
ArrSize := IncMat.NZero * 3;
- Result := DSS_RecreateArray_PInteger(ResultPtr, ResultCount, ArrSize + 1); // TODO: remove +1?
+ Result := DSS_RecreateArray_PInteger(ResultPtr, ResultCount, ArrSize + 1); // TODO: remove +1? Left for compatibility with the official version
Counter := 0;
IMIdx := 0;
while IMIdx < ArrSize do
diff --git a/src/CAPI/CAPI_Storages.pas b/src/CAPI/CAPI_Storages.pas
index f01235e05..9f57f27bf 100644
--- a/src/CAPI/CAPI_Storages.pas
+++ b/src/CAPI/CAPI_Storages.pas
@@ -39,7 +39,7 @@ function OldModels(DSS: TDSSContext): Boolean;
begin
Result := DSS_CAPI_LEGACY_MODELS;
if DSS_CAPI_LEGACY_MODELS then
- DoSimpleMsg(DSS, 'The Storages API is not available in the legacy-models mode!', 18990);
+ DoSimpleMsg(DSS, _('The Storages API is not available in the legacy-models mode!'), 18990);
end;
//------------------------------------------------------------------------------
@@ -55,7 +55,7 @@ function _activeObj(DSS: TDSSContext; out obj: TStorage2Obj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active Storage object found! Activate one and retry.', 18989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['Storage'], 18989);
end;
Exit;
end;
@@ -118,7 +118,7 @@ procedure Storages_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'Storage "' + Value + '" Not Found in Active Circuit.', 77003);
+ DoSimpleMsg(DSSPrime, 'Storage "%s" not found in Active Circuit.', [Value], 77003);
end;
end;
//------------------------------------------------------------------------------
@@ -139,7 +139,7 @@ procedure Storages_Set_idx(Value: Integer); CDECL;
pStorage := DSSPrime.ActiveCircuit.StorageElements.Get(Value);
if pStorage = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Storage index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['Storage', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pStorage;
@@ -223,7 +223,7 @@ procedure Storages_Set_State(Value: Integer); CDECL;
(Value <> STORE_IDLING) and
(Value <> STORE_DISCHARGING) then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Storage state: "' + IntToStr(Value) + '".', 656568);
+ DoSimpleMsg(DSSPrime, 'Invalid Storage state: "%d".', [Value], 656568);
end;
elem.StorageState := Value;
end;
diff --git a/src/CAPI/CAPI_SwtControls.pas b/src/CAPI/CAPI_SwtControls.pas
index 5cdd25e89..dd02182a9 100644
--- a/src/CAPI/CAPI_SwtControls.pas
+++ b/src/CAPI/CAPI_SwtControls.pas
@@ -44,7 +44,8 @@ implementation
SysUtils,
DSSPointerList,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ DSSObjectHelper;
function _activeObj(DSS: TDSSContext; out obj: TSwtControlObj): Boolean; inline;
begin
@@ -58,7 +59,7 @@ function _activeObj(DSS: TDSSContext; out obj: TSwtControlObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active SwtControl object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['SwtControl'], 8989);
end;
Exit;
end;
@@ -66,18 +67,6 @@ function _activeObj(DSS: TDSSContext; out obj: TSwtControlObj): Boolean; inline;
Result := True;
end;
//------------------------------------------------------------------------------
-procedure Set_Parameter(DSS: TDSSContext; const parm: String; const val: String);
-var
- cmd: String;
- obj: TSwtControlObj;
-begin
- if not _activeObj(DSS, obj) then
- exit;
- DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
- cmd := Format('swtcontrol.%s.%s=%s', [obj.Name, parm, val]);
- DSS.DSSExecutive.Command := cmd;
-end;
-//------------------------------------------------------------------------------
function SwtControls_Get_Action(): Integer; CDECL;
var
elem: TSwtControlObj;
@@ -141,7 +130,7 @@ function SwtControls_Get_IsLocked(): TAPIBoolean; CDECL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.IsLocked; // Fixed bug here
+ Result := elem.Locked; // Fixed bug here
end;
//------------------------------------------------------------------------------
function SwtControls_Get_Name(): PAnsiChar; CDECL;
@@ -163,7 +152,8 @@ function SwtControls_Get_SwitchedObj(): PAnsiChar; CDECL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.ElementName);
+ if elem.ControlledElement <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, AnsiLowerCase(elem.ControlledElement.FullName));
end;
//------------------------------------------------------------------------------
function SwtControls_Get_SwitchedTerm(): Integer; CDECL;
@@ -240,18 +230,28 @@ procedure SwtControls_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'SwtControl "' + Value + '" Not Found in Active Circuit.', 5003);
+ DoSimpleMsg(DSSPrime, 'SwtControl "%s" not found in Active Circuit.', [Value], 5003);
end;
end;
//------------------------------------------------------------------------------
procedure SwtControls_Set_SwitchedObj(const Value: PAnsiChar); CDECL;
+var
+ elem: TSwtControlObj;
begin
- Set_Parameter(DSSPrime, 'SwitchedObj', Value);
+ if not _activeObj(DSSPrime, elem) then
+ Exit;
+ DSSPrime.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.ParsePropertyValue(ord(TSwtControlProp.SwitchedObj), Value);
end;
//------------------------------------------------------------------------------
procedure SwtControls_Set_SwitchedTerm(Value: Integer); CDECL;
+var
+ elem: TSwtControlObj;
begin
- Set_Parameter(DSSPrime, 'SwitchedTerm', IntToStr(Value));
+ if not _activeObj(DSSPrime, elem) then
+ Exit;
+ DSSPrime.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.SetInteger(ord(TSwtControlProp.SwitchedTerm), Value);
end;
//------------------------------------------------------------------------------
function SwtControls_Get_Count(): Integer; CDECL;
@@ -325,9 +325,9 @@ procedure SwtControls_Reset(); CDECL;
function SwtControls_Get_idx(): Integer; CDECL;
begin
if InvalidCircuit(DSSPrime) then
- Result := DSSPrime.ActiveCircuit.SwtControls.ActiveIndex
+ Result := 0
else
- Result := 0;
+ Result := DSSPrime.ActiveCircuit.SwtControls.ActiveIndex;
end;
//------------------------------------------------------------------------------
procedure SwtControls_Set_idx(Value: Integer); CDECL;
@@ -340,7 +340,7 @@ procedure SwtControls_Set_idx(Value: Integer); CDECL;
pSwtControl := DSSPrime.ActiveCircuit.SwtControls.Get(Value);
if pSwtControl = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid SwtControl index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['SwtControl', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pSwtControl;
diff --git a/src/CAPI/CAPI_TSData.pas b/src/CAPI/CAPI_TSData.pas
index 4a5b0ed9e..7e676c8e3 100644
--- a/src/CAPI/CAPI_TSData.pas
+++ b/src/CAPI/CAPI_TSData.pas
@@ -8,9 +8,6 @@ interface
TSData,
CableData;
-type
- TSDataProps = (DiaShield = 1, TapeLayer, TapeLap);
-
// Common to all classes
function TSData_Get_Count(): Integer; CDECL;
function TSData_Get_First(): Integer; CDECL;
@@ -75,6 +72,10 @@ implementation
DSSClass,
DSSHelper;
+const
+ ConductorPropOffset = ord(High(TCableDataProp)) + ord(High(TTSDataProp));
+ CableDataPropOffset = ord(High(TTSDataProp));
+
//------------------------------------------------------------------------------
function _activeObj(DSS: TDSSContext; out obj: TTSDataObj): Boolean; inline;
begin
@@ -88,33 +89,13 @@ function _activeObj(DSS: TDSSContext; out obj: TTSDataObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active TSData object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['TSData'], 8989);
end;
Exit;
end;
Result := True;
end;
-//------------------------------------------------------------------------------
-procedure TSDataSetDefaults(prop: TSDataProps; conductor: TTSDataObj);
-begin
- {Set defaults}
- with conductor do
- begin
- {Check for critical errors}
- case prop of
- TSDataProps.DiaShield:
- if (FDiaShield <= 0.0) then
- DoSimpleMsg('Error: Diameter over shield must be positive for TapeShieldData ' + Name, 999);
- TSDataProps.TapeLayer:
- if (FTapeLayer <= 0.0) then
- DoSimpleMsg('Error: Tape shield thickness must be positive for TapeShieldData ' + Name, 999);
- TSDataProps.TapeLap:
- if ((FTapeLap < 0.0) or (FTapeLap > 100.0)) then
- DoSimpleMsg('Error: Tap lap must range from 0 to 100 for TapeShieldData ' + Name, 999);
- end;
- end;
-end;
//------------------------------------------------------------------------------
function TSData_Get_Count(): Integer; CDECL;
begin
@@ -142,12 +123,12 @@ function TSData_Get_Next(): Integer; CDECL;
//------------------------------------------------------------------------------
function TSData_Get_Name(): PAnsiChar; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := NIL;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, pTSData.Name);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.Name);
end;
//------------------------------------------------------------------------------
procedure TSData_Set_Name(const Value: PAnsiChar); CDECL;
@@ -158,7 +139,7 @@ procedure TSData_Set_Name(const Value: PAnsiChar); CDECL;
Exit;
if not DSSPrime.TSDataClass.SetActive(Value) then
- DoSimpleMsg(DSSPrime, 'TSData "' + Value + '" Not Found in Active Circuit.', 51008);
+ DoSimpleMsg(DSSPrime, 'TSData "%s" not found in Active Circuit.', [Value], 51008);
// Still same active object if not found
end;
@@ -179,344 +160,350 @@ procedure TSData_Get_AllNames_GR(); CDECL;
//------------------------------------------------------------------------------
function TSData_Get_NormAmps(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.NormAmps;
+ Result := elem.NormAmps;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_NormAmps(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.NormAmps := Value;
- ConductorSetDefaults(ConductorProps.NormAmps, pTSData);
+ elem.NormAmps := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.NormAmps))
end;
//------------------------------------------------------------------------------
function TSData_Get_EmergAmps(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData := DSSPrime.TSDataClass.GetActiveObj;
- Result := pTSData.EmergAmps;
+ elem := DSSPrime.TSDataClass.GetActiveObj;
+ Result := elem.EmergAmps;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_EmergAmps(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.EmergAmps := Value;
- ConductorSetDefaults(ConductorProps.EmergAmps, pTSData);
+ elem.EmergAmps := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.EmergAmps))
end;
//------------------------------------------------------------------------------
function TSData_Get_Diameter(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FRadius * 2.0;
+ Result := elem.FRadius * 2.0;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_Diameter(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FRadius := Value / 2.0;
- ConductorSetDefaults(ConductorProps.diam, pTSData);
+ elem.FRadius := Value / 2.0;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.diam))
end;
//------------------------------------------------------------------------------
function TSData_Get_Radius(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FRadius;
+ Result := elem.FRadius;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_Radius(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FRadius := Value;
- ConductorSetDefaults(ConductorProps.Radius, pTSData);
+ elem.FRadius := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.Radius))
end;
//------------------------------------------------------------------------------
function TSData_Get_GMRac(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FGMR60;
+ Result := elem.FGMR60;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_GMRac(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FGMR60 := Value;
- ConductorSetDefaults(ConductorProps.GMRac, pTSData);
+ elem.FGMR60 := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.GMRac))
end;
//------------------------------------------------------------------------------
function TSData_Get_Rac(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FR60;
+ Result := elem.FR60;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_Rac(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FR60 := Value;
- ConductorSetDefaults(ConductorProps.Rac, pTSData);
+ elem.FR60 := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.Rac))
end;
//------------------------------------------------------------------------------
function TSData_Get_Rdc(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FRDC;
+ Result := elem.FRDC;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_Rdc(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FRDC := Value;
- ConductorSetDefaults(ConductorProps.Rdc, pTSData);
+ elem.FRDC := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.Rdc))
end;
//------------------------------------------------------------------------------
function TSData_Get_GMRUnits(): Integer; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FGMRUnits;
+ Result := elem.FGMRUnits;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_GMRUnits(Value: Integer); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
+ prevVal: Integer;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FGMRUnits := Value;
- ConductorSetDefaults(ConductorProps.GMRunits, pTSData);
+ prevVal := elem.FGMRUnits;
+ elem.FGMRUnits := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.GMRunits), prevVal)
end;
//------------------------------------------------------------------------------
function TSData_Get_RadiusUnits(): Integer; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FRadiusUnits;
+ Result := elem.FRadiusUnits;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_RadiusUnits(Value: Integer); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
+ prevVal: Integer;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FRadiusUnits := Value;
- ConductorSetDefaults(ConductorProps.radunits, pTSData);
+ prevVal := elem.FRadiusUnits;
+ elem.FRadiusUnits := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.radunits), prevVal)
end;
//------------------------------------------------------------------------------
function TSData_Get_ResistanceUnits(): Integer; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FResistanceUnits;
+ Result := elem.FResistanceUnits;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_ResistanceUnits(Value: Integer); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
+ prevVal: Integer;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FResistanceUnits := Value;
- ConductorSetDefaults(ConductorProps.Runits, pTSData);
+ prevVal := elem.FResistanceUnits;
+ elem.FResistanceUnits := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.Runits), prevVal)
end;
//------------------------------------------------------------------------------
function TSData_Get_EpsR(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FEpsR;
+ Result := elem.FEpsR;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_EpsR(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FEpsR := Value;
- CableDataSetDefaults(CableDataProps.EpsR, pTSData);
+ elem.FEpsR := Value;
+ elem.PropertySideEffects(CableDataPropOffset + ord(TCableDataProp.EpsR))
end;
//------------------------------------------------------------------------------
function TSData_Get_InsLayer(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FInsLayer;
+ Result := elem.FInsLayer;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_InsLayer(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FInsLayer := Value;
- CableDataSetDefaults(CableDataProps.InsLayer, pTSData);
+ elem.FInsLayer := Value;
+ elem.PropertySideEffects(CableDataPropOffset + ord(TCableDataProp.InsLayer))
end;
//------------------------------------------------------------------------------
function TSData_Get_DiaIns(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FDiaIns;
+ Result := elem.FDiaIns;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_DiaIns(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FDiaIns := Value;
- CableDataSetDefaults(CableDataProps.DiaIns, pTSData);
+ elem.FDiaIns := Value;
+ elem.PropertySideEffects(CableDataPropOffset + ord(TCableDataProp.DiaIns))
end;
//------------------------------------------------------------------------------
function TSData_Get_DiaCable(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FDiaCable;
+ Result := elem.FDiaCable;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_DiaCable(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FDiaCable := Value;
- CableDataSetDefaults(CableDataProps.DiaCable, pTSData);
+ elem.FDiaCable := Value;
+ elem.PropertySideEffects(CableDataPropOffset + ord(TCableDataProp.DiaCable))
end;
//------------------------------------------------------------------------------
function TSData_Get_DiaShield(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FDiaShield;
+ Result := elem.DiaShield;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_DiaShield(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FDiaShield := Value;
- TSDataSetDefaults(TSDataProps.DiaShield, pTSData);
+ elem.DiaShield := Value;
+ elem.PropertySideEffects(ord(TTSDataProp.DiaShield))
end;
//------------------------------------------------------------------------------
function TSData_Get_TapeLayer(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FTapeLayer;
+ Result := elem.TapeLayer;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_TapeLayer(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FTapeLayer := Value;
- TSDataSetDefaults(TSDataProps.TapeLayer, pTSData);
+ elem.TapeLayer := Value;
+ elem.PropertySideEffects(ord(TTSDataProp.TapeLayer))
end;
//------------------------------------------------------------------------------
function TSData_Get_TapeLap(): Double; CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pTSData.FTapeLap;
+ Result := elem.TapeLap;
end;
//------------------------------------------------------------------------------
procedure TSData_Set_TapeLap(Value: Double); CDECL;
var
- pTSData: TTSDataObj;
+ elem: TTSDataObj;
begin
- if not _activeObj(DSSPrime, pTSData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pTSData.FTapeLap := Value;
- TSDataSetDefaults(TSDataProps.TapeLap, pTSData);
+ elem.TapeLap := Value;
+ elem.PropertySideEffects(ord(TTSDataProp.TapeLap))
end;
//------------------------------------------------------------------------------
function TSData_Get_idx(): Integer; CDECL;
@@ -527,7 +514,7 @@ function TSData_Get_idx(): Integer; CDECL;
procedure TSData_Set_idx(Value: Integer); CDECL;
begin
if (DSSPrime.TSDataClass = NIL) or (DSSPrime.TSDataClass.ElementList.Get(Value) = NIL) then
- DoSimpleMsg(DSSPrime, 'Invalid TSData index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['TSData', Value], 656565);
end;
//------------------------------------------------------------------------------
end.
diff --git a/src/CAPI/CAPI_Text.pas b/src/CAPI/CAPI_Text.pas
index 06afdbe12..25aeb9a3e 100644
--- a/src/CAPI/CAPI_Text.pas
+++ b/src/CAPI/CAPI_Text.pas
@@ -9,19 +9,21 @@ interface
function Text_Get_Command(): PAnsiChar; CDECL;
procedure Text_Set_Command(const Value: PAnsiChar); CDECL;
function Text_Get_Result(): PAnsiChar; CDECL;
+procedure Text_CommandBlock(const Value: PAnsiChar); CDECL;
+procedure Text_CommandArray(const Value: PPAnsiChar; ValueCount: TAPISize); CDECL;
implementation
uses
CAPI_Constants,
+ Classes,
DSSGlobals,
Executive,
SysUtils,
+ ExecHelper,
DSSClass,
DSSHelper;
-//TODO: Text_Run_Script or Text_Run_Commands -- run multiple lines at one
-
//------------------------------------------------------------------------------
function Text_Get_Command(): PAnsiChar; CDECL;
begin
@@ -31,7 +33,50 @@ function Text_Get_Command(): PAnsiChar; CDECL;
procedure Text_Set_Command(const Value: PAnsiChar); CDECL;
begin
DSSPrime.SolutionAbort := FALSE; // Reset for commands entered from outside
- DSSPrime.DSSExecutive.Command := Value; {Convert to String}
+ DSSPrime.DSSExecutive.Command := Value; // Convert to String
+end;
+//------------------------------------------------------------------------------
+procedure Text_CommandBlock(const Value: PAnsiChar); CDECL;
+var
+ i: Integer;
+ strs: TStringList;
+begin
+ DSSPrime.SolutionAbort := FALSE; // Reset for commands entered from outside
+ try
+ strs := TStringList.Create();
+ strs.AddText(Value);
+ for i := 1 to strs.Count do
+ if Length(strs[i - 1]) > 0 then
+ begin
+ DSSPrime.DSSExecutive.Set_Command(strs[i - 1], i); // Convert to String
+ if DSSPrime.ErrorNumber <> 0 then
+ begin
+ //TODO: complement error message?
+ Exit;
+ end;
+ end;
+ finally
+ strs.Free;
+ end;
+end;
+//------------------------------------------------------------------------------
+procedure Text_CommandArray(const Value: PPAnsiChar; ValueCount: TAPISize); CDECL;
+var
+ i: Integer;
+ p: PPAnsiChar;
+begin
+ DSSPrime.SolutionAbort := FALSE; // Reset for commands entered from outside
+ p := Value;
+ for i := 1 to ValueCount do
+ begin
+ DSSPrime.DSSExecutive.Set_Command(p^, i); // Convert to String
+ inc(p);
+ if DSSPrime.ErrorNumber <> 0 then
+ begin
+ //TODO: complement error message?
+ Exit;
+ end;
+ end;
end;
//------------------------------------------------------------------------------
function Text_Get_Result(): PAnsiChar; CDECL;
@@ -40,13 +85,10 @@ function Text_Get_Result(): PAnsiChar; CDECL;
Result := NIL
else
Result := DSS_GetAsPAnsiChar(DSSPrime, DSSPrime.GlobalResult);
- {****}
- {
- Need to implement a protocol for determining whether to go get the
- result from a file or to issue another DSS command to get the value
- from operations where the result is voluminous.
- }
+ // Need to implement a protocol for determining whether to go get the
+ // result from a file or to issue another DSS command to get the value
+ // from operations where the result is voluminous.
end;
//------------------------------------------------------------------------------
end.
diff --git a/src/CAPI/CAPI_Topology.pas b/src/CAPI/CAPI_Topology.pas
index 84007d467..c55cd432d 100644
--- a/src/CAPI/CAPI_Topology.pas
+++ b/src/CAPI/CAPI_Topology.pas
@@ -55,7 +55,7 @@ function ActiveTree(DSS: TDSSContext; out topo: TCktTree): Boolean;
begin
if (DSS_CAPI_EXT_ERRORS) then
begin
- DoSimpleMsg(DSS, 'Topology is not initialized for the active circuit.', 5097);
+ DoSimpleMsg(DSS, _('Topology is not initialized for the active circuit.'), 5097);
end;
Exit;
end;
@@ -127,9 +127,9 @@ procedure Topology_Get_AllIsolatedBranches(var ResultPtr: PPAnsiChar; ResultCoun
while assigned(elm) do
begin
- if elm.IsIsolated then
+ if Flg.IsIsolated in elm.Flags then
begin
- Result[k] := elm.QualifiedName;
+ Result[k] := elm.FullName;
Inc(k);
if k > 0 then
SetLength(Result, k + 1);
@@ -183,9 +183,9 @@ procedure Topology_Get_AllLoopedPairs(var ResultPtr: PPAnsiChar; ResultCount: PA
i := 1;
while (i <= k) and (not found) do
begin
- if (Result[i - 1] = pdElem.QualifiedName) and (Result[i] = pdLoop.QualifiedName) then
+ if (Result[i - 1] = pdElem.FullName) and (Result[i] = pdLoop.FullName) then
found := TRUE;
- if (Result[i - 1] = pdLoop.QualifiedName) and (Result[i] = pdElem.QualifiedName) then
+ if (Result[i - 1] = pdLoop.FullName) and (Result[i] = pdElem.FullName) then
found := TRUE;
i := i + 1;
end;
@@ -193,8 +193,8 @@ procedure Topology_Get_AllLoopedPairs(var ResultPtr: PPAnsiChar; ResultCount: PA
begin
k := k + 2;
SetLength(Result, k + 1);
- Result[k - 1] := pdElem.QualifiedName;
- Result[k] := pdLoop.QualifiedName;
+ Result[k - 1] := pdElem.FullName;
+ Result[k] := pdLoop.FullName;
end;
end;
PDElem := topo.GoForward;
@@ -248,7 +248,7 @@ function Topology_Get_BranchName(): PAnsiChar; CDECL;
Exit;
elm := node.CktObject;
if assigned(elm) then
- Result := DSS_GetAsPAnsiChar(DSSPrime, elm.QualifiedName);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elm.FullName);
end;
//------------------------------------------------------------------------------
function Topology_Get_First(): Integer; CDECL;
@@ -312,7 +312,7 @@ function Topology_Get_NumIsolatedBranches(): Integer; CDECL;
elm := DSSPrime.ActiveCircuit.PDElements.First;
while assigned(elm) do
begin
- if elm.IsIsolated then
+ if Flg.IsIsolated in elm.Flags then
Inc(Result);
elm := DSSPrime.ActiveCircuit.PDElements.Next;
end;
@@ -352,7 +352,7 @@ procedure Topology_Set_BranchName(const Value: PAnsiChar); CDECL;
pdElem := topo.First;
while Assigned(pdElem) do
begin
- if (CompareText(pdElem.QualifiedName, S) = 0) then
+ if (AnsiCompareText(pdElem.FullName, S) = 0) then
begin
DSSPrime.ActiveCircuit.ActiveCktElement := pdElem;
Found := TRUE;
@@ -363,7 +363,7 @@ procedure Topology_Set_BranchName(const Value: PAnsiChar); CDECL;
end;
if not Found then
begin
- DoSimpleMsg(DSSPrime, 'Branch "' + S + '" Not Found in Active Circuit Topology.', 5003);
+ DoSimpleMsg(DSSPrime, 'Branch "%s" not found in Active Circuit Topology.', [S], 5003);
if assigned(elem) then
DSSPrime.ActiveCircuit.ActiveCktElement := elem;
end;
@@ -384,9 +384,9 @@ procedure Topology_Get_AllIsolatedLoads(var ResultPtr: PPAnsiChar; ResultCount:
elm := DSSPrime.ActiveCircuit.PCElements.First;
while assigned(elm) do
begin
- if elm.IsIsolated then
+ if Flg.IsIsolated in elm.Flags then
begin
- Result[k] := elm.QualifiedName;
+ Result[k] := elm.FullName;
Inc(k);
if k > 0 then
SetLength(Result, (k) + 1);
@@ -462,7 +462,7 @@ function Topology_Get_NumIsolatedLoads(): Integer; CDECL;
elm := DSSPrime.ActiveCircuit.PCElements.First;
while assigned(elm) do
begin
- if elm.IsIsolated then
+ if Flg.IsIsolated in elm.Flags then
Inc(Result);
elm := DSSPrime.ActiveCircuit.PCElements.Next;
end;
@@ -507,7 +507,7 @@ procedure Topology_Set_BusName(const Value: PAnsiChar); CDECL;
B := pdElem.FirstBus;
while Length(B) > 0 do
begin
- if (CompareText(B, S) = 0) then
+ if (AnsiCompareText(B, S) = 0) then
begin
DSSPrime.ActiveCircuit.ActiveCktElement := pdElem;
Found := TRUE;
@@ -519,7 +519,7 @@ procedure Topology_Set_BusName(const Value: PAnsiChar); CDECL;
end;
if not Found then
begin
- DoSimpleMsg(DSSPrime, 'Bus "' + S + '" Not Found in Active Circuit Topology.', 5003);
+ DoSimpleMsg(DSSPrime, 'Bus "%s" not found in Active Circuit Topology.', [S], 5003);
if assigned(elem) then
DSSPrime.ActiveCircuit.ActiveCktElement := elem;
end;
diff --git a/src/CAPI/CAPI_Transformers.pas b/src/CAPI/CAPI_Transformers.pas
index 3b7e5b329..c957f55f7 100644
--- a/src/CAPI/CAPI_Transformers.pas
+++ b/src/CAPI/CAPI_Transformers.pas
@@ -73,12 +73,16 @@ implementation
Transformer,
SysUtils,
DSSPointerList,
- ucomplex,
+ UComplex, DSSUcomplex,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ DSSObjectHelper;
+
+type
+ TObj = TTransfObj;
//------------------------------------------------------------------------------
-function _activeObj(DSS: TDSSContext; out obj: TTransfObj): Boolean; inline;
+function _activeObj(DSS: TDSSContext; out obj: TObj): Boolean; inline;
begin
Result := False;
obj := NIL;
@@ -90,7 +94,7 @@ function _activeObj(DSS: TDSSContext; out obj: TTransfObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active Transformer object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['Transformer'], 8989);
end;
Exit;
end;
@@ -98,16 +102,34 @@ function _activeObj(DSS: TDSSContext; out obj: TTransfObj): Boolean; inline;
Result := True;
end;
//------------------------------------------------------------------------------
-procedure Set_Parameter(DSS: TDSSContext; const parm: String; const val: String);
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: String); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.ParsePropertyValue(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Double); overload;
var
- cmd: String;
- obj: TTransfObj;
+ elem: TObj;
begin
- if not _activeObj(DSS, obj) then
+ if not _activeObj(DSS, elem) then
Exit;
DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
- cmd := Format('transformer.%s.%s=%s', [obj.Name, parm, val]);
- DSS.DSSExecutive.Command := cmd;
+ elem.SetDouble(idx, val);
+end;
+//------------------------------------------------------------------------------
+procedure Set_Parameter(DSS: TDSSContext; const idx: Integer; const val: Integer); overload;
+var
+ elem: TObj;
+begin
+ if not _activeObj(DSS, elem) then
+ Exit;
+ DSS.SolutionAbort := FALSE; // Reset for commands entered from outside
+ elem.SetInteger(idx, val);
end;
//------------------------------------------------------------------------------
procedure Transformers_Get_AllNames(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
@@ -143,73 +165,73 @@ function Transformers_Get_Next(): Integer; CDECL;
//------------------------------------------------------------------------------
function Transformers_Get_IsDelta(): TAPIBoolean; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := FALSE;
if not _activeObj(DSSPrime, elem) then
Exit;
if (elem.ActiveWinding > 0) and
- (elem.ActiveWinding <= elem.NumberOfWindings) and
+ (elem.ActiveWinding <= elem.NumWindings) and
(elem.WdgConnection[elem.ActiveWinding] > 0) then
Result := TRUE;
end;
//------------------------------------------------------------------------------
function Transformers_Get_kV(): Double; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
if (elem.ActiveWinding > 0) and
- (elem.ActiveWinding <= elem.NumberOfWindings) then
+ (elem.ActiveWinding <= elem.NumWindings) then
Result := elem.Winding^[elem.ActiveWinding].kvll;
end;
//------------------------------------------------------------------------------
function Transformers_Get_kVA(): Double; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
if (elem.ActiveWinding > 0) and
- (elem.ActiveWinding <= elem.NumberOfWindings) then
+ (elem.ActiveWinding <= elem.NumWindings) then
Result := elem.WdgKVA[elem.ActiveWinding];
end;
//------------------------------------------------------------------------------
function Transformers_Get_MaxTap(): Double; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
if (elem.ActiveWinding > 0) and
- (elem.ActiveWinding <= elem.NumberOfWindings) then
+ (elem.ActiveWinding <= elem.NumWindings) then
Result := elem.Maxtap[elem.ActiveWinding];
end;
//------------------------------------------------------------------------------
function Transformers_Get_MinTap(): Double; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
if (elem.ActiveWinding > 0) and
- (elem.ActiveWinding <= elem.NumberOfWindings) then
+ (elem.ActiveWinding <= elem.NumWindings) then
Result := elem.Mintap[elem.ActiveWinding];
end;
//------------------------------------------------------------------------------
function Transformers_Get_Name(): PAnsiChar; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
@@ -220,70 +242,70 @@ function Transformers_Get_Name(): PAnsiChar; CDECL;
//------------------------------------------------------------------------------
function Transformers_Get_NumTaps(): Integer; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
Exit;
if (elem.ActiveWinding > 0) and
- (elem.ActiveWinding <= elem.NumberOfWindings) then
+ (elem.ActiveWinding <= elem.NumWindings) then
Result := elem.NumTaps[elem.ActiveWinding];
end;
//------------------------------------------------------------------------------
function Transformers_Get_NumWindings(): Integer; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.NumberOfWindings;
+ Result := elem.NumWindings;
end;
//------------------------------------------------------------------------------
function Transformers_Get_R(): Double; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
if (elem.ActiveWinding > 0) and
- (elem.ActiveWinding <= elem.NumberOfWindings) then
- Result := elem.WdgResistance[elem.ActiveWinding];
+ (elem.ActiveWinding <= elem.NumWindings) then
+ Result := elem.WdgResistance[elem.ActiveWinding] * 100;
end;
//------------------------------------------------------------------------------
function Transformers_Get_Rneut(): Double; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
if (elem.ActiveWinding > 0) and
- (elem.ActiveWinding <= elem.NumberOfWindings) then
+ (elem.ActiveWinding <= elem.NumWindings) then
Result := elem.WdgRneutral[elem.ActiveWinding];
end;
//------------------------------------------------------------------------------
function Transformers_Get_Tap(): Double; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
if (elem.ActiveWinding > 0) and
- (elem.ActiveWinding <= elem.NumberOfWindings) then
+ (elem.ActiveWinding <= elem.NumWindings) then
Result := elem.PresentTap[elem.ActiveWinding];
end;
//------------------------------------------------------------------------------
function Transformers_Get_Wdg(): Integer; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0;
if not _activeObj(DSSPrime, elem) then
@@ -294,87 +316,88 @@ function Transformers_Get_Wdg(): Integer; CDECL;
//------------------------------------------------------------------------------
function Transformers_Get_XfmrCode(): PAnsiChar; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := NIL;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.XfmrCode);
+ if elem.XfmrCodeObj <> NIL then
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.XfmrCodeObj.Name);
end;
//------------------------------------------------------------------------------
function Transformers_Get_Xhl(): Double; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.XhlVal * 100.0;
+ Result := elem.Xhl * 100.0;
end;
//------------------------------------------------------------------------------
function Transformers_Get_Xht(): Double; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.XhtVal * 100.0;
+ Result := elem.Xht * 100.0;
end;
//------------------------------------------------------------------------------
function Transformers_Get_Xlt(): Double; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
- Result := elem.XltVal * 100.0;
+ Result := elem.Xlt * 100.0;
end;
//------------------------------------------------------------------------------
function Transformers_Get_Xneut(): Double; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
if (elem.ActiveWinding > 0) and
- (elem.ActiveWinding <= elem.NumberOfWindings) then
+ (elem.ActiveWinding <= elem.NumWindings) then
Result := elem.WdgXneutral[elem.ActiveWinding];
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_IsDelta(Value: TAPIBoolean); CDECL;
begin
if Value = TRUE then
- Set_Parameter(DSSPrime, 'Conn', 'Delta')
+ Set_Parameter(DSSPrime, ord(TTransfProp.Conn), 1)
else
- Set_Parameter(DSSPrime, 'Conn', 'Wye')
+ Set_Parameter(DSSPrime, ord(TTransfProp.Conn), 0)
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_kV(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'kv', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TTransfProp.kv), Value);
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_kVA(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'kva', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TTransfProp.kva), Value);
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_MaxTap(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'MaxTap', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TTransfProp.MaxTap), Value);
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_MinTap(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'MinTap', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TTransfProp.MinTap), Value);
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_Name(const Value: PAnsiChar); CDECL;
@@ -389,18 +412,18 @@ procedure Transformers_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'Transformer "' + Value + '" Not Found in Active Circuit.', 5003);
+ DoSimpleMsg(DSSPrime, 'Transformer "%s" not found in Active Circuit.', [Value], 5003);
end;
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_NumTaps(Value: Integer); CDECL;
begin
- Set_Parameter(DSSPrime, 'NumTaps', IntToStr(Value));
+ Set_Parameter(DSSPrime, ord(TTransfProp.NumTaps), Value);
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_NumWindings(Value: Integer); CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
@@ -410,52 +433,52 @@ procedure Transformers_Set_NumWindings(Value: Integer); CDECL;
//------------------------------------------------------------------------------
procedure Transformers_Set_R(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, '%R', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TTransfProp.pctR), Value);
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_Rneut(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Rneut', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TTransfProp.Rneut), Value);
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_Tap(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Tap', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TTransfProp.Tap), Value);
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_Wdg(Value: Integer); CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- if (value > 0) and (value <= elem.NumberOfWindings) then
+ if (value > 0) and (value <= elem.NumWindings) then
elem.ActiveWinding := Value;
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_XfmrCode(const Value: PAnsiChar); CDECL;
begin
- Set_Parameter(DSSPrime, 'XfmrCode', Value);
+ Set_Parameter(DSSPrime, ord(TTransfProp.XfmrCode), Value);
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_Xhl(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Xhl', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TTransfProp.Xhl), Value);
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_Xht(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Xht', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TTransfProp.Xht), Value);
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_Xlt(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Xlt', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TTransfProp.Xlt), Value);
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_Xneut(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'Xneut', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TTransfProp.Xneut), Value);
end;
//------------------------------------------------------------------------------
function Transformers_Get_Count(): Integer; CDECL;
@@ -467,7 +490,7 @@ function Transformers_Get_Count(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Transformers_Get_WdgVoltages(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
begin
@@ -475,7 +498,7 @@ procedure Transformers_Get_WdgVoltages(var ResultPtr: PDouble; ResultCount: PAPI
Exit;
end;
- if (elem.ActiveWinding > 0) and (elem.ActiveWinding <= elem.NumberOfWindings) then
+ if (elem.ActiveWinding > 0) and (elem.ActiveWinding <= elem.NumWindings) then
begin
DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2 * elem.nphases);
elem.GetWindingVoltages(elem.ActiveWinding, pComplexArray(ResultPtr));
@@ -493,7 +516,7 @@ procedure Transformers_Get_WdgVoltages_GR(); CDECL;
//------------------------------------------------------------------------------
procedure Transformers_Get_WdgCurrents(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
NumCurrents: Integer;
begin
if not _activeObj(DSSPrime, elem) then
@@ -502,7 +525,7 @@ procedure Transformers_Get_WdgCurrents(var ResultPtr: PDouble; ResultCount: PAPI
Exit;
end;
- NumCurrents := 2 * elem.NPhases * elem.NumberOfWindings; // 2 currents per winding
+ NumCurrents := 2 * elem.NPhases * elem.NumWindings; // 2 currents per winding
DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2 * NumCurrents);
elem.GetAllWindingCurrents(pComplexArray(ResultPtr));
end;
@@ -516,7 +539,7 @@ procedure Transformers_Get_WdgCurrents_GR(); CDECL;
//------------------------------------------------------------------------------
function Transformers_Get_strWdgCurrents(): PAnsiChar; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
begin
@@ -524,12 +547,12 @@ function Transformers_Get_strWdgCurrents(): PAnsiChar; CDECL;
Exit;
end;
- Result := DSS_GetAsPAnsiChar(DSSPrime, elem.GetWindingCurrentsResult);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.GetPropertyValue(ord(TTransfProp.WdgCurrents)));
end;
//------------------------------------------------------------------------------
function Transformers_Get_CoreType(): Integer; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0; // default = shell
if _activeObj(DSSPrime, elem) then
@@ -538,46 +561,36 @@ function Transformers_Get_CoreType(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Transformers_Set_CoreType(Value: Integer); CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
elem.CoreType := Value;
- case Value of
- 1:
- elem.strCoreType := '1-phase';
- 3:
- elem.strCoreType := '3-leg';
- 5:
- elem.strCoreType := '5-leg';
- else
- elem.strCoreType := 'shell';
- end;
end;
//------------------------------------------------------------------------------
function Transformers_Get_RdcOhms(): Double; CDECL;
var
- elem: TTransfObj;
+ elem: TObj;
begin
Result := 0.0;
if not _activeObj(DSSPrime, elem) then
Exit;
if (elem.ActiveWinding > 0) and
- (elem.ActiveWinding <= elem.NumberOfWindings) then
- Result := elem.WdgRdc[elem.ActiveWinding];
+ (elem.ActiveWinding <= elem.NumWindings) then
+ Result := elem.Winding[elem.ActiveWinding].Rdcohms;
end;
//------------------------------------------------------------------------------
procedure Transformers_Set_RdcOhms(Value: Double); CDECL;
begin
- Set_Parameter(DSSPrime, 'RdcOhms', FloatToStr(Value));
+ Set_Parameter(DSSPrime, ord(TTransfProp.RdcOhms), Value);
end;
//------------------------------------------------------------------------------
procedure Transformers_Get_LossesByType(var ResultPtr: PDouble; ResultCount: PAPISize); CDECL;
// Returns an array with (TotalLosses, LoadLosses, NoLoadLosses) for the current active transformer, in VA
var
- CResult: PComplexArray; // this array is one-based, see Ucomplex
- elem: TTransfObj;
+ CResult: PComplexArray; // this array is one-based, see UComplex, DSSUcomplex
+ elem: TObj;
begin
if not _activeObj(DSSPrime, elem) then
begin
@@ -601,8 +614,8 @@ procedure Transformers_Get_AllLossesByType(var ResultPtr: PDouble; ResultCount:
// Returns an array with (TotalLosses, LoadLosses, NoLoadLosses) for all transformers, in VA
var
Result: PDoubleArray0;
- CResult: PComplexArray; // this array is one-based, see Ucomplex
- elem: TTransfObj;
+ CResult: PComplexArray; // this array is one-based, see UComplex, DSSUcomplex
+ elem: TObj;
lst: TDSSPointerList;
k: Integer;
begin
@@ -645,14 +658,14 @@ function Transformers_Get_idx(): Integer; CDECL;
//------------------------------------------------------------------------------
procedure Transformers_Set_idx(Value: Integer); CDECL;
var
- pTransformer: TTransfObj;
+ pTransformer: TObj;
begin
if InvalidCircuit(DSSPrime) then
Exit;
pTransformer := DSSPrime.ActiveCircuit.Transformers.Get(Value);
if pTransformer = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid Transformer index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['Transformer', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pTransformer;
diff --git a/src/CAPI/CAPI_Types.pas b/src/CAPI/CAPI_Types.pas
index 9ccbfc645..7f5b6bb1b 100644
--- a/src/CAPI/CAPI_Types.pas
+++ b/src/CAPI/CAPI_Types.pas
@@ -5,6 +5,9 @@
interface
type
+ PointerArray0 = array[0..$effffff] of Pointer;
+ PPointerArray0 = ^PointerArray0;
+
DoubleArray0 = array[0..$effffff] of Double;
PDoubleArray0 = ^DoubleArray0;
diff --git a/src/CAPI/CAPI_Utils.pas b/src/CAPI/CAPI_Utils.pas
index c8f9c4111..cb7a06c07 100644
--- a/src/CAPI/CAPI_Utils.pas
+++ b/src/CAPI/CAPI_Utils.pas
@@ -73,12 +73,15 @@ procedure DSS_Dispose_PSingle(var p: PSingle); CDECL;
procedure DSS_Dispose_PDouble(var p: PDouble); CDECL;
procedure DSS_Dispose_PInteger(var p: PInteger); CDECL;
procedure DSS_Dispose_PPAnsiChar(var p: PPAnsiChar; cnt: TAPISize); CDECL;
+procedure DSS_Dispose_PPointer(var p: PPointer); CDECL;
function DSS_CreateArray_PByte(var p: PByte; cnt: PAPISize; const incount: TAPISize): PByteArray;
function DSS_CreateArray_PSingle(var p: PSingle; cnt: PAPISize; const incount: TAPISize): PSingleArray0;
function DSS_CreateArray_PDouble(var p: PDouble; cnt: PAPISize; const incount: TAPISize): PDoubleArray0;
function DSS_CreateArray_PInteger(var p: PInteger; cnt: PAPISize; const incount: TAPISize): PIntegerArray0;
function DSS_CreateArray_PPAnsiChar(var p: PPAnsiChar; cnt: PAPISize; const incount: TAPISize): PPAnsiCharArray0;
+function DSS_CreateArray_PPointer(var p: PPointer; cnt: PAPISize; const incount: TAPISize): PPointerArray0;
+
// NOTE: these do not copy to copy old values
procedure DSS_RecreateArray_PByte(var res: PByteArray; var p: PByte; cnt: PAPISize; const incount: TAPISize);
@@ -86,12 +89,13 @@ procedure DSS_RecreateArray_PSingle(var res: PSingleArray0; var p: PSingle; cnt:
procedure DSS_RecreateArray_PDouble(var res: PDoubleArray0; var p: PDouble; cnt: PAPISize; const incount: TAPISize);
procedure DSS_RecreateArray_PInteger(var res: PIntegerArray0; var p: PInteger; cnt: PAPISize; const incount: TAPISize);
procedure DSS_RecreateArray_PPAnsiChar(var res: PPAnsiCharArray0; var p: PPAnsiChar; cnt: PAPISize; const incount: TAPISize);
+procedure DSS_RecreateArray_PPointer(var res: PPointerArray0; var p: PPointer; cnt: PAPISize; const incount: TAPISize);
function DSS_RecreateArray_PByte(var p: PByte; cnt: PAPISize; const incount: TAPISize): PByteArray;
function DSS_RecreateArray_PSingle(var p: PSingle; cnt: PAPISize; const incount: TAPISize): PSingleArray0;
function DSS_RecreateArray_PDouble(var p: PDouble; cnt: PAPISize; const incount: TAPISize): PDoubleArray0;
function DSS_RecreateArray_PInteger(var p: PInteger; cnt: PAPISize; const incount: TAPISize): PIntegerArray0;
function DSS_RecreateArray_PPAnsiChar(var p: PPAnsiChar; cnt: PAPISize; const incount: TAPISize): PPAnsiCharArray0;
-
+function DSS_RecreateArray_PPointer(var p: PPointer; cnt: PAPISize; const incount: TAPISize): PPointerArray0;
// MATLAB doesn't handle pointers that well,
// this just gets a single string from the pointer of strings
function DSS_Get_PAnsiChar(var p: Pointer; Index: TAPISize): PAnsiChar; CDECL;
@@ -179,7 +183,7 @@ function InvalidCircuit(DSS: TDSSContext): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'There is no active circuit! Create a circuit and retry.', 8888);
+ DoSimpleMsg(DSS, _('There is no active circuit! Create a circuit and retry.'), 8888);
end;
Result := True;
Exit;
@@ -197,7 +201,7 @@ function MissingSolution(DSS: TDSSContext): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'Solution state is not initialized for the active circuit!', 8899);
+ DoSimpleMsg(DSS, _('Solution state is not initialized for the active circuit!'), 8899);
end;
Result := True;
Exit;
@@ -252,6 +256,12 @@ procedure DSS_Dispose_PInteger(var p: PInteger); CDECL;
p := NIL;
end;
+procedure DSS_Dispose_PPointer(var p: PPointer); CDECL;
+begin
+ Dispose(p);
+ p := NIL;
+end;
+
procedure DSS_Dispose_PPAnsiChar(var p: PPAnsiChar; cnt: TAPISize); CDECL;
var
i: TAPISize;
@@ -315,6 +325,14 @@ function DSS_CreateArray_PPAnsiChar(var p: PPAnsiChar; cnt: PAPISize; const inco
result := PPAnsiCharArray0(p);
end;
+function DSS_CreateArray_PPointer(var p: PPointer; cnt: PAPISize; const incount: TAPISize): PPointerArray0;
+begin
+ cnt[0] := incount;
+ cnt[1] := incount;
+ p := AllocMem(incount * sizeof(Pointer));
+ result := PPointerArray0(p);
+end;
+
//------------------------------------------------------------------------------
function DSS_RecreateArray_PInteger(var p: PInteger; cnt: PAPISize; const incount: TAPISize): PIntegerArray0;
begin
@@ -349,6 +367,27 @@ procedure DSS_RecreateArray_PPAnsiChar(var res: PPAnsiCharArray0; var p: PPAnsiC
res := DSS_RecreateArray_PPAnsiChar(p, cnt, incount);
end;
+//------------------------------------------------------------------------------
+function DSS_RecreateArray_PPointer(var p: PPointer; cnt: PAPISize; const incount: TAPISize): PPointerArray0;
+begin
+ if (cnt[1] < incount) then
+ begin
+ DSS_Dispose_PPointer(p);
+ Result := DSS_CreateArray_PPointer(p, cnt, incount);
+ end
+ else
+ begin
+ cnt[0] := incount;
+ Result := PPointerArray0(p);
+ FillByte(Result^, incount * sizeof(Pointer), 0); // needs to zero it for compatibility
+ end;
+end;
+
+procedure DSS_RecreateArray_PPointer(var res: PPointerArray0; var p: PPointer; cnt: PAPISize; const incount: TAPISize);
+begin
+ res := DSS_RecreateArray_PPointer(p, cnt, incount);
+end;
+
//------------------------------------------------------------------------------
function DSS_RecreateArray_PSingle(var p: PSingle; cnt: PAPISize; const incount: TAPISize): PSingleArray0;
begin
diff --git a/src/CAPI/CAPI_Vsources.pas b/src/CAPI/CAPI_Vsources.pas
index df95864f7..deb9cdba2 100644
--- a/src/CAPI/CAPI_Vsources.pas
+++ b/src/CAPI/CAPI_Vsources.pas
@@ -53,7 +53,7 @@ function _activeObj(DSS: TDSSContext; out obj: TVsourceObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active Vsource object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['Vsource'], 8989);
end;
Exit;
end;
@@ -122,7 +122,7 @@ procedure Vsources_Set_Name(const Value: PAnsiChar); CDECL;
end
else
begin
- DoSimpleMsg(DSSPrime, 'Vsource "' + Value + '" Not Found in Active Circuit.', 77003);
+ DoSimpleMsg(DSSPrime, 'Vsource "%s" not found in Active Circuit.', [Value], 77003);
end;
end;
//------------------------------------------------------------------------------
@@ -218,7 +218,14 @@ procedure Vsources_Set_Phases(Value: Integer); CDECL;
begin
if not _activeObj(DSSPrime, elem) then
Exit;
- elem.Nphases := Value;
+
+ if Value < 1 then
+ begin
+ DoSimpleMsg(DSSPrime, '%s: Number of phases must be a positive integer!', [elem.FullName], 6568);
+ Exit;
+ end;
+ elem.FNphases := Value;
+ //TODO: missing side-effects?
end;
//------------------------------------------------------------------------------
function Vsources_Get_idx(): Integer; CDECL;
@@ -238,7 +245,7 @@ procedure Vsources_Set_idx(Value: Integer); CDECL;
pVsource := DSSPrime.VsourceClass.ElementList.Get(Value);
if pVsource = NIL then
begin
- DoSimpleMsg(DSSPrime, 'Invalid VSource index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['VSource', Value], 656565);
Exit;
end;
DSSPrime.ActiveCircuit.ActiveCktElement := pVsource;
diff --git a/src/CAPI/CAPI_WireData.pas b/src/CAPI/CAPI_WireData.pas
index db5c62462..49b9e87c8 100644
--- a/src/CAPI/CAPI_WireData.pas
+++ b/src/CAPI/CAPI_WireData.pas
@@ -8,10 +8,6 @@ interface
WireData,
ConductorData;
-type
- ConductorProps = (Rdc = 1, Rac, Runits, GMRac, GMRunits, radius, radunits, normamps, emergamps, diam, seasons, ratings, capradius);
-
-
function WireData_Get_Count(): Integer; CDECL;
function WireData_Get_First(): Integer; CDECL;
function WireData_Get_Next(): Integer; CDECL;
@@ -41,14 +37,9 @@ function WireData_Get_EmergAmps(): Double; CDECL;
procedure WireData_Set_EmergAmps(Value: Double); CDECL;
function WireData_Get_CapRadius(): Double; CDECL;
procedure WireData_Set_CapRadius(Value: Double); CDECL;
-
-
-procedure ConductorSetDefaults(prop: ConductorProps; conductor: TConductorDataObj);
-
function WireData_Get_idx(): Integer; CDECL;
procedure WireData_Set_idx(Value: Integer); CDECL;
-
implementation
uses
@@ -59,6 +50,9 @@ implementation
DSSClass,
DSSHelper;
+const
+ ConductorPropOffset = 0;
+
//------------------------------------------------------------------------------
function _activeObj(DSS: TDSSContext; out obj: TWireDataObj): Boolean; inline;
begin
@@ -72,7 +66,7 @@ function _activeObj(DSS: TDSSContext; out obj: TWireDataObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active WireData object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['WireData'], 8989);
end;
Exit;
end;
@@ -80,55 +74,6 @@ function _activeObj(DSS: TDSSContext; out obj: TWireDataObj): Boolean; inline;
Result := True;
end;
//------------------------------------------------------------------------------
-procedure ConductorSetDefaults(prop: ConductorProps; conductor: TConductorDataObj);
-begin
- {Set defaults}
- with conductor do
- begin
- case prop of
- ConductorProps.Rdc:
- if FR60 < 0.0 then
- FR60 := 1.02 * FRDC;
- ConductorProps.Rac:
- if FRDC < 0.0 then
- FRDC := FR60 / 1.02;
- ConductorProps.GMRac:
- if Fradius < 0.0 then
- Fradius := FGMR60 / 0.7788;
- ConductorProps.GMRunits:
- if FradiusUnits = 0 then
- FradiusUnits := FGMRunits;
- ConductorProps.radius:
- if FGMR60 < 0.0 then
- FGMR60 := 0.7788 * FRadius;
- ConductorProps.radunits:
- if FGMRUnits = 0 then
- FGMRunits := FradiusUnits;
- ConductorProps.normamps:
- if EmergAmps < 0.0 then
- EmergAmps := 1.5 * NormAmps;
- ConductorProps.emergamps:
- if NormAmps < 0.0 then
- NormAmps := EmergAmps / 1.5;
- ConductorProps.diam:
- if FGMR60 < 0.0 then
- FGMR60 := 0.7788 * FRadius;
- ConductorProps.capradius:
- if Fcapradius60 < 0.0 then
- Fcapradius60 := Fradius;
- end;
- {Check for critical errors}
- case prop of
- ConductorProps.GMRac:
- if (Fradius = 0.0) then
- DoSimpleMsg('Error: Radius is specified as zero for ConductorData.' + Name, 999);
- ConductorProps.radius:
- if (FGMR60 = 0.0) then
- DoSimpleMsg('Error: GMR is specified as zero for ConductorData.' + Name, 999);
- end;
- end;
-end;
-//------------------------------------------------------------------------------
function WireData_Get_Count(): Integer; CDECL;
begin
Result := 0;
@@ -155,12 +100,12 @@ function WireData_Get_Next(): Integer; CDECL;
//------------------------------------------------------------------------------
function WireData_Get_Name(): PAnsiChar; CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
Result := NIL; // signify no name
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := DSS_GetAsPAnsiChar(DSSPrime, pWireData.Name);
+ Result := DSS_GetAsPAnsiChar(DSSPrime, elem.Name);
end;
//------------------------------------------------------------------------------
procedure WireData_Set_Name(const Value: PAnsiChar); CDECL;
@@ -171,7 +116,7 @@ procedure WireData_Set_Name(const Value: PAnsiChar); CDECL;
Exit;
if not DSSPrime.WireDataClass.SetActive(Value) then
- DoSimpleMsg(DSSPrime, 'WireData "' + Value + '" Not Found in Active Circuit.', 51008);
+ DoSimpleMsg(DSSPrime, 'WireData "%s" not found in Active Circuit.', [Value], 51008);
// Still same active object if not found
end;
@@ -192,225 +137,232 @@ procedure WireData_Get_AllNames_GR(); CDECL;
//------------------------------------------------------------------------------
function WireData_Get_NormAmps(): Double; CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pWireData.NormAmps;
+ Result := elem.NormAmps;
end;
//------------------------------------------------------------------------------
procedure WireData_Set_NormAmps(Value: Double); CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pWireData.NormAmps := Value;
- ConductorSetDefaults(ConductorProps.NormAmps, pWireData);
+ elem.NormAmps := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.NormAmps))
end;
//------------------------------------------------------------------------------
function WireData_Get_EmergAmps(): Double; CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pWireData.EmergAmps;
+ Result := elem.EmergAmps;
end;
//------------------------------------------------------------------------------
procedure WireData_Set_EmergAmps(Value: Double); CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pWireData.EmergAmps := Value;
- ConductorSetDefaults(ConductorProps.EmergAmps, pWireData);
+ elem.EmergAmps := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.EmergAmps))
end;
//------------------------------------------------------------------------------
function WireData_Get_Diameter(): Double; CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pWireData.FRadius * 2.0;
+ Result := elem.FRadius * 2.0;
end;
//------------------------------------------------------------------------------
procedure WireData_Set_Diameter(Value: Double); CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pWireData.FRadius := Value / 2.0;
- ConductorSetDefaults(ConductorProps.diam, pWireData);
+ elem.FRadius := Value / 2.0;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.diam))
end;
//------------------------------------------------------------------------------
function WireData_Get_Radius(): Double; CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pWireData.FRadius;
+ Result := elem.FRadius;
end;
//------------------------------------------------------------------------------
procedure WireData_Set_Radius(Value: Double); CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pWireData.FRadius := Value;
- ConductorSetDefaults(ConductorProps.Radius, pWireData);
+ elem.FRadius := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.Radius))
end;
//------------------------------------------------------------------------------
function WireData_Get_GMRac(): Double; CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pWireData.FGMR60;
+ Result := elem.FGMR60;
end;
//------------------------------------------------------------------------------
procedure WireData_Set_GMRac(Value: Double); CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pWireData.FGMR60 := Value;
- ConductorSetDefaults(ConductorProps.GMRac, pWireData);
+ elem.FGMR60 := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.GMRac))
end;
//------------------------------------------------------------------------------
function WireData_Get_Rac(): Double; CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pWireData.FR60;
+ Result := elem.FR60;
end;
//------------------------------------------------------------------------------
procedure WireData_Set_Rac(Value: Double); CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pWireData.FR60 := Value;
- ConductorSetDefaults(ConductorProps.Rac, pWireData);
+ elem.FR60 := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.Rac))
end;
//------------------------------------------------------------------------------
function WireData_Get_Rdc(): Double; CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pWireData.FRDC;
+ Result := elem.FRDC;
end;
//------------------------------------------------------------------------------
procedure WireData_Set_Rdc(Value: Double); CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pWireData.FRDC := Value;
- ConductorSetDefaults(ConductorProps.Rdc, pWireData);
+ elem.FRDC := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.Rdc))
end;
//------------------------------------------------------------------------------
function WireData_Get_CapRadius(): Double; CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pWireData.CapRadius;
+ Result := elem.CapRadius;
end;
//------------------------------------------------------------------------------
procedure WireData_Set_CapRadius(Value: Double); CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pWireData.Fcapradius60 := Value;
- ConductorSetDefaults(ConductorProps.CapRadius, pWireData);
+ elem.Fcapradius60 := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.CapRadius))
end;
//------------------------------------------------------------------------------
function WireData_Get_GMRUnits(): Integer; CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pWireData.FGMRUnits;
+ Result := elem.FGMRUnits;
end;
//------------------------------------------------------------------------------
procedure WireData_Set_GMRUnits(Value: Integer); CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
+ prevVal: Integer;
begin
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pWireData.FGMRUnits := Value;
- ConductorSetDefaults(ConductorProps.GMRunits, pWireData);
+ prevVal := elem.FGMRUnits;
+ elem.FGMRUnits := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.GMRunits), prevVal)
end;
//------------------------------------------------------------------------------
function WireData_Get_RadiusUnits(): Integer; CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pWireData.FRadiusUnits;
+ Result := elem.FRadiusUnits;
end;
-
-//------------------------------------------------------------------------------
+//----------------his show is a genius, every contestant is so wholesome and (often unintentionally) hilarious. Eddy's enthusiasm during the Greggs round was so cute! And Jimmy is on fire as usual.--------------------------------------------------------------
procedure WireData_Set_RadiusUnits(Value: Integer); CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
+ prevVal: Integer;
begin
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pWireData.FRadiusUnits := Value;
- ConductorSetDefaults(ConductorProps.radunits, pWireData);
+
+ prevVal := elem.FRadiusUnits;
+ elem.FRadiusUnits := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.radunits), prevVal)
end;
//------------------------------------------------------------------------------
function WireData_Get_ResistanceUnits(): Integer; CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
begin
Result := 0;
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- Result := pWireData.FResistanceUnits;
+ Result := elem.FResistanceUnits;
end;
//------------------------------------------------------------------------------
procedure WireData_Set_ResistanceUnits(Value: Integer); CDECL;
var
- pWireData: TWireDataObj;
+ elem: TWireDataObj;
+ prevVal: Integer;
begin
- if not _activeObj(DSSPrime, pWireData) then
+ if not _activeObj(DSSPrime, elem) then
Exit;
- pWireData.FResistanceUnits := Value;
- ConductorSetDefaults(ConductorProps.Runits, pWireData);
+
+ prevVal := elem.FResistanceUnits;
+ elem.FResistanceUnits := Value;
+ elem.PropertySideEffects(ConductorPropOffset + ord(TConductorDataProp.Runits), prevVal)
end;
//------------------------------------------------------------------------------
function WireData_Get_idx(): Integer; CDECL;
@@ -424,7 +376,7 @@ function WireData_Get_idx(): Integer; CDECL;
procedure WireData_Set_idx(Value: Integer); CDECL;
begin
if DSSPrime.WireDataClass.ElementList.Get(Value) = NIL then
- DoSimpleMsg(DSSPrime, 'Invalid WireData index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['WireData', Value], 656565);
end;
//------------------------------------------------------------------------------
end.
diff --git a/src/CAPI/CAPI_XYCurves.pas b/src/CAPI/CAPI_XYCurves.pas
index c6fef3934..46ce45b60 100644
--- a/src/CAPI/CAPI_XYCurves.pas
+++ b/src/CAPI/CAPI_XYCurves.pas
@@ -62,7 +62,7 @@ function _activeObj(DSS: TDSSContext; out obj: TXYCurveObj): Boolean; inline;
begin
if DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSS, 'No active XYCurve object found! Activate one and retry.', 8989);
+ DoSimpleMsg(DSS, 'No active %s object found! Activate one and retry.', ['XYCurve'], 8989);
end;
Exit;
end;
@@ -116,7 +116,7 @@ procedure XYCurves_Set_Name(const Value: PAnsiChar); CDECL;
Exit;
if not DSSPrime.XYCurveClass.SetActive(Value) then
- DoSimpleMsg(DSSPrime, 'XYCurve "' + Value + '" Not Found in Active Circuit.', 51008);
+ DoSimpleMsg(DSSPrime, 'XYCurve "%s" not found in Active Circuit.', [Value], 51008);
// Still same active object if not found
end;
@@ -129,7 +129,7 @@ function XYCurves_Get_Npts(): Integer; CDECL;
Result := 0;
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51009);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51009);
Exit;
end;
@@ -144,7 +144,7 @@ procedure XYCurves_Get_Xarray(var ResultPtr: PDouble; ResultCount: PAPISize); CD
DefaultResult(ResultPtr, ResultCount);
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51013);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51013);
Exit;
end;
DSS_RecreateArray_PDouble(Result, ResultPtr, ResultCount, pXYCurve.NumPoints);
@@ -161,15 +161,17 @@ procedure XYCurves_Get_Xarray_GR(); CDECL;
procedure XYCurves_Set_Npts(Value: Integer); CDECL;
var
pXYCurve: TXYCurveObj;
-
+ prev: Integer;
begin
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51014);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51014);
Exit;
end;
- pXYCurve.NumPoints := Value;
+ prev := pXYCurve.FNumPoints;
+ pXYCurve.FNumPoints := Value;
+ pXYCurve.PropertySideEffects(ord(TXYcurveProp.npts), prev);
end;
//------------------------------------------------------------------------------
procedure XYCurves_Set_Xarray(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
@@ -180,13 +182,13 @@ procedure XYCurves_Set_Xarray(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
begin
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51015);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51015);
Exit;
end;
if (pXYCurve.NumPoints <> ValueCount) and DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSSPrime, Format('The number of values provided (%d) does not match the expected (%d).', [ValueCount, pXYCurve.NumPoints]), 183);
+ DoSimpleMsg(DSSPrime, 'The number of values provided (%d) does not match the expected (%d).', [ValueCount, pXYCurve.NumPoints], 183);
Exit;
end;
@@ -207,7 +209,7 @@ function XYCurves_Get_x(): Double; CDECL;
Result := 0;
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51010);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51010);
Exit;
end;
@@ -221,7 +223,7 @@ function XYCurves_Get_y(): Double; CDECL;
Result := 0;
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51011);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51011);
Exit;
end;
@@ -236,7 +238,7 @@ procedure XYCurves_Get_Yarray(var ResultPtr: PDouble; ResultCount: PAPISize); CD
DefaultResult(ResultPtr, ResultCount);
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51013);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51013);
Exit;
end;
DSS_RecreateArray_PDouble(Result, ResultPtr, ResultCount, pXYCurve.NumPoints);
@@ -256,7 +258,7 @@ procedure XYCurves_Set_x(Value: Double); CDECL;
begin
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51010);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51010);
Exit;
end;
@@ -269,7 +271,7 @@ procedure XYCurves_Set_y(Value: Double); CDECL;
begin
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51010);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51010);
Exit;
end;
@@ -283,13 +285,13 @@ procedure XYCurves_Set_Yarray(ValuePtr: PDouble; ValueCount: TAPISize); CDECL;
begin
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51016);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51016);
Exit;
end;
if (pXYCurve.NumPoints <> ValueCount) and DSS_CAPI_EXT_ERRORS then
begin
- DoSimpleMsg(DSSPrime, Format('The number of values provided (%d) does not match the expected (%d).', [ValueCount, pXYCurve.NumPoints]), 183);
+ DoSimpleMsg(DSSPrime, 'The number of values provided (%d) does not match the expected (%d).', [ValueCount, pXYCurve.NumPoints], 183);
Exit;
end;
@@ -309,7 +311,7 @@ function XYCurves_Get_Xscale(): Double; CDECL;
Result := 0;
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51011);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51011);
Exit;
end;
@@ -324,7 +326,7 @@ function XYCurves_Get_Xshift(): Double; CDECL;
Result := 0;
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51011);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51011);
Exit;
end;
@@ -339,7 +341,7 @@ function XYCurves_Get_Yscale(): Double; CDECL;
Result := 0;
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51011);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51011);
Exit;
end;
@@ -354,7 +356,7 @@ function XYCurves_Get_Yshift(): Double; CDECL;
Result := 0;
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51011);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51011);
Exit;
end;
@@ -368,7 +370,7 @@ procedure XYCurves_Set_Xscale(Value: Double); CDECL;
begin
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51010);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51010);
Exit;
end;
@@ -381,7 +383,7 @@ procedure XYCurves_Set_Xshift(Value: Double); CDECL;
begin
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51010);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51010);
Exit;
end;
@@ -394,7 +396,7 @@ procedure XYCurves_Set_Yscale(Value: Double); CDECL;
begin
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51010);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51010);
Exit;
end;
@@ -407,7 +409,7 @@ procedure XYCurves_Set_Yshift(Value: Double); CDECL;
begin
if not _activeObj(DSSPrime, pXYCurve) then
begin
- DoSimpleMsg(DSSPrime, 'No active XYCurve Object found.', 51010);
+ DoSimpleMsg(DSSPrime, 'No active %s object found! Activate one and retry.', ['XYCurve'], 51010);
Exit;
end;
@@ -422,7 +424,7 @@ function XYCurves_Get_idx(): Integer; CDECL;
procedure XYCurves_Set_idx(Value: Integer); CDECL;
begin
if (DSSPrime.XYCurveClass.ElementList.Get(Value) = NIL) then
- DoSimpleMsg(DSSPrime, 'Invalid XYCurve index: "' + IntToStr(Value) + '".', 656565);
+ DoSimpleMsg(DSSPrime, 'Invalid %s index: "%d".', ['XYCurve', Value], 656565);
end;
//------------------------------------------------------------------------------
procedure XYCurves_Get_AllNames(var ResultPtr: PPAnsiChar; ResultCount: PAPISize); CDECL;
diff --git a/src/CAPI/CAPI_YMatrix.pas b/src/CAPI/CAPI_YMatrix.pas
index 2426de64f..a89d2d013 100644
--- a/src/CAPI/CAPI_YMatrix.pas
+++ b/src/CAPI/CAPI_YMatrix.pas
@@ -4,7 +4,7 @@
interface
uses
- UComplex,
+ UComplex, DSSUcomplex,
Solution,
CAPI_Utils,
CAPI_Types;
@@ -18,7 +18,7 @@ procedure YMatrix_BuildYMatrixD(BuildOps, AllocateVI: Longint); CDECL;
procedure YMatrix_AddInAuxCurrents(SType: Integer); CDECL;
procedure YMatrix_getIpointer(var IvectorPtr: pNodeVarray); CDECL;
procedure YMatrix_getVpointer(var VvectorPtr: pNodeVarray); CDECL;
-function YMatrix_SolveSystem(var NodeV: pNodeVarray): Integer; CDECL;
+function YMatrix_SolveSystem(NodeV: pNodeVarray): Integer; CDECL;
procedure YMatrix_Set_SystemYChanged(arg: TAPIBoolean); CDECL;
function YMatrix_Get_SystemYChanged(): TAPIBoolean; CDECL;
@@ -44,7 +44,8 @@ implementation
Ymatrix,
KLUSolve,
DSSClass,
- DSSHelper;
+ DSSHelper,
+ SysUtils;
procedure YMatrix_GetCompressedYMatrix(factor: TAPIBoolean; var nBus, nNz: Longword; var ColPtr, RowIdxPtr: pInteger; var cValsPtr: PDouble); CDECL;
{Returns Pointers to column and row and matrix values}
@@ -58,7 +59,7 @@ procedure YMatrix_GetCompressedYMatrix(factor: TAPIBoolean; var nBus, nNz: Longw
Yhandle := DSSPrime.ActiveCircuit.Solution.hY;
if Yhandle <= 0 then
begin
- DoSimpleMsg(DSSPrime, 'Y Matrix not Built.', 222);
+ DoSimpleMsg(DSSPrime, _('Y Matrix was not built.'), 222);
Exit;
end;
@@ -168,12 +169,12 @@ procedure YMatrix_getVpointer(var VvectorPtr: pNodeVarray); CDECL;
VVectorPtr := DSSPrime.ActiveCircuit.Solution.NodeV;
end;
-function YMatrix_SolveSystem(var NodeV: pNodeVarray): Integer; CDECL;
+function YMatrix_SolveSystem(NodeV: pNodeVarray): Integer; CDECL;
begin
Result := 0;
if InvalidCircuit(DSSPrime) then
Exit;
- if (@NodeV <> NIL) then
+ if (NodeV <> NIL) then
Result := DSSPrime.ActiveCircuit.Solution.SolveSystem(NodeV)
else
Result := DSSPrime.ActiveCircuit.Solution.SolveSystem(DSSPrime.ActiveCircuit.Solution.NodeV);
@@ -227,7 +228,7 @@ procedure YMatrix_SetGeneratordQdV(); CDECL;
except
ON E: EEsolv32Problem do
begin
- DoSimpleMsg(DSSPrime, 'From DoPFLOWsolution.SetGeneratordQdV: ' + CRLF + E.Message + CheckYMatrixforZeroes(DSSPrime), 7073);
+ DoSimpleMsg(DSSPrime, 'From DoPFLOWsolution.SetGeneratordQdV: %s%s', [E.Message, CheckYMatrixforZeroes(DSSPrime)], 7073);
end;
end;
end;
@@ -247,9 +248,9 @@ procedure YMatrix_Set_SolverOptions(opts: UInt64); CDECL;
SolverOptions := opts;
{$IFDEF DSS_CAPI_INCREMENTAL_Y}
if hY <> 0 then
- KLUSolve.SetOptions(hY, SolverOptions and $FFFFFFFF);
+ KLUSolve.SetOptions(hY, SolverOptions and $FFFFFF);
{$ELSE}
- DoSimpleMsg('This version of DSS C-API was not compiled with extended solver options.', 7074);
+ DoSimpleMsg(_('This version of DSS C-API was not compiled with extended solver options.'), 7074);
{$ENDIF}
end;
end;
diff --git a/src/CAPI/CAPI_ZIP.pas b/src/CAPI/CAPI_ZIP.pas
new file mode 100644
index 000000000..2e466a448
--- /dev/null
+++ b/src/CAPI/CAPI_ZIP.pas
@@ -0,0 +1,122 @@
+unit CAPI_ZIP;
+
+interface
+
+uses
+ CAPI_Utils,
+ CAPI_Types;
+
+procedure ZIP_Open(const FileName: PAnsiChar); CDECL;
+procedure ZIP_Close(); CDECL;
+procedure ZIP_Redirect(const FileName: PAnsiChar); CDECL;
+procedure ZIP_Extract(var ResultPtr: PByte; ResultCount: PAPISize; const FileName: PAnsiChar); CDECL;
+procedure ZIP_Extract_GR(const FileName: PAnsiChar); CDECL;
+function ZIP_Contains(const Name: PAnsiChar): TAPIBoolean; CDECL;
+procedure ZIP_List(var ResultPtr: PPAnsiChar; ResultCount: PAPISize; const RegExp: PAnsiChar); CDECL;
+
+implementation
+
+uses
+ CAPI_Constants,
+ Classes,
+ DSSGlobals,
+ Executive,
+ SysUtils,
+ ExecHelper,
+ DSSClass,
+ DSSHelper,
+ RegExpr,
+ Contnrs,
+ Zipper;
+
+//------------------------------------------------------------------------------
+procedure ZIP_Open(const FileName: PAnsiChar); CDECL;
+begin
+ DSSPrime.DSSExecutive.ZipOpen(FileName);
+end;
+//------------------------------------------------------------------------------
+procedure ZIP_Close(); CDECL;
+begin
+ DSSPrime.DSSExecutive.ZipClose();
+end;
+//------------------------------------------------------------------------------
+procedure ZIP_Redirect(const FileName: PAnsiChar); CDECL;
+begin
+ DSSPrime.DSSExecutive.ZipRedirect(FileName);
+end;
+//------------------------------------------------------------------------------
+procedure ZIP_Extract(var ResultPtr: PByte; ResultCount: PAPISize; const FileName: PAnsiChar); CDECL;
+begin
+ DSSPrime.DSSExecutive.ZipExtract(ResultPtr, ResultCount, FileName);
+end;
+
+procedure ZIP_Extract_GR(const FileName: PAnsiChar); CDECL;
+begin
+ ZIP_Extract(DSSPrime.GR_DataPtr_PByte, @DSSPrime.GR_Counts_PByte[0], FileName);
+end;
+//------------------------------------------------------------------------------
+function ZIP_Contains(const Name: PAnsiChar): TAPIBoolean; CDECL;
+var
+ Hashes: TFPHashList = NIL;
+begin
+ Result := False;
+ if not DSSPrime.DSSExecutive.ZipHashes(Hashes) then
+ begin
+ DoSimpleMsg(DSSPrime, _('No ZIP file is open.'), 89891);
+ Exit;
+ end;
+ Result := Integer(Hashes.Find(Name)) > 0;
+end;
+//------------------------------------------------------------------------------
+procedure ZIP_List(var ResultPtr: PPAnsiChar; ResultCount: PAPISize; const RegExp: PAnsiChar); CDECL;
+var
+ Result: PPAnsiCharArray0;
+ unzipper: TUnZipper;
+ s: String;
+ i: Integer;
+ rex: TRegExpr = NIL;
+begin
+ DefaultResult(ResultPtr, ResultCount);
+ unzipper := TUnZipper(DSSPrime.unzipper);
+ if unzipper = NIL then
+ begin
+ DoSimpleMsg(DSSPrime, _('No ZIP file is open.'), 89892);
+ Exit;
+ end;
+ if RegExp <> NIL then
+ begin
+ s := RegExp;
+ end
+ else
+ s := '';
+
+ Result := DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, unzipper.Entries.Count);
+ if s = '' then
+ begin
+ for i := 0 to unzipper.Entries.Count - 1 do
+ Result[i] := DSS_CopyStringAsPChar(unzipper.Entries[i].ArchiveFileName);
+
+ Exit;
+ end;
+
+ try
+ rex := TRegExpr.Create();
+ rex.ModifierI := False;
+ rex.ModifierM := False;
+ rex.ModifierS := True;
+ rex.Expression := s;
+ ResultCount[0] := 0;
+ for i := 0 to unzipper.Entries.Count - 1 do
+ begin
+ if rex.Exec(unzipper.Entries[i].ArchiveFileName) then
+ begin
+ Result[ResultCount[0]] := DSS_CopyStringAsPChar(unzipper.Entries[i].ArchiveFileName);
+ inc(ResultCount[0]);
+ end;
+ end;
+ finally
+ FreeAndNil(rex);
+ end;
+end;
+//------------------------------------------------------------------------------
+end.
diff --git a/src/Common/AutoAdd.pas b/src/Common/AutoAdd.pas
index 16fb4ac90..10a6f178e 100644
--- a/src/Common/AutoAdd.pas
+++ b/src/Common/AutoAdd.pas
@@ -6,23 +6,16 @@
All rights reserved.
----------------------------------------------------------
}
-{ Unit for processing the AutoAdd Solution FUNCTIONs
-
- Note: Make sure this class in instantiated after energymeter class
-
- There is one of these per circuit
-
- 6/11/00 - reorganized object
- 6/14/00 - resolved sign issue with normal and Newton solution in AddCurrents
- 9/13/03 - Modified to use pu improvement in losses and EEN instead of kW
-}
-
-{$M+}
+// Unit for processing the AutoAdd Solution FUNCTIONs
+//
+// Note: Make sure this class in instantiated after energymeter class
+//
+// There is one of these per circuit
interface
uses
- uComplex,
+ UComplex, DSSUcomplex,
EnergyMeter,
HashList,
Arraydef,
@@ -58,12 +51,8 @@ TAutoAdd = class(TObject)
function GetUniqueGenName: String;
function GetUniqueCapName: String;
-
- PROTECTED
-
PUBLIC
-
- {Autoadd mode Variables}
+ // Autoadd mode Variables
GenkW,
GenPF,
Genkvar,
@@ -84,25 +73,19 @@ TAutoAdd = class(TObject)
function Solve: Integer; // Automatically add caps or generators
property WeightedLosses: Double READ Get_WeightedLosses;
-
- PUBLISHED
-
end;
implementation
uses
+ BufStream,
DSSClassDefs,
DSSGlobals,
PDElement,
Utilities,
SysUtils,
Executive,
-{$IFDEF FPC}
CmdForms,
-{$ELSE}
- DSSForms,
-{$ENDIF}
{ProgressForm, Forms,} Solution,
DSSHelper;
@@ -134,8 +117,6 @@ constructor TAutoAdd.Create(dssContext: TDSSContext);
LastAddedCapacitor := 0;
ModeChanged := TRUE;
-
-
end;
destructor TAutoAdd.Destroy;
@@ -161,7 +142,6 @@ procedure TAutoAdd.MakeBusList;
FBusListCreatedHere: Boolean;
begin
-
if (BusIdxListCreated) then
ReallocMem(BusIdxList, 0);
@@ -198,7 +178,6 @@ procedure TAutoAdd.MakeBusList;
pMeter := DSS.ActiveCircuit.EnergyMeters.First;
while pMeter <> NIL do
begin
-
if pMeter.BranchList <> NIL then
begin
PDElem := pMeter.BranchList.First;
@@ -246,7 +225,6 @@ function TAutoAdd.Get_WeightedLosses: Double;
begin
-
ComputekWLosses_EEN;
if DSS.ActiveCircuit.EnergyMeters.Count = 0 then
@@ -278,17 +256,16 @@ procedure TAutoAdd.AppendToFile(const WhichFile, S: String);
FName := DSS.OutputDirectory + DSS.CircuitName_ + 'AutoAdded' + WhichFile + '.txt';
if FileExists(FName) then
begin
- F := TFileStream.Create(Fname, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(Fname, fmOpenReadWrite);
F.Seek(0, soEnd);
end
else
- F := TFileStream.Create(Fname, fmCreate);
+ F := TBufferedFileStream.Create(Fname, fmCreate);
FSWriteLn(F, S);
except
On E: EXCEPTion do
- DoSimpleMsg(DSS, 'Error TRYing to append to ' + Fname + CRLF +
- E.Message, 438);
+ DoSimpleMsg(DSS, 'Error trying to append to "%s": %s', [Fname, E.Message], 438);
end;
if F <> nil then
F.Free();
@@ -304,7 +281,6 @@ function TAutoAdd.GetUniqueGenName: String;
Done: Boolean;
begin
-
repeat
Done := TRUE;
Inc(LastAddedGenerator);
@@ -314,7 +290,6 @@ function TAutoAdd.GetUniqueGenName: String;
until Done;
Result := TrialName;
-
end;
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -337,7 +312,6 @@ function TAutoAdd.GetUniqueCapName: String;
until Done;
Result := TrialName;
-
end;
@@ -389,7 +363,6 @@ function TAutoAdd.Solve: Integer; // Automatically add caps or generators
with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
begin
-
if (LoadModel = ADMITTANCE) then
begin
LoadModel := POWERFLOW;
@@ -421,7 +394,7 @@ function TAutoAdd.Solve: Integer; // Automatically add caps or generators
{Start up Log File}
- FLog := TFileStream.Create(DSS.OutputDirectory + DSS.CircuitName_ + 'AutoAddLog.CSV', fmCreate);
+ FLog := TBufferedFileStream.Create(DSS.OutputDirectory + DSS.CircuitName_ + 'AutoAddLog.csv', fmCreate);
FSWriteLn(FLog, '"Bus", "Base kV", "kW Losses", "% Improvement", "kW UE", "% Improvement", "Weighted Total", "Iterations"');
@@ -471,14 +444,12 @@ function TAutoAdd.Solve: Integer; // Automatically add caps or generators
for i := 1 to BusIdxListSize do
begin
-
Inc(ProgressCount);
BusIndex := BusIdxList^[i];
if BusIndex > 0 then
begin
-
TestBus := BusList.NameOfIndex(BusIndex);
// ProgressFormCaption( 'Testing bus ' + TestBus);
if ((ProgressCount mod 20) = 0) or (i = BusIdxListSize) then
@@ -539,7 +510,6 @@ function TAutoAdd.Solve: Integer; // Automatically add caps or generators
if MinLossBus > 0 then
with DSS.DSSExecutive do
begin
-
if MinBusPhases >= 3 then
kVrat := Buses^[MinLossBus].kVBase * SQRT3
else
@@ -553,7 +523,7 @@ function TAutoAdd.Solve: Integer; // Automatically add caps or generators
Format('! Factor = %-g (%-.3g, %-.3g)', [MaxLossImproveFactor, LossWeight, UEWeight]);
Command := CommandString; // Defines Generator
- // AppEnd this command to '...AutoAddedGenerators.Txt'
+ // AppEnd this command to '...AutoAddedGenerators.txt'
AppendToFile('Generators', CommandString);
SolveSnap; // Force rebuilding of lists
@@ -573,7 +543,6 @@ function TAutoAdd.Solve: Integer; // Automatically add caps or generators
CAPADD:
begin
-
MinLossBus := 0; // null string
MaxLossImproveFactor := -1.0e50; // Some very large number
MinBusPhases := 3;
@@ -590,7 +559,6 @@ function TAutoAdd.Solve: Integer; // Automatically add caps or generators
for i := 1 to BusIdxListSize do
begin
-
Inc(ProgressCount);
{Make sure testbus is actually in the circuit}
BusIndex := BusIdxList^[i];
@@ -656,7 +624,6 @@ function TAutoAdd.Solve: Integer; // Automatically add caps or generators
if MinLossBus > 0 then
with DSS.DSSExecutive do
begin
-
if MinBusPhases >= 3 then
kVrat := Buses^[MinLossBus].kVBase * SQRT3
else
@@ -669,7 +636,7 @@ function TAutoAdd.Solve: Integer; // Automatically add caps or generators
', kv=' + Format('%-g', [kVrat]);
Command := CommandString; // Defines capacitor
- // AppEnd this command to 'DSSAutoAddedCapacitors.Txt'
+ // AppEnd this command to 'DSSAutoAddedCapacitors.txt'
AppendToFile('Capacitors', CommandString);
@@ -704,13 +671,12 @@ procedure TAutoAdd.AddCurrents(SolveType: Integer);
Nref: Integer;
begin
-
case AddType of
GENADD:
with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
begin
- {For buses with voltage <> 0, add into aux current array}
+ // For buses with voltage <> 0, add into aux current array
for i := 1 to Phases do
begin
Nref := Buses^[BusIndex].GetRef(i);
@@ -718,12 +684,12 @@ procedure TAutoAdd.AddCurrents(SolveType: Integer);
begin // add in only non-ground currents
BusV := NodeV^[Nref];
if (BusV.re <> 0.0) or (BusV.im <> 0.0) then
- {Current INTO the system network}
+ // Current INTO the system network
case SolveType of
NEWTONSOLVE:
- Caccum(Currents^[NRef], Cnegate(Conjg(Cdiv(GenVA, BusV)))); // Terminal Current
+ Currents^[NRef] -= cong(GenVA / BusV); // Terminal Current
NORMALSOLVE:
- Caccum(Currents^[NRef], Conjg(Cdiv(GenVA, BusV))); // Injection Current
+ Currents^[NRef] += cong(GenVA / BusV); // Injection Current
end;
end;
end;
@@ -732,8 +698,7 @@ procedure TAutoAdd.AddCurrents(SolveType: Integer);
CAPADD:
with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
begin
-
- {For buses with voltage <> 0, add into aux current array}
+ // For buses with voltage <> 0, add into aux current array
for i := 1 to Phases do
begin
Nref := Buses^[BusIndex].GetRef(i);
@@ -744,9 +709,9 @@ procedure TAutoAdd.AddCurrents(SolveType: Integer);
{Current INTO the system network}
case SolveType of
NEWTONSOLVE:
- Caccum(Currents^[NRef], Cmul(Cmplx(0.0, Ycap), BusV)); // Terminal Current
+ Currents^[NRef] += Cmplx(0.0, Ycap) * BusV; // Terminal Current
NORMALSOLVE:
- Caccum(Currents^[NRef], Cmul(Cmplx(0.0, -Ycap), BusV)); // Injection Current
+ Currents^[NRef] += Cmplx(0.0, -Ycap) * BusV; // Injection Current
end; // Constant Y model
end;
end;
@@ -761,10 +726,8 @@ procedure TAutoAdd.ComputekWLosses_EEN;
pMeter: TEnergyMeterObj;
begin
-
if DSS.ActiveCircuit.EnergyMeters.Count = 0 then
begin
-
// No energymeters in circuit
// Just go by total system losses
kWLosses := DSS.ActiveCircuit.Losses.re * 0.001;
@@ -778,11 +741,9 @@ procedure TAutoAdd.ComputekWLosses_EEN;
with DSS.ActiveCircuit do
begin
-
pMeter := DSS.ActiveCircuit.Energymeters.First;
while pMeter <> NIL do
begin
-
kWLosses := kWLosses + SumSelectedRegisters(pMeter, LossRegs, NumLossRegs);
kWEEN := kWEEN + SumSelectedRegisters(pMeter, UEregs, NumUEregs);
@@ -790,8 +751,6 @@ procedure TAutoAdd.ComputekWLosses_EEN;
end;
end;
end;
-
-
end;
procedure TAutoAdd.SetBaseLosses;
diff --git a/src/Common/Bus.pas b/src/Common/Bus.pas
index c93212518..8e6c274b2 100644
--- a/src/Common/Bus.pas
+++ b/src/Common/Bus.pas
@@ -7,36 +7,30 @@
----------------------------------------------------------
}
-{
- 2/4/03 added Zsc and Zsc1, Zsc0 properties
-}
-
interface
uses
ArrayDef,
- uComplex,
+ UComplex, DSSUcomplex,
uCMatrix,
NamedObject,
DSSClass,
DSSObject;
type
-
-
TDSSBus = class(TNamedObject)
PRIVATE
FNumNodesThisBus: SmallInt;
Nodes: pIntegerArray;
Allocation: SmallInt;
- RefNo: pIntegerArray;
procedure AddANode;
function Get_Zsc0: Complex;
function Get_Zsc1: Complex;
PUBLIC
+ RefNo: pIntegerArray;
VBus,
BusCurrent: pComplexArray;
@@ -52,7 +46,7 @@ TDSSBus = class(TNamedObject)
BusChecked,
Keep: Boolean; // Flag for general use in bus searches
- // ***** Reliability Variables
+ // ***** Reliability Variables
BusFltRate: Double; // Accumulated failure rate downstream from this bus faults per year
Bus_Num_Interrupt: Double; // Number of interruptions this bus per year
Bus_Int_Duration: Double; // Avg Annual Interruption duration for this bus
@@ -66,8 +60,7 @@ TDSSBus = class(TNamedObject)
destructor Destroy; OVERRIDE;
procedure AllocateBusQuantities;
- procedure AllocateBusVoltages;
- procedure AllocateBusCurrents;
+ procedure AllocateBusState;
function Add(Circuit: TNamedObject; NodeNum: SmallInt): Integer;
function Find(NodeNum: SmallInt): Integer; // Returns reference num for node by node number
@@ -81,7 +74,7 @@ TDSSBus = class(TNamedObject)
end;
- // Bus Collection
+ // Bus Collection
pTBusArray = ^TBusArray;
TBusArray = array[1..10] of TDSSBus;
@@ -127,16 +120,14 @@ constructor TDSSBus.Create(dssContext: TDSSContext);
destructor TDSSBus.Destroy;
begin
- ReallocMem(Nodes, 0);
- ReallocMem(RefNo, 0);
+ FreeMem(Nodes);
+ FreeMem(RefNo);
if Ysc <> NIL then
Ysc.Free;
if Zsc <> NIL then
Zsc.Free;
- if VBus <> NIL then
- Reallocmem(VBus, 0);
- if BusCurrent <> NIL then
- Reallocmem(BusCurrent, 0);
+ FreeMem(VBus);
+ FreeMem(BusCurrent);
inherited Destroy;
end;
@@ -156,10 +147,8 @@ function TDSSBus.Add(Circuit: TNamedObject; NodeNum: SmallInt): Integer;
begin
if NodeNum = 0 then
Result := 0
-
else
begin
-
Result := Find(NodeNum);
if Result = 0 then
begin
@@ -177,7 +166,6 @@ function TDSSBus.Add(Circuit: TNamedObject; NodeNum: SmallInt): Integer;
end;
end;
-
function TDSSBus.Find(NodeNum: SmallInt): Integer;
// Returns reference number
var
@@ -194,7 +182,6 @@ function TDSSBus.Find(NodeNum: SmallInt): Integer;
Result := 0;
end;
-
function TDSSBus.GetRef(NodeIndex: Integer): Integer;
begin
Result := 0;
@@ -221,16 +208,14 @@ procedure TDSSBus.AllocateBusQuantities;
Ysc := Tcmatrix.CreateMatrix(FNumNodesThisBus);
Zsc := Tcmatrix.CreateMatrix(FNumNodesThisBus);
Zsc012 := Tcmatrix.CreateMatrix(3); // can only be 3x3 -- 0, 1, 2
- AllocateBusVoltages;
- AllocateBusCurrents;
-
+ AllocateBusState;
end;
function TDSSBus.Get_Zsc0: Complex;
// = Zs + 2 Zm
begin
if Assigned(Zsc) then
- Result := Cadd(Zsc.AvgDiagonal, CmulReal(Zsc.AvgOffDiagonal, 2.0))
+ Result := Zsc.AvgDiagonal + Zsc.AvgOffDiagonal * 2
else
Result := cZERO;
end;
@@ -238,12 +223,10 @@ function TDSSBus.Get_Zsc0: Complex;
function TDSSBus.Get_Zsc1: Complex;
// = Zs-Zm
begin
-
if Assigned(Zsc) then
- Result := Csub(Zsc.AvgDiagonal, Zsc.AvgOffDiagonal)
+ Result := Zsc.AvgDiagonal - Zsc.AvgOffDiagonal
else
Result := cZERO;
-
end;
function TDSSBus.FindIdx(NodeNum: SmallInt): Integer;
@@ -260,25 +243,14 @@ function TDSSBus.FindIdx(NodeNum: SmallInt): Integer;
end;
end;
Result := 0;
-
-end;
-
-procedure TDSSBus.AllocateBusVoltages;
-var
- i: Integer;
-begin
- Reallocmem(VBus, Sizeof(VBus^[1]) * FNumNodesThisBus);
- for i := 1 to FNumNodesThisBus do
- VBus^[i] := CZERO;
end;
-procedure TDSSBus.AllocateBusCurrents;
-var
- i: Integer;
+procedure TDSSBus.AllocateBusState;
begin
- Reallocmem(BusCurrent, Sizeof(BusCurrent^[1]) * FNumNodesThisBus);
- for i := 1 to FNumNodesThisBus do
- BusCurrent^[i] := CZERO;
+ FreeMem(VBus);
+ FreeMem(BusCurrent);
+ VBus := AllocMem(Sizeof(Complex) * FNumNodesThisBus);
+ BusCurrent := AllocMem(Sizeof(Complex) * FNumNodesThisBus);
end;
end.
diff --git a/src/Common/Circuit.pas b/src/Common/Circuit.pas
index e781afec9..9772c7556 100644
--- a/src/Common/Circuit.pas
+++ b/src/Common/Circuit.pas
@@ -1,4 +1,5 @@
unit Circuit;
+
{
----------------------------------------------------------
Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
@@ -6,585 +7,606 @@
----------------------------------------------------------
}
-{
- Change Log
- 10-12-99 Added DuplicatesAllowed and ZonesLocked
- 10-24-99 Added Losses Property
- 12-15-99 Added Default load shapes and generator dispatch reference
- 4-17=00 Add Loads List
- 5-30-00 Added Positive Sequence Flag
- 8-24-00 Added PriceCurve stuff Updated 3-6-11
- 8-1-01 Modified Compute Capacity to report up to loadmult=1
- 9-25-15 Fixed broken repository
-}
-
-{$WARN UNIT_PLATFORM OFF}
-
interface
-USES
- Classes, Solution, SysUtils, ArrayDef, HashList, DSSPointerList, CktElement,
- DSSClass, Bus, LoadShape, PriceShape, ControlQueue, uComplex,
- AutoAdd, EnergyMeter, NamedObject, CktTree, MeTIS_Exec,
- Monitor, PCClass, PDClass, math, Sparse_Math, Process
+uses
+ Classes,
+ Solution,
+ SysUtils,
+ ArrayDef,
+ HashList,
+ DSSPointerList,
+ CktElement,
+ DSSClass,
+ Bus,
+ LoadShape,
+ PriceShape,
+ ControlQueue,
+ UComplex, DSSUcomplex,
+ AutoAdd,
+ EnergyMeter,
+ NamedObject,
+ CktTree,
+ MeTIS_Exec,
+ Monitor,
+ PCClass,
+ PDClass,
+ math,
+ Sparse_Math,
+ Process
{$IFDEF DSS_CAPI_PM}
- , syncobjs
+ ,
+ syncobjs
{$ENDIF}
- ;
-
+ ;
-TYPE
+type
TReductionStrategy = (rsDefault, rsShortlines, {rsTapEnds,} rsMergeParallel, rsBreakLoop, rsDangling, rsSwitches, rsLaterals);
- // for adding markers to Plot
+ // for adding markers to Plot
TBusMarker = class(TObject)
// Must be defined before calling circuit plot
- private
-
- public
- BusName: String;
- AddMarkerColor: {$IFNDEF FPC}Tcolor{$ELSE}Integer{$ENDIF};
- AddMarkerCode,
- AddMarkerSize: Integer;
-
- constructor Create;
- destructor Destroy; override;
- end;
-
- TDSSCircuit = CLASS(TNamedObject)
- Private
- NodeBuffer :pIntegerArray;
- NodeBufferMax :Integer;
- FBusNameRedefined :Boolean;
- FActiveCktElement :TDSSCktElement;
- FCaseName :String;
-
- // Temp arrays for when the bus swap takes place
- SavedBuses :pTBusArray;
- SavedBusNames :pStringArray;
- SavedNumBuses :Integer;
- FLoadMultiplier :Double; // global multiplier for every load
-
- AbortBusProcess :Boolean;
-
- Branch_List : TCktTree; // topology from the first source, lazy evaluation
- BusAdjPC, BusAdjPD : TAdjArray; // bus adjacency lists of PD and PC elements
-
- Procedure AddABus;
- Procedure AddANodeBus;
- Function AddBus(const BusName:String; NNodes:Integer):Integer;
- Procedure Set_ActiveCktElement(Value:TDSSCktElement);
- Procedure Set_BusNameRedefined(Value:Boolean);
- Function Get_Losses:Complex; //Total Circuit losses
- Procedure Set_LoadMultiplier(Value :Double);
- Procedure SaveBusInfo;
- Procedure RestoreBusInfo;
-
- Function SaveMasterFile:Boolean;
- Function SaveDSSObjects:Boolean;
- Function SaveFeeders:Boolean;
- Function SaveBusCoords:Boolean;
- Function SaveVoltageBases:Boolean;
-
- procedure ReallocDeviceList;
- procedure Set_CaseName(const Value: String);
-
- function Get_Name:String;
-
-
-
-
- Public
- DSS: TDSSContext;
-
- ActiveBusIndex :Integer;
- Fundamental :Double; // fundamental and default base frequency
-
- Control_BusNameRedefined :Boolean; // Flag for use by control elements to detect redefinition of buses
-
- BusList,
- AutoAddBusList: TBusHashListType;
- DeviceList :THashList;
-
- // lists of pointers to different elements by class
- Faults,
- PDElements,
- PCElements,
- DSSControls,
- Sources,
- MeterElements,
- Sensors,
- Monitors,
- EnergyMeters,
- Generators,
- StorageElements,
- PVSystems,
- Substations,
- Transformers,
- CapControls,
- RegControls,
- Lines,
- Loads,
- ShuntCapacitors,
- Reactors, // added for CIM XML export
- Relays, // added for CIM XML export
- Fuses, // added for CIM XML export
- Reclosers, // added for CIM XML export
- SwtControls : TDSSPointerList;
- CktElements : TDSSPointerList;
+ PRIVATE
+
+ PUBLIC
+ BusName: String;
+ AddMarkerColor: Integer;
+ AddMarkerCode,
+ AddMarkerSize: Integer;
+
+ constructor Create;
+ destructor Destroy; OVERRIDE;
+ end;
+
+ TDSSCircuit = class(TNamedObject)
+ PRIVATE
+ NodeBuffer: pIntegerArray;
+ NodeBufferMax: Integer;
+ FBusNameRedefined: Boolean;
+ FActiveCktElement: TDSSCktElement;
+ FCaseName: String;
+
+ // Temp arrays for when the bus swap takes place
+ SavedBuses: pTBusArray;
+ SavedBusNames: pStringArray;
+ SavedNumBuses: Integer;
+ FLoadMultiplier: Double; // global multiplier for every load
+
+ AbortBusProcess: Boolean;
+
+ Branch_List: TCktTree; // topology from the first source, lazy evaluation
+ BusAdjPC, BusAdjPD: TAdjArray; // bus adjacency lists of PD and PC elements
+
+ procedure AddABus;
+ procedure AddANodeBus;
+ function AddBus(const BusName: String; NNodes: Integer): Integer;
+ procedure Set_ActiveCktElement(Value: TDSSCktElement);
+ procedure Set_BusNameRedefined(Value: Boolean);
+ function Get_Losses: Complex; //Total Circuit losses
+ procedure Set_LoadMultiplier(Value: Double);
+ procedure SaveBusInfo;
+ procedure RestoreBusInfo;
+
+ function SaveMasterFile: Boolean;
+ function SaveDSSObjects: Boolean;
+ function SaveFeeders: Boolean;
+ function SaveBusCoords: Boolean;
+ function SaveVoltageBases: Boolean;
+
+ procedure ReallocDeviceList;
+ procedure Set_CaseName(const Value: String);
+
+ function Get_Name: String;
+
+ PUBLIC
+ DSS: TDSSContext;
+
+ ActiveBusIndex: Integer;
+ Fundamental: Double; // fundamental and default base frequency
+
+ Control_BusNameRedefined: Boolean; // Flag for use by control elements to detect redefinition of buses
+
+ BusList,
+ AutoAddBusList: TBusHashListType;
+ DeviceList: THashList;
+
+ // lists of pointers to different elements by class
+ Faults,
+ PDElements,
+ PCElements,
+ DSSControls,
+ Sources,
+ MeterElements,
+ Sensors,
+ Monitors,
+ EnergyMeters,
+ Generators,
+ StorageElements,
+ PVSystems,
+ Substations,
+ Transformers,
+ AutoTransformers,
+ CapControls,
+ RegControls,
+ Lines,
+ Loads,
+ ShuntCapacitors,
+ Reactors, // added for CIM XML export
+ Relays, // added for CIM XML export
+ Fuses, // added for CIM XML export
+ Reclosers, // added for CIM XML export
+ InvControls,
+ ExpControls,
+ SwtControls: TDSSPointerList;
+ CktElements: TDSSPointerList;
{$IFDEF DSS_CAPI_INCREMENTAL_Y}
- IncrCktElements : TDSSPointerList;
+ IncrCktElements: TDSSPointerList;
{$ENDIF}
-
- ControlQueue:TControlQueue;
-
- Solution :TSolutionObj;
- AutoAddObj :TAutoAdd;
-
- // For AutoAdd stuff
- UEWeight,
- LossWeight :Double;
-
- NumUEregs,
- NumLossRegs :Integer;
- Ueregs,
- LossRegs :pIntegerArray;
-
- CapacityStart,
- CapacityIncrement: Double;
-
- TrapezoidalIntegration, // flag for trapezoidal integration
- LogEvents :Boolean;
-
- LoadDurCurve:String;
- LoadDurCurveObj:TLoadShapeObj;
- PriceCurve:String;
- PriceCurveObj:TPriceShapeObj;
-
- NumDevices, NumBuses, NumNodes:Integer;
- MaxDevices, MaxBuses, MaxNodes:Integer;
- IncDevices, IncBuses, IncNodes:Integer;
-
- // Variables for the tearing Algorithm
-
- Coverage, // Used for the user to stablish the coverage for the algorithm
- Actual_Coverage : Double; // Indicates the actual coverage of the circuit after running the tearing algorithm
- Longest_paths : Array of Integer; //Stores the coordinates of the longest paths in the circuit
- Path_Idx : Array of integer; //Stores the indexes from where the areas where formed on the linearized graph
- Buses_Covered : Array of integer; //Stores the number of buses (estimated - 1 quadrant) per path
- Path_Size : Array of Integer; //Stores the estimated size of each path
- New_Graph : Array of Integer; //Stores the latest weighted graph
- Num_SubCkts : Integer; // Stores the number of subcircuits for tearing the circuit when executing the "tear_Circuit" command
- Link_Branches : Array of String; // Stores the names of the Link branches for Diakoptics
- PConn_Names : Array of String; // Stores the names of the buses (bus1) of the link branches
- PConn_Voltages : Array of Double; // Stores the voltages at the point of connection of the subcircuits
- Locations : Array of Integer; // Stores the indexes of the locations
- BusZones : Array of String;
-
- // Variables for Diakoptics
- //TODO: migrate TSparse_Complex to KLUSolveX (most functionality already present in Eigen)
- ContoursT : TSparse_Complex; // Contours matrix transposed
- Contours : TSparse_Complex; // Contours matrix
- ZLL : TSparse_Complex; // Link branch matrix
- ZCT : TSparse_Complex; // The transformation matrix (to go from one to other domain)
- ZCC : TSparse_Complex; // Interconnections matrix
- Y4 : TSparse_Complex; // The inverse of the interconnections matrix
- V_0 : TSparse_Complex; // The voltages of the partial solutions
-
- Ic : TSparse_Complex; // The complementary Currents vector
{$IFDEF DSS_CAPI_PM}
- LockIc : TCriticalSection;
+ LockIc: TCriticalSection;
+{$ENDIF}
+
+ ControlQueue: TControlQueue;
+
+ Solution: TSolutionObj;
+ AutoAddObj: TAutoAdd;
+
+ // For AutoAdd stuff
+ UEWeight,
+ LossWeight: Double;
+
+ NumUEregs,
+ NumLossRegs: Integer;
+ Ueregs,
+ LossRegs: pIntegerArray;
+
+ CapacityStart,
+ CapacityIncrement: Double;
+
+ TrapezoidalIntegration,
+ LogEvents: Boolean;
+
+ LoadDurCurve: String;
+ LoadDurCurveObj: TLoadShapeObj;
+ PriceCurve: String;
+ PriceCurveObj: TPriceShapeObj;
+
+ NumDevices, NumBuses, NumNodes: Integer;
+ MaxDevices, MaxBuses, MaxNodes: Integer;
+ IncDevices, IncBuses, IncNodes: Integer;
+
+ // Bus and Node stuff
+ Buses: pTBusArray;
+ MapNodeToBus: pTNodeBusArray;
+
+ // Flags
+ Issolved: Boolean;
+ DuplicatesAllowed: Boolean;
+ ZonesLocked: Boolean;
+ MeterZonesComputed: Boolean;
+ PositiveSequence: Boolean; // Model is to be interpreted as Pos seq
+ NeglectLoadY: Boolean;
+
+ // Voltage limits
+ NormalMinVolts,
+ NormalMaxVolts,
+ EmergMaxVolts,
+ EmergMinVolts: Double; //per unit voltage restraints for this circuit
+ LegalVoltageBases: pDoubleArray;
+
+ // Global circuit multipliers
+ GeneratorDispatchReference,
+ DefaultGrowthFactor,
+ DefaultGrowthRate,
+ GenMultiplier, // global multiplier for every generator
+ HarmMult: Double;
+ DefaultHourMult: Complex;
+
+ PriceSignal: Double; // price signal for entire circuit
+
+ // EnergyMeter Totals
+ RegisterTotals: TRegisterArray;
+
+ DefaultDailyShapeObj,
+ DefaultYearlyShapeObj: TLoadShapeObj;
+
+ CurrentDirectory: String;
+
+ ReductionStrategy: TReductionStrategy;
+ ReductionZmag: Double;
+ ReduceLateralsKeepLoad: Boolean;
+ ReductionStrategyString: String;
+
+ PctNormalFactor: Double;
+
+ // ------Plot Marker Circuit Globals---------
+ NodeMarkerCode: Integer;
+ NodeMarkerWidth: Integer;
+ SwitchMarkerCode: Integer;
+
+ TransMarkerSize: Integer;
+ CapMarkerSize: Integer;
+ RegMarkerSize: Integer;
+ PVMarkerSize: Integer;
+ StoreMarkerSize: Integer;
+ FuseMarkerSize: Integer;
+ RecloserMarkerSize: Integer;
+ RelayMarkerSize: Integer;
+
+ TransMarkerCode: Integer;
+ CapMarkerCode: Integer;
+ RegMarkerCode: Integer;
+ PVMarkerCode: Integer;
+ StoreMarkerCode: Integer;
+ FuseMarkerCode: Integer;
+ RecloserMarkerCode: Integer;
+ RelayMarkerCode: Integer;
+
+ MarkSwitches: Boolean;
+ MarkTransformers: Boolean;
+ MarkCapacitors: Boolean;
+ MarkRegulators: Boolean;
+ MarkPVSystems: Boolean;
+ MarkStorage: Boolean;
+ MarkFuses: Boolean;
+ MarkReclosers: Boolean;
+ MarkRelays: Boolean;
+
+ BusMarkerList: TList; // list of buses to mark
+ // ---------------------------------
+
+ ActiveLoadShapeClass: Integer;
+
+
+ // Variables for the tearing Algorithm
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ VIndex: Integer; // To store the index of the sub-circuit in the interconnected system
+ VLength: Integer; // To store the length of the sub-circuit in the interconnected system
+ AD_Init: Boolean; // This is used only by the A-Diakoptics coordiantor (ID = 1)
+
+ Coverage, // Used for the user to stablish the coverage for the algorithm
+ Actual_Coverage: Double; // Indicates the actual coverage of the circuit after running the tearing algorithm
+ Longest_paths: array of Integer; //Stores the coordinates of the longest paths in the circuit
+ Path_Idx: array of Integer; //Stores the indexes from where the areas where formed on the linearized graph
+ Buses_Covered: array of Integer; //Stores the number of buses (estimated - 1 quadrant) per path
+ Path_Size: array of Integer; //Stores the estimated size of each path
+ New_Graph: array of Integer; //Stores the latest weighted graph
+ Num_SubCkts: Integer; // Stores the number of subcircuits for tearing the circuit when executing the "tear_Circuit" command
+ Link_Branches: array of String; // Stores the names of the Link branches for Diakoptics
+ PConn_Names: array of String; // Stores the names of the buses (bus1) of the link branches
+ PConn_Voltages: array of Double; // Stores the voltages at the point of connection of the subcircuits
+ Locations: array of Integer; // Stores the indexes of the locations
+ BusZones: array of String;
+
+ // Variables for Diakoptics
+ //TODO: migrate TSparse_Complex to KLUSolveX (most functionality already present in Eigen)
+ ContoursT: TSparse_Complex; // Contours matrix transposed
+ Contours: TSparse_Complex; // Contours matrix
+ ZLL: TSparse_Complex; // Link branch matrix
+ ZCT: TSparse_Complex; // The transformation matrix (to go from one to other domain)
+ ZCC: TSparse_Complex; // Interconnections matrix
+ Y4: TSparse_Complex; // The inverse of the interconnections matrix
+ // V_0: TSparse_Complex; // The voltages of the partial solutions
+ Ic: TSparse_Complex; // The complementary Currents vector
+ MeTISZones: TStringList; // The list for assigning a zone to a bus after tearing
+
+ procedure AggregateProfiles(mode: String);
+ procedure Disable_All_DER();
+ function Tear_Circuit(): Integer; // Tears the circuit considering the number of Buses of the original Circuit
+ function Create_MeTIS_graph(): String; // Generates the graph dscribing the model for MeTiS
+ function Create_MeTIS_Zones(Filename: String): String; // Executes MeTiS and loads the zones into memory for further use
+ procedure Save_SubCircuits(AddISrc: Boolean);
+ // To guarantee the desired coverage when tearing the system
+ procedure Format_SubCircuits(Path: String; NumCkts: Integer; AddISrc: Boolean); // Arrange the files of the subcircuits to make them independent
+ procedure AppendIsources(Path: String; BusNum: Integer; LinkBranch: String);
+
+ procedure get_longest_path();
+ function Append2PathsArray(New_Path: array of Integer): Integer;// appends a new path to the array and returns the index(1D)
+ procedure Normalize_graph();
+ procedure Get_paths_4_Coverage(); // Calculates the paths inside the graph
{$ENDIF}
- VIndex : Integer; // To store the index of the sub-circuit in the interconnected system
- VLength : Integer; // To store the length of the sub-circuit in the interconnected system
- AD_Init : Boolean; // This is used only by the A-Diakoptics coordiantor (ID = 1)
-
- // Bus and Node stuff
- Buses: pTBusArray;
- MapNodeToBus:pTNodeBusArray;
-
- // Flags
- Issolved :Boolean;
- DuplicatesAllowed :Boolean;
- ZonesLocked :Boolean;
- MeterZonesComputed:Boolean;
- PositiveSequence :Boolean; // Model is to be interpreted as Pos seq
- NeglectLoadY :Boolean;
-
- // Voltage limits
- NormalMinVolts,
- NormalMaxVolts,
- EmergMaxVolts,
- EmergMinVolts :Double; //per unit voltage restraints for this circuit
- LegalVoltageBases :pDoubleArray;
-
- // Global circuit multipliers
- GeneratorDispatchReference,
- DefaultGrowthFactor,
- DefaultGrowthRate,
- GenMultiplier, // global multiplier for every generator
- HarmMult :Double;
- DefaultHourMult: Complex;
-
- PriceSignal:Double; // price signal for entire circuit
-
- // EnergyMeter Totals
- RegisterTotals:TRegisterArray;
-
- DefaultDailyShapeObj,
- DefaultYearlyShapeObj :TLoadShapeObj;
-
- CurrentDirectory :String;
-
- ReductionStrategy:TReductionStrategy;
- {ReductionMaxAngle,} ReductionZmag:double;
- ReduceLateralsKeepLoad:Boolean;
- ReductionStrategyString:String;
-
- PctNormalFactor:Double;
-
- {------Plot Marker Circuit Globals---------}
- NodeMarkerCode :Integer;
- NodeMarkerWidth :Integer;
- SwitchMarkerCode :Integer;
-
- TransMarkerSize :Integer;
- CapMarkerSize :Integer;
- RegMarkerSize :Integer;
- PVMarkerSize :Integer;
- StoreMarkerSize :Integer;
- FuseMarkerSize :Integer;
- RecloserMarkerSize :Integer;
- RelayMarkerSize :Integer;
-
- TransMarkerCode :Integer;
- CapMarkerCode :Integer;
- RegMarkerCode :Integer;
- PVMarkerCode :Integer;
- StoreMarkerCode :Integer;
- FuseMarkerCode :Integer;
- RecloserMarkerCode :Integer;
- RelayMarkerCode :Integer;
-
- MarkSwitches :Boolean;
- MarkTransformers :Boolean;
- MarkCapacitors :Boolean;
- MarkRegulators :Boolean;
- MarkPVSystems :Boolean;
- MarkStorage :Boolean;
- MarkFuses :Boolean;
- MarkReclosers :Boolean;
- MarkRelays :Boolean;
- NumCircuits :integer;
-
- BusMarkerList :TList; // list of buses to mark
-
- {---------------------------------}
-
- ActiveLoadShapeClass: Integer;
- MeTISZones: TStringList; // The list for assigning a zone to a bus after tearing
-
-
- Constructor Create(dssContext: TDSSContext; const aName:String);
- Destructor Destroy; Override;
-
- Procedure AddCktElement(); // Adds last DSS object created to circuit
- Procedure ClearBusMarkers;
-
- Procedure TotalizeMeters;
- Function ComputeCapacity: Boolean;
-
- Function Save(Dir:String): Boolean;
-
- Procedure ProcessBusDefs;
- Procedure ReProcessBusDefs;
- Procedure DoResetMeterZones;
- Function SetElementActive(Const FullObjectName:String) : Integer;
- Procedure InvalidateAllPCElements;
-
- Procedure DebugDump(Var F:TFileStream);
+ constructor Create(dssContext: TDSSContext; const aName: String);
+ destructor Destroy; OVERRIDE;
+
+ procedure AddCktElement(Obj: TDSSCktElement);
+ procedure ClearBusMarkers;
+
+ procedure TotalizeMeters;
+ function ComputeCapacity: Boolean;
+
+ function Save(Dir: String): Boolean;
+
+ procedure ProcessBusDefs;
+ procedure ReProcessBusDefs;
+ procedure DoResetMeterZones;
+ function SetElementActive(const FullObjectName: String): Integer;
+ procedure InvalidateAllPCElements;
+
+ procedure DebugDump(var F: TFileStream);
// Access to topology from the first source
- Function GetTopology: TCktTree;
- Procedure FreeTopology;
- Function GetBusAdjacentPDLists: TAdjArray;
- Function GetBusAdjacentPCLists: TAdjArray;
- function Tear_Circuit(): Integer; // Tears the circuit considering the number of Buses of the original Circuit
- function Create_MeTIS_graph():string; // Generates the graph dscribing the model for MeTiS
- function Create_MeTIS_Zones(Filename : string): string; // Executes MeTiS and loads the zones into memory for further use
- procedure AggregateProfiles(mode: string);
- procedure Disable_All_DER();
- procedure Save_SubCircuits(AddISrc: Boolean);
- function getPCEatBus(BusName: String; useNone: Boolean = True): ArrayOfString;
- function getPDEatBus(BusName: String; useNone: Boolean = True): ArrayOfString;
- function ReportPCEatBus(BusName: String): String;
- function ReportPDEatBus(BusName: String): String;
- function get_Line_Bus(LName: String; NBus: Integer):String;
- procedure get_longest_path();
- function Append2PathsArray(New_Path : array of integer): Integer;// appends a new path to the array and returns the index(1D)
- procedure Normalize_graph();
- procedure Get_paths_4_Coverage(); // Calculates the paths inside the graph
- // To guarantee the desired coverage when tearing the system
- procedure Format_SubCircuits(Path: String; NumCkts: Integer; AddISrc: Boolean); // Arrange the files of the subcircuits to make them independent
- procedure AppendIsources(Path: string; BusNum: Integer; LinkBranch: String);
-
- property Name : String Read Get_Name;
- Property CaseName : String Read FCaseName Write Set_CaseName;
- Property ActiveCktElement : TDSSCktElement Read FActiveCktElement Write Set_ActiveCktElement;
- Property Losses : Complex Read Get_Losses; // Total Circuit PD Element losses
- Property BusNameRedefined : Boolean Read FBusNameRedefined Write Set_BusNameRedefined;
- Property LoadMultiplier : Double Read FLoadMultiplier write Set_LoadMultiplier;
-
- End;
+ function GetTopology: TCktTree;
+ procedure FreeTopology;
+ function GetBusAdjacentPDLists: TAdjArray;
+ function GetBusAdjacentPCLists: TAdjArray;
+ function getPCEatBus(BusName: String; useNone: Boolean = TRUE): ArrayOfString;
+ function getPDEatBus(BusName: String; useNone: Boolean = TRUE): ArrayOfString;
+ function ReportPCEatBus(BusName: String): String;
+ function ReportPDEatBus(BusName: String): String;
+
+ property Name: String READ Get_Name;
+ property CaseName: String READ FCaseName WRITE Set_CaseName;
+ property ActiveCktElement: TDSSCktElement READ FActiveCktElement WRITE Set_ActiveCktElement;
+ property Losses: Complex READ Get_Losses; // Total Circuit PD Element losses
+ property BusNameRedefined: Boolean READ FBusNameRedefined WRITE Set_BusNameRedefined;
+ property LoadMultiplier: Double READ FLoadMultiplier WRITE Set_LoadMultiplier;
+ end;
implementation
-USES
- PDElement, CktElementClass,
- ParserDel, DSSClassDefs, DSSGlobals, Dynamics,
- Line, Transformer, Vsource,
- Utilities, {$IFDEF FPC}CmdForms,{$ELSE}DSSForms, {$ENDIF}
- {$IFDEF MSWINDOWS}Windows, SHELLAPI, {$ELSE} BaseUnix, Unix, {$ENDIF} Executive, StrUtils,
- Load, PVSystem, DSSHelper,
- BufStream;
-//----------------------------------------------------------------------------
-Constructor TDSSCircuit.Create(dssContext: TDSSContext; const aName:String);
+uses
+ BufStream,
+ PDElement,
+ CktElementClass,
+ ParserDel,
+ DSSClassDefs,
+ DSSGlobals,
+ Dynamics,
+ Line,
+ Transformer,
+ Vsource,
+ Utilities,
+ CmdForms,
+ {$IFDEF MSWINDOWS}
+ Windows,
+ SHELLAPI,
+{$ELSE}
+ BaseUnix,
+ Unix,
+{$ENDIF}
+ Executive,
+ StrUtils,
+ Load,
+ PVSystem,
+ DSSHelper;
-// Var Retval:Integer;
+constructor TDSSCircuit.Create(dssContext: TDSSContext; const aName: String);
+begin
+ inherited Create('Circuit');
-BEGIN
- inherited Create('Circuit');
-
- DSS := dssContext;
+ DSS := dssContext;
- IsSolved := False;
- {*Retval := *} DSS.SolutionClass.NewObject(Name);
- Solution := DSS.ActiveSolutionObj;
+ IsSolved := FALSE;
+ Solution := TSolutionObj.Create(DSS, Name);
- LocalName := LowerCase(aName);
+ LocalName := AnsiLowerCase(aName);
- CaseName := aName; // Default case name to circuitname
- // Sets CircuitName_
+ CaseName := aName; // Default case name to circuitname
+ // Sets CircuitName_
- Fundamental := DSS.DefaultBaseFreq;
- ActiveCktElement := nil;
- ActiveBusIndex := 1; // Always a bus
+ Fundamental := DSS.DefaultBaseFreq;
+ ActiveCktElement := NIL;
+ ActiveBusIndex := 1; // Always a bus
// initial allocations increased from 100 to 1000 to speed things up
- MaxBuses := 1000; // good sized allocation to start
- MaxDevices := 1000;
- MaxNodes := 3*MaxBuses;
- IncDevices := 1000;
- IncBuses := 1000;
- IncNodes := 3000;
+ MaxBuses := 1000; // good sized allocation to start
+ MaxDevices := 1000;
+ MaxNodes := 3 * MaxBuses;
+ IncDevices := 1000;
+ IncBuses := 1000;
+ IncNodes := 3000;
// Allocate some nominal sizes
- BusList := TBusHashListType.Create(900); // Bus name list Nominal size to start; gets reallocated
- DeviceList := THashList.Create(900);
- AutoAddBusList := TBusHashListType.Create(100);
+ BusList := TBusHashListType.Create(900); // Bus name list Nominal size to start; gets reallocated
+ DeviceList := THashList.Create(900);
+ AutoAddBusList := TBusHashListType.Create(100);
- NumBuses := 0; // Eventually allocate a single source
- NumDevices := 0;
- NumNodes := 0;
+ NumBuses := 0; // Eventually allocate a single source
+ NumDevices := 0;
+ NumNodes := 0;
- Faults := TDSSPointerList.Create(2);
- CktElements := TDSSPointerList.Create(1000);
+ Faults := TDSSPointerList.Create(2);
+ CktElements := TDSSPointerList.Create(1000);
{$IFDEF DSS_CAPI_INCREMENTAL_Y}
- IncrCktElements := TDSSPointerList.Create(1000);
+ IncrCktElements := TDSSPointerList.Create(1000);
{$ENDIF}
- PDElements := TDSSPointerList.Create(1000);
- PCElements := TDSSPointerList.Create(1000);
- DSSControls := TDSSPointerList.Create(10);
- Sources := TDSSPointerList.Create(10);
- MeterElements := TDSSPointerList.Create(20);
- Monitors := TDSSPointerList.Create(20);
- EnergyMeters := TDSSPointerList.Create(5);
- Sensors := TDSSPointerList.Create(5);
- Generators := TDSSPointerList.Create(5);
- StorageElements := TDSSPointerList.Create(5);
- PVSystems := TDSSPointerList.Create(5);
- Substations := TDSSPointerList.Create(5);
- Transformers := TDSSPointerList.Create(10);
- CapControls := TDSSPointerList.Create(10);
- SwtControls := TDSSPointerList.Create(50);
- RegControls := TDSSPointerList.Create(5);
- Lines := TDSSPointerList.Create(1000);
- Loads := TDSSPointerList.Create(1000);
- ShuntCapacitors := TDSSPointerList.Create(20);
- Reactors := TDSSPointerList.Create(5);
- Reclosers := TDSSPointerList.Create(10);
- Relays := TDSSPointerList.Create(10);
- Fuses := TDSSPointerList.Create(50);
-
- Buses := Allocmem(Sizeof(Buses^[1]) * Maxbuses);
- MapNodeToBus := Allocmem(Sizeof(MapNodeToBus^[1]) * MaxNodes);
-
- ControlQueue := TControlQueue.Create(DSS);
-
- LegalVoltageBases := AllocMem(SizeOf(LegalVoltageBases^[1]) * 8);
+ PDElements := TDSSPointerList.Create(1000);
+ PCElements := TDSSPointerList.Create(1000);
+ DSSControls := TDSSPointerList.Create(10);
+ Sources := TDSSPointerList.Create(10);
+ MeterElements := TDSSPointerList.Create(20);
+ Monitors := TDSSPointerList.Create(20);
+ EnergyMeters := TDSSPointerList.Create(5);
+ Sensors := TDSSPointerList.Create(5);
+ Generators := TDSSPointerList.Create(5);
+ StorageElements := TDSSPointerList.Create(5);
+ PVSystems := TDSSPointerList.Create(5);
+ InvControls := TDSSPointerList.Create(5);
+ ExpControls := TDSSPointerList.Create(5);
+ Substations := TDSSPointerList.Create(5);
+ Transformers := TDSSPointerList.Create(10);
+ AutoTransformers := TDSSPointerList.Create(10);
+ CapControls := TDSSPointerList.Create(10);
+ SwtControls := TDSSPointerList.Create(50);
+ RegControls := TDSSPointerList.Create(5);
+ Lines := TDSSPointerList.Create(1000);
+ Loads := TDSSPointerList.Create(1000);
+ ShuntCapacitors := TDSSPointerList.Create(20);
+ Reactors := TDSSPointerList.Create(5);
+ Reclosers := TDSSPointerList.Create(10);
+ Relays := TDSSPointerList.Create(10);
+ Fuses := TDSSPointerList.Create(50);
+
+ Buses := Allocmem(Sizeof(Buses^[1]) * Maxbuses);
+ MapNodeToBus := Allocmem(Sizeof(MapNodeToBus^[1]) * MaxNodes);
+
+ ControlQueue := TControlQueue.Create(DSS);
+
+ LegalVoltageBases := AllocMem(SizeOf(LegalVoltageBases^[1]) * 8);
// Default Voltage Bases
- LegalVoltageBases^[1] := 0.208;
- LegalVoltageBases^[2] := 0.480;
- LegalVoltageBases^[3] := 12.47;
- LegalVoltageBases^[4] := 24.9;
- LegalVoltageBases^[5] := 34.5;
- LegalVoltageBases^[6] := 115.0;
- LegalVoltageBases^[7] := 230.0;
- LegalVoltageBases^[8] := 0.0; // terminates array
+ LegalVoltageBases^[1] := 0.208;
+ LegalVoltageBases^[2] := 0.480;
+ LegalVoltageBases^[3] := 12.47;
+ LegalVoltageBases^[4] := 24.9;
+ LegalVoltageBases^[5] := 34.5;
+ LegalVoltageBases^[6] := 115.0;
+ LegalVoltageBases^[7] := 230.0;
+ LegalVoltageBases^[8] := 0.0; // terminates array
- ActiveLoadShapeClass := USENONE; // Signify not set
+ ActiveLoadShapeClass := USENONE; // Signify not set
- NodeBufferMax := 50;
- NodeBuffer := AllocMem(SizeOf(NodeBuffer^[1]) * NodeBufferMax); // A place to hold the nodes
+ NodeBufferMax := 50;
+ NodeBuffer := AllocMem(SizeOf(NodeBuffer^[1]) * NodeBufferMax); // A place to hold the nodes
// Init global circuit load and harmonic source multipliers
- FLoadMultiplier := 1.0;
- GenMultiplier := 1.0;
- HarmMult := 1.0;
+ FLoadMultiplier := 1.0;
+ GenMultiplier := 1.0;
+ HarmMult := 1.0;
- PriceSignal := 25.0; // $25/MWH
+ PriceSignal := 25.0; // $25/MWH
// Factors for Autoadd stuff
- UEWeight := 1.0; // Default to weighting UE same as losses
- LossWeight := 1.0;
- NumUEregs := 1;
- NumLossRegs := 1;
- UEregs := nil; // set to something so it wont break reallocmem
- LossRegs := nil;
- Reallocmem(UEregs, sizeof(UEregs^[1])*NumUEregs);
- Reallocmem(Lossregs, sizeof(Lossregs^[1])*NumLossregs);
- UEregs^[1] := 10; // Overload UE
- LossRegs^[1] := 13; // Zone Losses
-
- CapacityStart := 0.9; // for Capacity search
- CapacityIncrement := 0.005;
-
- LoadDurCurve := '';
- LoadDurCurveObj := nil;
- PriceCurve := '';
- PriceCurveObj := nil;
+ UEWeight := 1.0; // Default to weighting UE same as losses
+ LossWeight := 1.0;
+ NumUEregs := 1;
+ NumLossRegs := 1;
+ UEregs := NIL; // set to something so it wont break reallocmem
+ LossRegs := NIL;
+ Reallocmem(UEregs, sizeof(UEregs^[1]) * NumUEregs);
+ Reallocmem(Lossregs, sizeof(Lossregs^[1]) * NumLossregs);
+ UEregs^[1] := 10; // Overload UE
+ LossRegs^[1] := 13; // Zone Losses
+
+ CapacityStart := 0.9; // for Capacity search
+ CapacityIncrement := 0.005;
+
+ LoadDurCurve := '';
+ LoadDurCurveObj := NIL;
+ PriceCurve := '';
+ PriceCurveObj := NIL;
// Flags
- DuplicatesAllowed := False;
- ZonesLocked := False; // Meter zones recomputed after each change
- MeterZonesComputed := False;
- PositiveSequence := False;
- NeglectLoadY := False;
-
- NormalMinVolts := 0.95;
- NormalMaxVolts := 1.05;
- EmergMaxVolts := 1.08;
- EmergMinVolts := 0.90;
-
- NodeMarkerCode := 16;
- NodeMarkerWidth:= 1;
- MarkSwitches := FALSE;
- MarkTransformers := FALSE;
- MarkCapacitors := FALSE;
- MarkRegulators := FALSE;
- MarkPVSystems := FALSE;
- MarkStorage := FALSE;
- MarkFuses := FALSE;
- MarkReclosers := FALSE;
-
- SwitchMarkerCode := 5;
- TransMarkerCode := 35;
- CapMarkerCode := 38;
- RegMarkerCode := 17; //47;
- PVMarkerCode := 15;
- StoreMarkerCode := 9;
- FuseMarkerCode := 25;
- RecloserMarkerCode := 17;
- RelayMarkerCode := 17;
-
- TransMarkerSize := 1;
- CapMarkerSize := 3;
- RegMarkerSize := 5; //1;
- PVMarkerSize := 1;
- StoreMarkerSize := 1;
- FuseMarkerSize := 1;
- RecloserMarkerSize := 5;
- RelayMarkerSize := 5;
-
- BusMarkerList := TList.Create;
- BusMarkerList.Clear;
-
- TrapezoidalIntegration := FALSE; // Default to Euler method
- LogEvents := FALSE;
-
- GeneratorDispatchReference := 0.0;
- DefaultGrowthRate := 1.025;
- DefaultGrowthFactor := 1.0;
-
- DefaultDailyShapeObj := DSS.LoadShapeClass.Find('default');
- DefaultYearlyShapeObj := DSS.LoadShapeClass.Find('default');
-
- CurrentDirectory := '';
-
- BusNameRedefined := True; // set to force rebuild of buslists, nodelists
-
- SavedBuses := nil;
- SavedBusNames := nil;
-
- ReductionStrategy := rsDefault;
-// ReductionMaxAngle := 15.0;
- ReductionZmag := 0.02;
- NumCircuits := 0;
- ReduceLateralsKeepLoad := TRUE;
-
- {Misc objects}
- AutoAddObj := TAutoAdd.Create(DSS);
-
- Branch_List := nil;
- BusAdjPC := nil;
- BusAdjPD := nil;
+ DuplicatesAllowed := FALSE;
+ ZonesLocked := FALSE; // Meter zones recomputed after each change
+ MeterZonesComputed := FALSE;
+ PositiveSequence := FALSE;
+ NeglectLoadY := FALSE;
+
+ NormalMinVolts := 0.95;
+ NormalMaxVolts := 1.05;
+ EmergMaxVolts := 1.08;
+ EmergMinVolts := 0.90;
+
+ NodeMarkerCode := 16;
+ NodeMarkerWidth := 1;
+ MarkSwitches := FALSE;
+ MarkTransformers := FALSE;
+ MarkCapacitors := FALSE;
+ MarkRegulators := FALSE;
+ MarkPVSystems := FALSE;
+ MarkStorage := FALSE;
+ MarkFuses := FALSE;
+ MarkReclosers := FALSE;
+
+ SwitchMarkerCode := 5;
+ TransMarkerCode := 35;
+ CapMarkerCode := 38;
+ RegMarkerCode := 17; //47;
+ PVMarkerCode := 15;
+ StoreMarkerCode := 9;
+ FuseMarkerCode := 25;
+ RecloserMarkerCode := 17;
+ RelayMarkerCode := 17;
+
+ TransMarkerSize := 1;
+ CapMarkerSize := 3;
+ RegMarkerSize := 5; //1;
+ PVMarkerSize := 1;
+ StoreMarkerSize := 1;
+ FuseMarkerSize := 1;
+ RecloserMarkerSize := 5;
+ RelayMarkerSize := 5;
+
+ BusMarkerList := TList.Create;
+ BusMarkerList.Clear;
- // tearing algorithm vars initialization
+ TrapezoidalIntegration := FALSE; // Default to Euler method
+ LogEvents := FALSE;
+ GeneratorDispatchReference := 0.0;
+ DefaultGrowthRate := 1.025;
+ DefaultGrowthFactor := 1.0;
- Coverage := 0.9; // 90% coverage expected by default
- Actual_coverage := -1; // No coverage
- Num_SubCkts := CPU_Cores-1;
+ DefaultDailyShapeObj := DSS.LoadShapeClass.Find('default');
+ DefaultYearlyShapeObj := DSS.LoadShapeClass.Find('default');
+
+ CurrentDirectory := '';
+
+ BusNameRedefined := TRUE; // set to force rebuild of buslists, nodelists
+
+ SavedBuses := NIL;
+ SavedBusNames := NIL;
+
+ ReductionStrategy := rsDefault;
+ ReductionZmag := 0.02;
+ ReduceLateralsKeepLoad := TRUE;
+
+ // Misc objects
+ AutoAddObj := TAutoAdd.Create(DSS);
+
+ Branch_List := NIL;
+ BusAdjPC := NIL;
+ BusAdjPD := NIL;
+
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ // tearing algorithm vars initialization
+ Coverage := 0.9; // 90% coverage expected by default
+ Actual_coverage := -1; // No coverage
+ Num_SubCkts := CPU_Cores - 1;
+
+ // Diakoptics variables
+ Contours := TSparse_Complex.Create;
+ ZLL := TSparse_Complex.Create;
+ ZCC := TSparse_Complex.Create;
+ ZCT := TSparse_Complex.Create;
+ Y4 := TSparse_Complex.Create;
+ // V_0 := TSparse_Complex.Create;
+ Ic := TSparse_Complex.Create;
+
+ setlength(Longest_paths, 0);
+ setlength(Path_Idx, 0);
+ setlength(Buses_Covered, 0);
+ setlength(Path_size, 0);
- setlength(Longest_paths,0);
- setlength(Path_Idx,0);
- setlength(Buses_Covered,0);
- setlength(Path_size,0);
+{$ENDIF}
- // Diakoptics variables
- Contours := TSparse_Complex.Create;
- ZLL := TSparse_Complex.Create;
- ZCC := TSparse_Complex.Create;
- ZCT := TSparse_Complex.Create;
- Y4 := TSparse_Complex.Create;
- V_0 := TSparse_Complex.Create;
- Ic := TSparse_Complex.Create;
{$IFDEF DSS_CAPI_PM}
- LockIc := syncobjs.TCriticalSection.Create;
+ LockIc := syncobjs.TCriticalSection.Create;
{$ENDIF}
-END;
-
-//----------------------------------------------------------------------------
-Destructor TDSSCircuit.Destroy;
-VAR
- i:Integer;
- pCktElem :TDSSCktElement;
- ElemName :String;
-
-BEGIN
- For i := 1 to NumDevices Do Begin
- TRY
+end;
+
+destructor TDSSCircuit.Destroy;
+var
+ i: Integer;
+ pCktElem: TDSSCktElement;
+ ElemName: String;
+
+begin
+ for i := 1 to NumDevices do
+ begin
+ try
pCktElem := TDSSCktElement(CktElements.Get(i));
ElemName := pCktElem.ParentClass.name + '.' + pCktElem.Name;
pCktElem.Free;
- EXCEPT
- ON E: Exception Do
- DoSimpleMsg(DSS, 'Exception Freeing Circuit Element:' + ElemName + CRLF + E.Message, 423);
- END;
- End;
- FOR i := 1 to NumBuses Do Buses^[i].Free; // added 10-29-00
+ except
+ ON E: Exception do
+ DoSimpleMsg(DSS, 'Exception Freeing Circuit Element: %s %s', [ElemName, CRLF + E.Message], 423);
+ end;
+ end;
+ for i := 1 to NumBuses do
+ Buses^[i].Free;
Reallocmem(Buses, 0);
Reallocmem(MapNodeToBus, 0);
@@ -617,6 +639,8 @@ implementation
Transformers.Free;
CapControls.Free;
SwtControls.Free;
+ InvControls.Free;
+ ExpControls.Free;
RegControls.Free;
Loads.Free;
Lines.Free;
@@ -625,6 +649,7 @@ implementation
Reclosers.Free;
Relays.Free;
Fuses.Free;
+ AutoTransformers.Free;
ControlQueue.Free;
@@ -635,924 +660,1169 @@ implementation
FreeTopology;
-// Release all ADiakoptics matrixes
-
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ // Release all ADiakoptics matrices
Contours.Free;
ZLL.Free;
ZCC.Free;
ZCT.Free;
Y4.Free;
- V_0.Free;
+ // V_0.Free;
Ic.Free;
-
+{$ENDIF}
{$IFDEF DSS_CAPI_PM}
LockIc.Free;
{$ENDIF}
- Inherited Destroy;
-END;
-
-{*******************************************************************************
-* Routine created to empty a recently created folder *
-********************************************************************************}
-{$IFDEF MSWINDOWS}
-procedure DelFilesFromDir(Directory, FileMask: string; DelSubDirs: Boolean);
-var
- SourceLst: string;
- FOS: TSHFileOpStruct;
-begin
- FillChar(FOS, SizeOf(FOS), 0);
- FOS.wFunc := FO_DELETE;
- SourceLst := Directory + PathDelim + FileMask + #0;
- FOS.pFrom := PChar(SourceLst);
- if not DelSubDirs then
- FOS.fFlags := FOS.fFlags OR FOF_FILESONLY;
- // Remove the next line if you want a confirmation dialog box
- FOS.fFlags := FOS.fFlags OR FOF_NOCONFIRMATION;
- // Add the next line for a "silent operation" (no progress box)
- FOS.fFlags := FOS.fFlags OR FOF_SILENT;
- SHFileOperation(FOS);
-end;
- {$ENDIF}
-{$IFDEF UNIX}
-procedure DeltreeDir(Directory: string);
-var
- Info: TSearchRec;
-Begin
- If FindFirst(Directory + PathDelim + '*', faAnyFile and faDirectory, Info) = 0 then
- begin
- Repeat
- With Info do
- begin
- If (name = '.') or (name = '..') then continue;
- If (Attr and faDirectory) = faDirectory then
- begin
- DeltreeDir(Directory + PathDelim + Name)
- end
- else
- begin
- DeleteFile(Directory + PathDelim + Name);
- end;
- end;
- Until FindNext(info) <> 0;
- end;
- rmdir(Directory);
+ inherited Destroy;
end;
-procedure DelFilesFromDir(Directory, FileMask: string; DelSubDirs: Boolean);
+// This routine retuns the index of the element within the array
+function get_element_Idx(graph_in: array of Integer; element: Integer): Integer;
var
- Info: TSearchRec;
- flags: LongInt;
-Begin
- if DelSubDirs then
- flags := faAnyFile and faDirectory
- else
- flags := faAnyFile;
-
- If FindFirst(Directory + PathDelim + FileMask, flags, Info) = 0 then
- begin
- Repeat
- With Info do
- begin
- if (name = '.') or (name = '..') then continue;
- If (Attr and faDirectory) = faDirectory then
+ Found, // To indicate that the element was found
+ End_Flag: Boolean;
+ Graph_size,
+ Local_idx: Integer;
+begin
+ Result := -1; // In case the element is not in the array
+ End_Flag := TRUE; // To control the algorithm execution (while based)
+ Local_idx := 0;
+ Found := FALSE; // Not found yet
+ Graph_size := length(graph_in);
+ while (End_Flag) and (Local_idx < Graph_Size) do
+ begin
+ if graph_in[Local_idx] = element then
begin
- try
- DeltreeDir(Directory + PathDelim + Name)
- except
- DSSMessageDlg('Could not remove directory ' + Directory + PathDelim + Name, True);
- end;
+ End_Flag := FALSE;
+ Found := TRUE;
end
else
begin
- DeleteFile(Directory + PathDelim + Name);
+ inc(Local_idx);
end;
- end;
- Until FindNext(info) <> 0;
- end;
+ end;
+ if Found then
+ Result := Local_Idx;
end;
-{$ENDIF}
-{*******************************************************************************
-* This routine retuns the index of the element within the array *
-********************************************************************************}
-function get_element_Idx(graph_in: array of integer; element: Integer): Integer;
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+// This routine calculates the longest path within a linearized
+// graph considering the zero level buses as the beginning of
+// new path
+procedure TDSSCircuit.get_longest_path();
var
- Found, // To indicate that the element was found
- End_Flag : Boolean;
- Graph_size,
- Local_idx : Integer;
+ End_flag: Boolean; // Terminates the process
+ Current_Idx, // Stores the Index value of the current level
+ Current_level: Integer; // Stores the current level traced
begin
- Result := -1; // In case the element is not in the array
- End_Flag := True; // To control the algorithm execution (while based)
- Local_idx := 0;
- Found := False; // Not found yet
- Graph_size := length(graph_in);
- while (End_Flag) and (Local_idx < Graph_Size) do
- Begin
- if graph_in[Local_idx] = element then
- Begin
- End_Flag := False;
- Found := True;
- End
- else
+ with solution do
begin
- inc(Local_idx);
+ Current_level := maxintvalue(Inc_Mat_Levels); // Init level
+ Current_idx := get_element_idx(Inc_Mat_Levels, Current_level); // Init Index
+ End_flag := TRUE;
+ setlength(New_graph, 0);
+ while End_flag do
+ begin
+ //Checks the termination criteria
+ if (Current_level > Inc_Mat_Levels[Current_idx]) or (Inc_Mat_Levels[Current_idx] = 0) then
+ End_Flag := FALSE;
+ // Is the current bus part of the new backbone?
+ if Inc_Mat_Levels[Current_idx] = Current_level then
+ begin
+ dec(Current_level);
+ setlength(New_graph, (length(New_graph) + 1));
+ New_graph[High(New_graph)] := Current_idx;
+ end;
+ dec(Current_idx);
+ end;
end;
- End;
- if Found then Result := Local_Idx;
end;
-{*******************************************************************************
-* This routine calculates the longest path within a linearized *
-* graph considering the zero level buses as the beginning of *
-* new path *
-********************************************************************************}
-procedure TDSSCircuit.get_longest_path();
-var
- End_flag : Boolean; // Terminates the process
- Current_Idx, // Stores the Index value of the current level
- Current_level : Integer; // Stores the current level traced
-Begin
- with solution do
- Begin
- Current_level := maxintvalue(Inc_Mat_Levels); // Init level
- Current_idx := get_element_idx(Inc_Mat_Levels,Current_level); // Init Index
- End_flag := True;
- setlength(New_graph,0);
- while End_flag do
- Begin
- //Checks the termination cirteria
- if (Current_level > Inc_Mat_Levels[Current_idx]) or (Inc_Mat_Levels[Current_idx] = 0)then
- End_Flag := False;
- // Is the current bus part of the new backbone?
- if Inc_Mat_Levels[Current_idx] = Current_level then
- Begin
- dec(Current_level);
- setlength(New_graph,(length(New_graph) + 1));
- New_graph[High(New_graph)] := Current_idx;
- End;
- dec(Current_idx);
- End;
- End;
-End;
-{*******************************************************************************
-* This routine appends an array to the paths array and returns its index *
-********************************************************************************}
-function TDSSCircuit.Append2PathsArray(New_Path : array of integer): Integer;
+
+// This routine appends an array to the paths array and returns its index
+function TDSSCircuit.Append2PathsArray(New_Path: array of Integer): Integer;
var
- local_idx : Integer;
+ local_idx: Integer;
begin
- Result := High(Longest_paths) + 1;
+ Result := High(Longest_paths) + 1;
for local_idx := 0 to High(New_path) do
- Begin
- setlength(Longest_paths,(length(Longest_paths) + 1));
- Longest_paths[High(Longest_paths)] := New_Path[Local_idx];
- End;
+ begin
+ setlength(Longest_paths, (length(Longest_paths) + 1));
+ Longest_paths[High(Longest_paths)] := New_Path[Local_idx];
+ end;
end;
-{*******************************************************************************
-* This routine normalizes the Inc_matrix levels *
-********************************************************************************}
+
+// This routine normalizes the Inc_matrix levels
procedure TDSSCircuit.Normalize_graph();
var
- Curr_level, // To set the active level
- idx : Integer; //
- Ref_detected : Boolean; // To detect if there is a zero
-Begin
- Curr_level := -1; // Initializing values
- Ref_detected := False;
- with Solution do
- Begin
- for idx := 0 to High(Inc_Mat_Levels) do // Sweeps the whole graph
- Begin
- if Inc_Mat_Levels[idx] = 0 then Ref_detected := True
- else
- Begin
- if (Curr_level >= Inc_Mat_Levels[idx]) or Ref_detected then
- Begin
- Ref_detected := False;
- Curr_level := Inc_Mat_Levels[idx] - 1;
- Inc_Mat_Levels[idx] := 1;
- End
- else Inc_Mat_Levels[idx] := Inc_Mat_Levels[idx] - Curr_level;
- End;
- End;
- End;
-
-End;
-{*******************************************************************************
-* Traces the paths (0) in the graph to guarantee the desired coverage *
-********************************************************************************}
+ Curr_level, // To set the active level
+ idx: Integer;
+ Ref_detected: Boolean; // To detect if there is a zero
+begin
+ Curr_level := -1; // Initializing values
+ Ref_detected := FALSE;
+ with Solution do
+ begin
+ for idx := 0 to High(Inc_Mat_Levels) do // Sweeps the whole graph
+ begin
+ if Inc_Mat_Levels[idx] = 0 then
+ Ref_detected := TRUE
+ else
+ begin
+ if (Curr_level >= Inc_Mat_Levels[idx]) or Ref_detected then
+ begin
+ Ref_detected := FALSE;
+ Curr_level := Inc_Mat_Levels[idx] - 1;
+ Inc_Mat_Levels[idx] := 1;
+ end
+ else
+ Inc_Mat_Levels[idx] := Inc_Mat_Levels[idx] - Curr_level;
+ end;
+ end;
+ end;
+end;
+
+// Traces the paths (0) in the graph to guarantee the desired coverage
procedure TDSSCircuit.Get_paths_4_Coverage();
var
- DBLTemp, // For storing temoprary doubles
- Sys_Size : Double; // Stores the number of buses contained in the system
- SMEnd : Boolean; // Terminates the state machine
- i,
- State : Integer; // The current state of the state machine
- Candidates : array of integer; // Array for 0 level buses idx
-Begin
- with solution do
- Begin
- SMEnd := True;
- State := 0;
- Sys_Size := length(Inc_Mat_Cols);
- setlength(Buses_Covered,1);
- setlength(Path_Idx,1);
- Actual_Coverage := -1;
- while SMEnd do // The state machine starts
- Begin
- case State of
- 0: Begin // Processes the first path
- setlength(Candidates,0);
- for i := 0 to (length(Inc_Mat_Levels) - 1) do //Extracts the 0 Level Buses
- Begin
- if solution.Inc_Mat_Levels[i] = 0 then
- Begin
- setlength(Candidates,length(Candidates)+1);
- Candidates[High(Candidates)] := i;
- End;
- End;
- setlength(Longest_paths,0);
- Buses_covered[0] := MaxIntValue(Candidates); // Extracts the maximum level covered
- Path_Idx[0] := Append2PathsArray(Candidates); // No shifting in the graph
- State := 1; // Go to the next state
- End;
- 1: Begin // Extracts a new path from the longest branch to
- get_longest_path(); // the backbone (Zeros)
- setlength(Path_Idx,(length(Path_Idx) + 1));
- Path_Idx[High(Path_Idx)] := Append2PathsArray(New_Graph); // Adds the new candidates
- // Estimates the amount of buses covered in this path
- setlength(Buses_covered,(length(Buses_covered) + 1));
- Buses_covered[High(Buses_covered)] := New_Graph[0] - New_Graph[High(New_Graph)];
- // Replaces the latest path with 0 in the Bus levels array
- for i := Path_Idx[High(Path_Idx)] to High(Longest_paths) do
- Inc_Mat_Levels[Longest_paths[i]] := 0;
- Normalize_graph;
- // remains in the same state
- End;
- end;
- // Checks the coverage index to stablish if is necessary to keep tracing paths to increase the coverage
- DBLTemp := 0.0;
- for i := Low(Buses_covered) to High(Buses_covered) do
- DBLtemp := DBLTemp + (0.0+Buses_Covered[i]);
- DBLtemp := DBLTemp/Sys_Size;
-{ If the New coverage is different from the previous one and is below the expected coverage keep going
- The first criteria is to avoid keep working on a path that will not contribute to improve the coverage}
- if (DBLTemp <> Actual_Coverage) and (DBLTemp >= Coverage) then SMEnd := False;
- Actual_Coverage := DBLTemp;
- end;
- End;
-End;
-
-{*******************************************************************************
-* Appends single phase ISources to the each node of bus specified *
-* if the given linkBranch. This actions take place within the given file. *
-********************************************************************************
-}
-procedure TDSSCircuit.AppendIsources(Path : string; BusNum : Integer; LinkBranch: String);
-VAR
- jj,
- kk : Integer;
- text,
- BusName : String;
- pBus : TDSSBus;
- F: TFileStream = nil;
-
-Begin
-
- F := TFileStream.Create(Path, fmOpenReadWrite);
- F.Seek(0, soEnd);
-
- With DSS.ActiveCircuit Do
- Begin
+ DBLTemp, // For storing temoprary doubles
+ Sys_Size: Double; // Stores the number of buses contained in the system
+ SMEnd: Boolean; // Terminates the state machine
+ i,
+ State: Integer; // The current state of the state machine
+ Candidates: array of Integer; // Array for 0 level buses idx
+begin
+ with solution do
+ begin
+ SMEnd := TRUE;
+ State := 0;
+ Sys_Size := length(Inc_Mat_Cols);
+ setlength(Buses_Covered, 1);
+ setlength(Path_Idx, 1);
+ Actual_Coverage := -1;
+ while SMEnd do // The state machine starts
+ begin
+ case State of
+ 0:
+ begin // Processes the first path
+ setlength(Candidates, 0);
+ for i := 0 to (length(Inc_Mat_Levels) - 1) do // Extracts the 0 Level Buses
+ begin
+ if solution.Inc_Mat_Levels[i] = 0 then
+ begin
+ setlength(Candidates, length(Candidates) + 1);
+ Candidates[High(Candidates)] := i;
+ end;
+ end;
+ setlength(Longest_paths, 0);
+ Buses_covered[0] := MaxIntValue(Candidates); // Extracts the maximum level covered
+ Path_Idx[0] := Append2PathsArray(Candidates); // No shifting in the graph
+ State := 1; // Go to the next state
+ end;
+ 1:
+ begin // Extracts a new path from the longest branch to
+ get_longest_path(); // the backbone (Zeros)
+ setlength(Path_Idx, (length(Path_Idx) + 1));
+ Path_Idx[High(Path_Idx)] := Append2PathsArray(New_Graph); // Adds the new candidates
+ // Estimates the amount of buses covered in this path
+ setlength(Buses_covered, (length(Buses_covered) + 1));
+ Buses_covered[High(Buses_covered)] := New_Graph[0] - New_Graph[High(New_Graph)];
+ // Replaces the latest path with 0 in the Bus levels array
+ for i := Path_Idx[High(Path_Idx)] to High(Longest_paths) do
+ Inc_Mat_Levels[Longest_paths[i]] := 0;
+ Normalize_graph;
+ // remains in the same state
+ end;
+ end;
+ // Checks the coverage index to stablish if is necessary to keep tracing paths to increase the coverage
+ DBLTemp := 0.0;
+ for i := Low(Buses_covered) to High(Buses_covered) do
+ DBLtemp := DBLTemp + (0.0 + Buses_Covered[i]);
+ DBLtemp := DBLTemp / Sys_Size;
+ // If the New coverage is different from the previous one and is below the expected coverage keep going
+ // The first criteria is to avoid keep working on a path that will not contribute to improve the coverage
+ if (DBLTemp <> Actual_Coverage) and (DBLTemp >= Coverage) then
+ SMEnd := FALSE;
+ Actual_Coverage := DBLTemp;
+ end;
+ end;
+end;
+
+// Appends single phase ISources to the each node of bus specified
+// if the given linkBranch. This actions take place within the given file.
+procedure TDSSCircuit.AppendIsources(Path: String; BusNum: Integer; LinkBranch: String);
+var
+ jj,
+ kk: Integer;
+ text,
+ BusName: String;
+ pBus: TDSSBus;
+ F: TFileStream = NIL;
+begin
+ F := TBufferedFileStream.Create(Path, fmOpenReadWrite);
+ F.Seek(0, soEnd);
SetElementActive(LinkBranch);
- BusName := ActiveCktElement.GetBus(BusNum);
- jj := ansipos('.',BusName); // removes the dot
- if jj > 0 then BusName := BusName.Substring(0,jj - 1);
+ BusName := ActiveCktElement.GetBus(BusNum);
+ jj := ansipos('.', BusName); // removes the dot
+ if jj > 0 then
+ BusName := BusName.Substring(0, jj - 1);
+
SetActiveBus(DSS, BusName);
- pBus := Buses^[ActiveBusIndex];
+ pBus := Buses^[ActiveBusIndex];
for kk := 1 to pBus.NumNodesThisBus do
- Begin
- text := 'New ISource.' + inttostr(BusNum) + '_' + inttostr(kk) + ' phases=1 bus1=' + BusName +
- '.' + inttostr(kk) + ' amps=0.000001 angle=0';
- FSWriteLn(F, text);
- End;
- End;
- F.Free;
-
-End;
+ begin
+ text := 'New ISource.' + inttostr(BusNum) + '_' + inttostr(kk) + ' phases=1 bus1=' + BusName + '.' + inttostr(kk) + ' amps=0.000001 angle=0';
+ FSWriteLn(F, text);
+ end;
+ F.Free;
+end;
{$IFDEF UNIX}
-PROCEDURE CopyFile(inFn: String; outFn: String; failIfExists: Boolean);
-VAR
+procedure CopyFile(inFn: String; outFn: String; failIfExists: Boolean);
+var
inStream, outStream: TFilestream;
exists: Boolean;
-BEGIN
+begin
exists := FileExists(outFn);
- IF FileExists(inFn) AND ((NOT exists) OR (exists AND NOT failIfExists)) THEN
- BEGIN
- inStream := TFilestream.Create(inFn, fmOpenRead);
- TRY
- outStream := TFilestream.Create(inFn, fmOpenwrite);
- TRY
+ if FileExists(inFn) and ((not exists) or (exists and not failIfExists)) then
+ begin
+ inStream := TBufferedFileStream.Create(inFn, fmOpenRead or fmShareDenyWrite);
+ try
+ outStream := TBufferedFileStream.Create(inFn, fmOpenwrite);
+ try
outStream.Position := outStream.size;
outStream.CopyFrom(inStream, 0);
- FINALLY
+ finally
inStream.Free;
- END;
- FINALLY
+ end;
+ finally
outStream.Free;
- END;
- END;
-END;
+ end;
+ end;
+end;
{$ENDIF}
-{*******************************************************************************
-* This routine reads the master file of the torn circuit and creates the *
-* header definitions for declaring separate subcircuits in OpenDSS *
-********************************************************************************}
+// This routine reads the master file of the torn circuit and creates the
+// header definitions for declaring separate subcircuits in OpenDSS
procedure TDSSCircuit.Format_SubCircuits(Path: String; NumCkts: Integer; AddISrc: Boolean);
var
- myFile : TFileStream = nil;
- Temp_txt,
- Temp_txt2,
- text : string;
- Xtra,
- File_Struc : Array of String;
- Str_Found : Boolean;
- Local_Temp,
- FS_Idx,
- FS_Idx1,
- FS_Idx2 : Integer;
+ myFile: TFileStream = NIL;
+ Temp_txt,
+ Temp_txt2,
+ text: String;
+ Xtra,
+ File_Struc: array of String;
+ Str_Found: Boolean;
+ Local_Temp,
+ FS_Idx,
+ FS_Idx1,
+ FS_Idx2: Integer;
const
- Reference : array[0..5] of string = // To filter the source file
- ('Redirect EnergyM', 'Redirect Monitor', 'MakeBu', 'Redirect BusVolta', 'Buscoords busco', 'Redirect zone');
+ Reference: array[0..5] of String = // To filter the source file
+ ('Redirect EnergyM', 'Redirect Monitor', 'MakeBu', 'Redirect BusVolta', 'Buscoords busco', 'Redirect zone');
begin
// Reads the master file
- myFile := TFileStream.Create(Path + PathDelim + 'master.dss', fmOpenRead);
- setlength(File_Struc,0);
- FS_Idx := 0;
- while (myFile.Position + 1) < myFile.Size do // Extracts the file content as an array of strings
+ myFile := TBufferedFileStream.Create(Path + PathDelim + 'Master.dss', fmOpenRead or fmShareDenyWrite);
+ setlength(File_Struc, 0);
+ FS_Idx := 0;
+ while (myFile.Position + 1) < myFile.Size do // Extracts the file content as an array of strings
begin
- setlength(File_Struc,(length(File_Struc) + 1));
- FSReadLn(myFile, text);
- File_Struc[FS_Idx] := text;
- inc(FS_Idx);
+ setlength(File_Struc, (length(File_Struc) + 1));
+ FSReadLn(myFile, text);
+ File_Struc[FS_Idx] := text;
+ inc(FS_Idx);
end;
FreeAndNil(myFile);
- // Creates the copy for the interconnected system
- setlength(Xtra,0);
-
- myFile := TFileStream.Create(Path + PathDelim + 'Master_Interconnected.dss', fmCreate);
+ // Creates the copy for the interconnected system
+ setlength(Xtra, 0);
+
+ myFile := TBufferedFileStream.Create(Path + PathDelim + 'Master_Interconnected.dss', fmCreate);
for FS_Idx := 0 to High(File_Struc) do
- Begin
- Str_Found := False;
- for FS_Idx1 := 0 to 5 do
- Begin
- Local_Temp := ansipos(Reference[FS_Idx1], File_Struc[FS_Idx]);
- Str_Found := (Local_Temp <> 0) or Str_Found;
- End;
- if Str_found then
- Begin
- setlength(Xtra,(length(Xtra) + 1));
- Xtra[High(Xtra)] := File_Struc[FS_Idx];
- End
- else FSWriteLn(myFile,File_Struc[FS_Idx]);
- End;
+ begin
+ Str_Found := FALSE;
+ for FS_Idx1 := 0 to 5 do
+ begin
+ Local_Temp := ansipos(Reference[FS_Idx1], File_Struc[FS_Idx]);
+ Str_Found := (Local_Temp <> 0) or Str_Found;
+ end;
+ if Str_found then
+ begin
+ setlength(Xtra, (length(Xtra) + 1));
+ Xtra[High(Xtra)] := File_Struc[FS_Idx];
+ end
+ else
+ FSWriteLn(myFile, File_Struc[FS_Idx]);
+ end;
// Adds the zones and the rest to the file
for FS_Idx := 0 to High(Xtra) do
- Begin
- FSWriteLn(myFile,Xtra[FS_Idx])
- End;
+ begin
+ FSWriteLn(myFile, Xtra[FS_Idx])
+ end;
FreeAndNil(myFile);
// removes the unnecessary information from the master file (deletes the other zones)
- myFile := TFileStream.Create(Path + PathDelim + 'master.dss', fmCreate);
+ myFile := TBufferedFileStream.Create(Path + PathDelim + 'Master.dss', fmCreate);
for FS_Idx := 0 to High(File_Struc) do
- Begin
- Local_Temp := ansipos('Redirect zone', File_Struc[FS_Idx]);
- if Local_Temp = 0 then
- Begin
- Local_Temp := ansipos('Redirect EnergyM', File_Struc[FS_Idx]);
- if Local_Temp = 0 then
- Begin
- Local_Temp := ansipos('Redirect Monitor', File_Struc[FS_Idx]);
- if Local_Temp = 0 then
- FSWriteLn(myFile,File_Struc[FS_Idx]);
- End;
- End;
- End;
+ begin
+ Local_Temp := ansipos('Redirect zone', File_Struc[FS_Idx]);
+ if Local_Temp = 0 then
+ begin
+ Local_Temp := ansipos('Redirect EnergyM', File_Struc[FS_Idx]);
+ if Local_Temp = 0 then
+ begin
+ Local_Temp := ansipos('Redirect Monitor', File_Struc[FS_Idx]);
+ if Local_Temp = 0 then
+ FSWriteLn(myFile, File_Struc[FS_Idx]);
+ end;
+ end;
+ end;
FreeAndNil(myFile);
-
+
// Adds Isources at the link branch edges if requested
if AddISrc then
- AppendIsources(Path + PathDelim + 'master.dss', 1, Link_Branches[1]);
+ AppendIsources(Path + PathDelim + 'Master.dss', 1, Link_Branches[1]);
// Copies the support files to the zones directories
- FS_Idx := 0;
+ FS_Idx := 0;
while FS_Idx <> -1 do
- Begin
- Local_Temp := ansipos('Redirect zone', File_Struc[FS_Idx]);
- if Local_Temp = 0 then
- Begin
- Local_Temp := ansipos('Redirect ', File_Struc[FS_Idx]);
- if Local_temp <> 0 then
- Begin
- text := stringreplace(File_Struc[FS_Idx], 'Redirect ', '',[rfReplaceAll, rfIgnoreCase]);
- for FS_Idx1 := 2 to NumCkts do
- CopyFile(PChar(Path + PathDelim + text), PChar(Path + PathDelim + 'zone_' + inttostr(FS_Idx1) + PathDelim + text), true);
- End;
- inc(FS_Idx);
- End
- else
- FS_Idx := -1; // Ends the routine
- End;
+ begin
+ Local_Temp := ansipos('Redirect zone', File_Struc[FS_Idx]);
+ if Local_Temp = 0 then
+ begin
+ Local_Temp := ansipos('Redirect ', File_Struc[FS_Idx]);
+ if Local_temp <> 0 then
+ begin
+ text := stringreplace(File_Struc[FS_Idx], 'Redirect ', '', [rfReplaceAll, rfIgnoreCase]);
+ for FS_Idx1 := 2 to NumCkts do
+ CopyFile(Pchar(Path + PathDelim + text), Pchar(Path + PathDelim + 'zone_' + inttostr(FS_Idx1) + PathDelim + text), TRUE);
+ end;
+ inc(FS_Idx);
+ end
+ else
+ FS_Idx := -1; // Ends the routine
+ end;
// Creates the master file for each subcircuit
for FS_Idx := 2 to NumCkts do
- Begin
- myFile := TFileStream.Create(Path + PathDelim + 'zone_' + inttostr(FS_Idx) + PathDelim + 'master.dss', fmCreate);
- FSWriteLn(myFile,'Clear');
- FSWriteLn(myFile,'New Circuit.Zone_' + inttostr(FS_Idx));
- FS_Idx1 := 2;
- while FS_Idx1 <> -1 do // Writes the global files
- Begin
- Local_Temp := ansipos('Redirect zone', File_Struc[FS_Idx1]);
- if Local_Temp = 0 then
- Begin
- FSWriteLn(myFile,File_Struc[FS_Idx1]);
- inc(FS_Idx1);
- End
- else
- FS_Idx1 := -1;
- End;
- for FS_Idx1 := 0 to High(File_Struc) do // Writes the zone files
- Begin
- Local_Temp := ansipos('Redirect zone_' + inttostr(FS_Idx), File_Struc[FS_Idx1]);
- if Local_Temp <> 0 then
- Begin
- text := stringreplace(File_Struc[FS_Idx1], 'zone_' + inttostr(FS_Idx) + PathDelim, '',[rfReplaceAll, rfIgnoreCase]);
- FSWriteLn(myFile,text);
- End;
- End;
- FreeAndNil(myFile);
- // Adds Isources at the link branch edges if requested
- if AddISrc then
- Begin
- text := Path + PathDelim + 'zone_' + inttostr(FS_Idx) + PathDelim + 'master.dss';
- AppendIsources(text, 2, Link_Branches[FS_Idx - 1]);
- // If there is another link branch, means that this zone conencts with other through ZCC
- // Add Another current source at the point of connection
- if Length(Link_Branches) > FS_Idx then
- AppendIsources(text, 1, Link_Branches[FS_Idx]);
-
- End;
-
- End;
+ begin
+ myFile := TBufferedFileStream.Create(Path + PathDelim + 'zone_' + inttostr(FS_Idx) + PathDelim + 'Master.dss', fmCreate);
+ FSWriteLn(myFile, 'Clear');
+ FSWriteLn(myFile, 'New Circuit.Zone_' + inttostr(FS_Idx));
+ FS_Idx1 := 2;
+ while FS_Idx1 <> -1 do // Writes the global files
+ begin
+ Local_Temp := ansipos('Redirect zone', File_Struc[FS_Idx1]);
+ if Local_Temp = 0 then
+ begin
+ FSWriteLn(myFile, File_Struc[FS_Idx1]);
+ inc(FS_Idx1);
+ end
+ else
+ FS_Idx1 := -1;
+ end;
+ for FS_Idx1 := 0 to High(File_Struc) do // Writes the zone files
+ begin
+ Local_Temp := ansipos('Redirect zone_' + inttostr(FS_Idx), File_Struc[FS_Idx1]);
+ if Local_Temp <> 0 then
+ begin
+ text := stringreplace(File_Struc[FS_Idx1], 'zone_' + inttostr(FS_Idx) + PathDelim, '', [rfReplaceAll, rfIgnoreCase]);
+ FSWriteLn(myFile, text);
+ end;
+ end;
+ FreeAndNil(myFile);
+ // Adds Isources at the link branch edges if requested
+ if AddISrc then
+ begin
+ text := Path + PathDelim + 'zone_' + inttostr(FS_Idx) + PathDelim + 'Master.dss';
+ AppendIsources(text, 2, Link_Branches[FS_Idx - 1]);
+ // If there is another link branch, means that this zone conencts with other through ZCC
+ // Add Another current source at the point of connection
+ if Length(Link_Branches) > FS_Idx then
+ AppendIsources(text, 1, Link_Branches[FS_Idx]);
+
+ end;
+
+ end;
// Sets the properties of the VSource on each subcricuit based on the latest voltage measured
- FS_Idx1 := 0;
+ FS_Idx1 := 0;
for FS_Idx := 1 to NumCkts do
- Begin
- if FS_Idx = 1 then
- myFile := TFileStream.Create(Path + PathDelim + 'VSource.dss', fmCreate)
- else
- myFile := TFileStream.Create(Path + PathDelim + 'zone_' + inttostr(FS_Idx) + PathDelim + 'VSource.dss', fmCreate);
-
- for FS_Idx2 := 1 to 3 do
- Begin
- if FS_Idx2 = 1 then
- Begin
- Temp_txt := 'source';
- Temp_txt2 := 'Edit '
- End
- else
- Begin
- Temp_txt := 'Vph_' + inttostr(FS_Idx2);
- Temp_txt2 := 'New '
- End;
-
- text := Temp_txt2 + 'Vsource.' + Temp_txt +
- ' bus1=' + PConn_Names[FS_Idx - 1] + '.' + inttostr(FS_Idx2) +
- ' phases=1 pu=1.0' +
- ' basekv=' + floattostrF(PConn_Voltages[FS_Idx1],ffGeneral, 8, 3) +
- ' angle=' + floattostrF(PConn_Voltages[FS_Idx1 + 1],ffGeneral, 8, 3) +
- ' R1=0 X1=0.001 R0=0 X0=0.001';
- FSWriteLn(myFile,text);
- FS_Idx1 := FS_Idx1 + 2;
- End;
- FreeAndNil(myFile);
- End;
+ begin
+ if FS_Idx = 1 then
+ myFile := TBufferedFileStream.Create(Path + PathDelim + 'VSource.dss', fmCreate)
+ else
+ myFile := TBufferedFileStream.Create(Path + PathDelim + 'zone_' + inttostr(FS_Idx) + PathDelim + 'VSource.dss', fmCreate);
+
+ for FS_Idx2 := 1 to 3 do
+ begin
+ if FS_Idx2 = 1 then
+ begin
+ Temp_txt := 'source';
+ Temp_txt2 := 'Edit '
+ end
+ else
+ begin
+ Temp_txt := 'Vph_' + inttostr(FS_Idx2);
+ Temp_txt2 := 'New '
+ end;
+
+ text := Temp_txt2 + 'Vsource.' + Temp_txt +
+ ' bus1=' + PConn_Names[FS_Idx - 1] + '.' + inttostr(FS_Idx2) +
+ ' phases=1 pu=1.0' +
+ ' basekv=' + floattostrF(PConn_Voltages[FS_Idx1], ffGeneral, 8, 3) +
+ ' angle=' + floattostrF(PConn_Voltages[FS_Idx1 + 1], ffGeneral, 8, 3) +
+ ' R1=0 X1=0.001 R0=0 X0=0.001';
+ FSWriteLn(myFile, text);
+ FS_Idx1 := FS_Idx1 + 2;
+ end;
+ FreeAndNil(myFile);
+ end;
end;
-{*******************************************************************************
-* Saves the subcircuits created in memory into the hard drive *
-* The flag AddISrc indicates if its necessary to create *
-* Isources at the edges of the link branches, the ISource *
-* magnitude is equal to 0.000001, angle 0 (for A-Diakoptics) *
-********************************************************************************
-}
+// Saves the subcircuits created in memory into the hard drive
+// The flag AddISrc indicates if its necessary to create
+// Isources at the edges of the link branches, the ISource
+// magnitude is equal to 0.000001, angle 0 (for A-Diakoptics)
procedure TDSSCircuit.Save_SubCircuits(AddISrc: Boolean);
var
- Fileroot : String;
-Begin
+ Fileroot: String;
+begin
// Prepares everything to save the base of the torn circuit on a separate folder
- Fileroot := DSS.OutputDirectory; {CurrentDSSDir;}
- Fileroot := Fileroot + PathDelim + 'Torn_Circuit';
- CreateDir(Fileroot); // Creates the folder for storing the modified circuit
- DelFilesFromDir(Fileroot,'*',True); // Removes all the files inside the new directory (if exists)
- DSS.DssExecutive.Command := 'save circuit Dir="' + Fileroot + '"';
+ Fileroot := DSS.OutputDirectory; {CurrentDSSDir;}
+ Fileroot := Fileroot + PathDelim + 'Torn_Circuit';
+ CreateDir(Fileroot); // Creates the folder for storing the modified circuit
+ DelFilesFromDir(Fileroot); // Removes all the files inside the new directory (if exists)
+ DSS.DssExecutive.Command := 'save circuit Dir="' + Fileroot + '"';
// This routine extracts and modifies the file content to separate the subsystems as OpenDSS projects indepedently
Format_SubCircuits(FileRoot, length(Locations), AddISrc);
-End;
-
-{*******************************************************************************
-* Delivers the name of the bus at the specific line and terminal *
-*******************************************************************************}
-Function TDSSCircuit.get_Line_Bus(LName: String; NBus: Integer):String;
-VAR
- i,
- activesave : integer;
- pLine : TLineObj;
- S : String;
- Found : Boolean;
- NBuses : Array of String;
-Begin
- Result := '';
- setlength(NBuses,2);
- IF DSS.ActiveCircuit <> NIL THEN // TODO: check why this is not using self
- Begin // Search list of Lines in active circuit for name
- WITH DSS.ActiveCircuit.Lines DO
- Begin
- S := LName; // Convert to Pascal String
- Found := FALSE;
- ActiveSave := ActiveIndex;
- pLine := First;
- While pLine <> NIL Do
- Begin
- IF (CompareText(pLine.Name, S) = 0) THEN
- Begin
- DSS.ActiveCircuit.ActiveCktElement := pLine;
- Found := TRUE;
- Break;
- End;
- pLine := Next;
- End;
- IF NOT Found THEN
- Begin
- DoSimpleMsg(DSS, 'Line "'+S+'" Not Found in Active Circuit.', 5008);
- pLine := Get(ActiveSave); // Restore active Line
- DSS.ActiveCircuit.ActiveCktElement := pLine;
- End;
- End;
- For i := 1 to DSS.ActiveCircuit.ActiveCktElement.Nterms Do
- NBuses[i-1] := DSS.ActiveCircuit.ActiveCktElement.GetBus(i);
- // returns the name of the desired bus
- Result := NBuses[NBus - 1];
- End;
end;
-{*******************************************************************************
-* Generates the graph file for MeTIS within the project's folder *
-*******************************************************************************}
-function TDSSCircuit.Create_MeTIS_graph(): string;
+// Generates the graph file for MeTIS within the project's folder
+function TDSSCircuit.Create_MeTIS_graph(): String;
var
- exists : Boolean;
- myIntVar,
- k,
- jj,
- i : Integer;
- myClass,
- myName,
- FileName : String;
- F: TFileStream = nil;
- myPDEList,
- MyGraph : array of string;
- MyIdx : array of integer;
-Begin
- with solution do
- Begin
-
- // Calculates the incidence matrix and laplacian to generate the graph file to be
- // send to MeTiS
- Calc_Inc_Matrix_Org; //Calculates the ordered incidence matrix
- // Initializes the METIS related variables
- setlength(myPDEList,1);
- setlength(myGraph,1);
-
- Laplacian := IncMat.Transpose(); // Transposes the Incidence Matrix
- Laplacian := Laplacian.multiply(IncMat); // Laplacian Matrix calculated
- // Filters the incidence matrix to remove duplicated branches (parallel)
- for i := 0 to High(Inc_Mat_Cols) do
- Begin
- setlength(myIdx,1);
- MyName := Inc_Mat_Cols[i];
- // first, get the name of all PDE conencted to this Bus
- for jj := 0 to (IncMat.NZero - 1) do
- Begin
- if IncMat.data[jj][1] = i then
- Begin
- // Check if this is not a parallel branch
- exists := False;
- if length(myIdx) > 1 then // Only if it's not the first time
- Begin
- for k := 0 to (length(myIdx) - 2) div 2 do
- Begin
- // Checks for the other terminal
- if jj < High(IncMat.data) then
- Begin
- if IncMat.data[jj + 1][0] = IncMat.data[jj][0] then myIntVar := IncMat.data[jj + 1][1]
- else myIntVar := IncMat.data[jj - 1][1];
- End
- else myIntVar := IncMat.data[jj - 1][1];
-
- if myIdx[k * 2] = myIntVar then
- Begin
- exists := true;
- break;
- End;
- End;
- End;
-
- if not exists then
- Begin
- // Stores the name of the PDE
- myName := Inc_Mat_Rows[IncMat.data[jj][0]];
- myPDEList[High(myPDEList)] := myName;
- setlength(myPDEList,length(myPDEList) + 1);
- // Checks for the other terminal
- if jj < High(IncMat.data) then
- Begin
- if IncMat.data[jj + 1][0] = IncMat.data[jj][0] then myIdx[High(myIdx)] := IncMat.data[jj + 1][1]
- else myIdx[High(myIdx)] := IncMat.data[jj - 1][1];
- End
- else myIdx[High(myIdx)] := IncMat.data[jj - 1][1];
-
- setlength(myIdx, length(myIdx) + 1);
- // Now, get the number of Phases
- myIntVar := ansipos('.',myName);
- myClass := myName.Substring(0,(myIntVar - 1));
- // if transformer, the weigth is the lowest
- if myClass <> 'Transformer' then
- Begin
- DSS.ActiveCircuit.SetElementActive(MyName);
- myIntVar := DSS.ActiveCircuit.ActiveCktElement.NPhases;
- end
- else
- myIntVar := 1;
-
- myIdx[High(myIdx)] := myIntVar;
- setlength(myIdx, length(myIdx) + 1);
- End;
- End;
- End;
- setlength(myIdx, length(myIdx) - 1);
- myName := '';
- for jj := 0 to High(myIdx) do myName := myName + inttostr(myIdx[jj]) + ' ';
- myGraph[High(myGraph)] := myName;
- setlength(myGraph,length(myGraph) + 1);
-
- End;
- setlength(myGraph,length(myGraph) - 1);
- setlength(myPDEList,length(myPDEList) - 1);
-{*******************************************************************************
- Generates the graph file
-********************************************************************************}
- // First, get the number of branches in the model excluding parallel branches
- jj := 0;
- for i := 0 to High(Inc_Mat_Rows) do
- Begin
- // check if it's on the list
- for k := 0 to High(myPDEList) do
- Begin
- if LowerCase(Inc_Mat_Rows[i]) = LowerCase(myPDEList[k]) then
- Begin
- inc(jj);
- break;
- End;
- End;
- End;
-
- FileName := DSS.OutputDirectory + DSS.CircuitName_ + '.graph';
- F := TFileStream.Create(FileName, fmCreate);
- FSWriteln(F,inttostr(length(Inc_Mat_Cols)) + ' ' + inttostr(jj) + ' 1'); // it should be the rank of the incidence matrix
- for i := 1 to High(myGraph) do
- FSWriteln(F,myGraph[i]);
- F.Free();
-
- End;
-
- Result := FileName;
-
-End;
-
-{*******************************************************************************
-* Executes MeTIS and gets the names of the link branches between zones *
-********************************************************************************}
-
-Function TDSSCircuit.Create_MeTIS_Zones(Filename : string): string;
+ exists: Boolean;
+ myIntVar,
+ k,
+ jj,
+ i: Integer;
+ myClass,
+ myName,
+ FileName: String;
+ F: TFileStream = NIL;
+ myPDEList,
+ MyGraph: array of String;
+ MyIdx: array of Integer;
+begin
+ with solution do
+ begin
+ // Calculates the incidence matrix and laplacian to generate the graph file to be
+ // send to MeTiS
+ Calc_Inc_Matrix_Org; //Calculates the ordered incidence matrix
+ // Initializes the METIS related variables
+ setlength(myPDEList, 1);
+ setlength(myGraph, 1);
+
+ Laplacian := IncMat.Transpose(); // Transposes the Incidence Matrix
+ Laplacian := Laplacian.multiply(IncMat); // Laplacian Matrix calculated
+ // Filters the incidence matrix to remove duplicated branches (parallel)
+ for i := 0 to High(Inc_Mat_Cols) do
+ begin
+ setlength(myIdx, 1);
+ MyName := Inc_Mat_Cols[i];
+ // first, get the name of all PDE conencted to this Bus
+ for jj := 0 to (IncMat.NZero - 1) do
+ begin
+ if IncMat.data[jj][1] = i then
+ begin
+ // Check if this is not a parallel branch
+ exists := FALSE;
+ if length(myIdx) > 1 then // Only if it's not the first time
+ begin
+ for k := 0 to (length(myIdx) - 2) div 2 do
+ begin
+ // Checks for the other terminal
+ if jj < High(IncMat.data) then
+ begin
+ if IncMat.data[jj + 1][0] = IncMat.data[jj][0] then
+ myIntVar := IncMat.data[jj + 1][1]
+ else
+ myIntVar := IncMat.data[jj - 1][1];
+ end
+ else
+ myIntVar := IncMat.data[jj - 1][1];
+
+ if myIdx[k * 2] = myIntVar then
+ begin
+ exists := TRUE;
+ break;
+ end;
+ end;
+ end;
+
+ if not exists then
+ begin
+ // Stores the name of the PDE
+ myName := Inc_Mat_Rows[IncMat.data[jj][0]];
+ myPDEList[High(myPDEList)] := myName;
+ setlength(myPDEList, length(myPDEList) + 1);
+ // Checks for the other terminal
+ if jj < High(IncMat.data) then
+ begin
+ if IncMat.data[jj + 1][0] = IncMat.data[jj][0] then
+ myIdx[High(myIdx)] := IncMat.data[jj + 1][1]
+ else
+ myIdx[High(myIdx)] := IncMat.data[jj - 1][1];
+ end
+ else
+ myIdx[High(myIdx)] := IncMat.data[jj - 1][1];
+
+ setlength(myIdx, length(myIdx) + 1);
+ // Now, get the number of Phases
+ myIntVar := ansipos('.', myName);
+ myClass := myName.Substring(0, (myIntVar - 1));
+ // if transformer, the weigth is the lowest
+ if myClass <> 'Transformer' then
+ begin
+ DSS.ActiveCircuit.SetElementActive(MyName);
+ myIntVar := DSS.ActiveCircuit.ActiveCktElement.NPhases;
+ end
+ else
+ myIntVar := 1;
+
+ myIdx[High(myIdx)] := myIntVar;
+ setlength(myIdx, length(myIdx) + 1);
+ end;
+ end;
+ end;
+ setlength(myIdx, length(myIdx) - 1);
+ myName := '';
+ for jj := 0 to High(myIdx) do
+ myName := myName + inttostr(myIdx[jj]) + ' ';
+ myGraph[High(myGraph)] := myName;
+ setlength(myGraph, length(myGraph) + 1);
+
+ end;
+ setlength(myGraph, length(myGraph) - 1);
+ setlength(myPDEList, length(myPDEList) - 1);
+
+ // Generates the graph file
+ // First, get the number of branches in the model excluding parallel branches
+ jj := 0;
+ for i := 0 to High(Inc_Mat_Rows) do
+ begin
+ // check if it's on the list
+ for k := 0 to High(myPDEList) do
+ begin
+ if AnsiLowerCase(Inc_Mat_Rows[i]) = AnsiLowerCase(myPDEList[k]) then
+ begin
+ inc(jj);
+ break;
+ end;
+ end;
+ end;
+
+ FileName := DSS.OutputDirectory + DSS.CircuitName_ + '.graph';
+ F := TBufferedFileStream.Create(FileName, fmCreate);
+ FSWriteln(F, inttostr(length(Inc_Mat_Cols)) + ' ' + inttostr(jj) + ' 1'); // it should be the rank of the incidence matrix
+ for i := 1 to High(myGraph) do
+ FSWriteln(F, myGraph[i]);
+ F.Free();
+
+ end;
+
+ Result := FileName;
+end;
+
+// Executes MeTIS and gets the names of the link branches between zones
+function TDSSCircuit.Create_MeTIS_Zones(Filename: String): String;
var
- MeTISCmd,
- TextCmd: String;
- Num_Pieces,
- j,jj,
- i: Integer;
- Replacer: TFileSearchReplace;
-Begin
- Num_pieces := Num_SubCkts;
- with solution do
- Begin
-{******************************************************************************************}
+ MeTISCmd,
+ TextCmd: String;
+ Num_Pieces,
+ j, jj,
+ i: Integer;
+ Replacer: TFileSearchReplace;
+ Flag: Boolean;
+begin
+ Num_pieces := Num_SubCkts;
+ with solution do
+ begin
{$IFNDEF UNIX}
// if Num_pieces <= 8 then MeTISCmd := 'kmetis.exe' // For less than 8 zones use pMeTIS
// else MeTISCmd := 'kmetis.exe'; // For more than 8 zonez use k-Way (kMeTIS)
// In the past we use to use pmetis and kmetis, however, in our latest update we realized kmetis is enough
// update 09-24-2020 by Davis Montenegro
- MeTISCmd := 'kmetis.exe';
+ MeTISCmd := 'kmetis.exe';
{$ELSE}
- MeTISCmd := 'kmetis';
+ MeTISCmd := 'kmetis';
{$ENDIF}
- {******************************************************************************************}
- if fileexists(pchar(FileName + '.part.' + inttostr(Num_pieces))) then // Checks if the file exists before
- deletefile(pchar(FileName + '.part.' + inttostr(Num_pieces)));
- repeat
-{$IFNDEF FPC}
- TextCmd := RunMeTIS(DSSDirectory + MeTISCmd + ' "' + FileName + '" ' + inttostr(Num_pieces)); // Executes MeTIS
-{$ELSE}
- Process.RunCommand(DSSDirectory + MeTISCmd, [Filename, inttostr(Num_pieces)], TextCmd); // Executes MeTIS
- {$ENDIF}
- Flag := {$IFDEF FPC}ANSIContainsText{$ELSE}ContainsText{$ENDIF}(TextCmd,'I detected an error');
- if Flag then // The # of edges was wrong, use the one proposed by MeTIS
- Begin
- TextCmd := GetNumEdges(TextCmd); // Gest the # of edges proposed by MeTIS
- jj := length(inttostr(length(Inc_Mat_Cols))) + 2;// Caculates the index for replacing the number in the Graph File
- // Replaces the old data with the new at the file header
- Replacer:=TFileSearchReplace.Create(FileName);
- try
- Replacer.Replace(inttostr(length(Inc_Mat_Cols)) + ' ' + inttostr(length(Inc_Mat_Cols) - 1),
- inttostr(length(Inc_Mat_Cols)) + ' ' + TextCmd, [rfIgnoreCase]);
- finally
- Replacer.Free;
- end;
- End;
- until Not flag;
- {******************************************************************************************}
- // Verifies if there was no error executing MeTIS and the zones file was created
- if (TextCmd <> '**Error**') and fileexists(pchar(FileName + '.part.' + inttostr(Num_pieces))) then
- Begin
- MeTISZones := TStringList.Create; // Opens the file containing the tearing results
- MeTISZones.LoadFromFile(FileName + '.part.' + inttostr(Num_pieces));
- TextCmd := MeTISZones.Strings[1];
- MeTISZones.Delete(0);
- MetisZones.Insert(0, TextCmd);
- setlength(Locations,1);
- setlength(BusZones,1);
- for i := 0 to (MeTISZones.Count - 1) do
- Begin
- if i = 0 then
- Begin
- Locations[i] := 0;
- BusZones[i] := MeTISZones[i];
- End
- else
- Begin
- if MeTISZones[i] <> BusZones[high(BusZones)] then // Moving to another zone in the file
- Begin
- j := 0;
- if i < (MeTISZones.Count - 1) then // If not lower means the zone is only 1 bus
- j := Integer(MeTISZones[i] = MeTISZones[i + 1]);
- if j = 1 then // Varifies that the zone is big enough
- Begin
- j := 0; // Verifies that this zone hasn't been counted before
- for jj := 0 to High(BusZones) do
- Begin
- if MeTISZones[i] = BusZones[jj] then
- Begin
- inc(j);
- Break;
- End;
- End;
- if j = 0 then // Is not in the list, add the new location
- Begin
- setlength(Locations,Length(Locations) + 1);
- setlength(BusZones,Length(BusZones) + 1);
- Locations[High(Locations)] := i;
- BusZones[High(BusZones)] := MeTISZones[i];
- End;
- End;
- End;
- End;
- End;
-
- End;
- for j := 0 to High(Locations) do inc(Locations[j]); //Adjust the location coords
-
- End;
- Result := TextCmd
-
-End;
-
-{*******************************************************************************
-* Disables all DER present in the model *
-********************************************************************************}
-procedure TDSSCircuit.Disable_All_DER();
-var
- myDERIdx,
- myIdx,
- DevClassIndex : Integer;
- myDERList : array of string;
-
-Begin
- setlength(myDERList,3);
- myDERList := ['PVSystem','Generator','Storage'];
-
- for myDERIdx := 0 to High(myDERList) do
- Begin
- DevClassIndex := DSS.ClassNames.Find(myDERList[myDERIdx]);
- DSS.LastClassReferenced := DevClassIndex;
- DSS.ActiveDSSClass := DSS.DSSClassList.Get(DSS.LastClassReferenced);
- if DSS.ActiveDSSClass.ElementCount > 0 then
- Begin
- myIdx := DSS.ActiveDSSClass.First;
- Repeat
- ActiveCktElement.Enabled := False ;
- myIdx := DSS.ActiveDSSClass.Next;
- Until (myIdx <= 0);
- End;
- End;
-End;
-
-{*******************************************************************************
-* Returns the list of all PDE connected to the bus name given at BusName *
-********************************************************************************}
-function TDSSCircuit.getPDEatBus(BusName: String; useNone: Boolean): ArrayOfString;
-Var
- Dss_Class: TDSSClass;
- j, i: integer;
- myBus: Array of String;
-Begin
- SetLength(myBus, 2);
- SetLength(Result, 0);
- BusName := LowerCase(BusName);
- for i := 1 to DSS.DSSClassList.Count do
- begin
- Dss_Class := DSS.DSSClassList.Get(i);
- if (DSS_Class is TCktElementClass) then
+ if fileexists(Pchar(FileName + '.part.' + inttostr(Num_pieces))) then // Checks if the file exists before
+ deletefile(Pchar(FileName + '.part.' + inttostr(Num_pieces)));
+ repeat
+ Process.RunCommand(DSSDirectory + MeTISCmd, [Filename, inttostr(Num_pieces)], TextCmd); // Executes MeTIS
+ Flag := ANSIContainsText(TextCmd, 'I detected an error');
+ if Flag then // The # of edges was wrong, use the one proposed by MeTIS
+ begin
+ TextCmd := GetNumEdges(TextCmd); // Gest the # of edges proposed by MeTIS
+ jj := length(inttostr(length(Inc_Mat_Cols))) + 2;// Caculates the index for replacing the number in the Graph File
+ // Replaces the old data with the new at the file header
+ Replacer := TFileSearchReplace.Create(FileName);
+ try
+ Replacer.Replace(inttostr(length(Inc_Mat_Cols)) + ' ' + inttostr(length(Inc_Mat_Cols) - 1),
+ inttostr(length(Inc_Mat_Cols)) + ' ' + TextCmd, [rfIgnoreCase]);
+ finally
+ Replacer.Free;
+ end;
+ end;
+ until not flag;
+
+ // Verifies if there was no error executing MeTIS and the zones file was created
+ if (TextCmd <> '**Error**') and fileexists(Pchar(FileName + '.part.' + inttostr(Num_pieces))) then
begin
- // Checks if it is a PCE class
- if not (DSS_Class.ClassType.InheritsFrom(TPDClass)) then
- continue;
-
- // If it is, checks all the elements to verify if one or more are
- // connected to the bus given
- DSS_Class.First;
- for j := 1 to DSS_Class.ElementCount do
+ MeTISZones := TStringList.Create; // Opens the file containing the tearing results
+ MeTISZones.LoadFromFile(FileName + '.part.' + inttostr(Num_pieces));
+ TextCmd := MeTISZones.Strings[1];
+ MeTISZones.Delete(0);
+ MetisZones.Insert(0, TextCmd);
+ setlength(Locations, 1);
+ setlength(BusZones, 1);
+ for i := 0 to (MeTISZones.Count - 1) do
begin
- myBus[0] := LowerCase(StripExtension(ActiveCktElement.GetBus(1)));
- myBus[1] := LowerCase(StripExtension(ActiveCktElement.GetBus(2)));
- if ((myBus[0] = BusName) or (myBus[1] = BusName)) and (myBus[0] <> myBus[1]) then
+ if i = 0 then
begin
- SetLength(Result, length(Result) + 1);
- Result[High(Result)] := DSS_Class.Name + '.' + ActiveCktElement.Name;
+ Locations[i] := 0;
+ BusZones[i] := MeTISZones[i];
+ end
+ else
+ begin
+ if MeTISZones[i] <> BusZones[high(BusZones)] then // Moving to another zone in the file
+ begin
+ j := 0;
+ if i < (MeTISZones.Count - 1) then // If not lower means the zone is only 1 bus
+ j := Integer(MeTISZones[i] = MeTISZones[i + 1]);
+ if j = 1 then // Varifies that the zone is big enough
+ begin
+ j := 0; // Verifies that this zone hasn't been counted before
+ for jj := 0 to High(BusZones) do
+ begin
+ if MeTISZones[i] = BusZones[jj] then
+ begin
+ inc(j);
+ Break;
+ end;
+ end;
+ if j = 0 then // Is not in the list, add the new location
+ begin
+ setlength(Locations, Length(Locations) + 1);
+ setlength(BusZones, Length(BusZones) + 1);
+ Locations[High(Locations)] := i;
+ BusZones[High(BusZones)] := MeTISZones[i];
+ end;
+ end;
+ end;
end;
- DSS_Class.Next;
end;
+
end;
+ for j := 0 to High(Locations) do
+ inc(Locations[j]); //Adjust the location coords
+
end;
- if (length(Result) = 0) and useNone then
+ Result := TextCmd
+
+end;
+
+// Disables all DER present in the model
+procedure TDSSCircuit.Disable_All_DER();
+var
+ myDERIdx,
+ myIdx,
+ DevClassIndex: Integer;
+ myDERList: array of String;
+
+begin
+ setlength(myDERList, 3);
+ myDERList := ['PVSystem', 'Generator', 'Storage'];
+
+ for myDERIdx := 0 to High(myDERList) do
begin
- SetLength(Result, 1);
- Result[0] := 'None';
+ DevClassIndex := DSS.ClassNames.Find(myDERList[myDERIdx]);
+ DSS.LastClassReferenced := DevClassIndex;
+ DSS.ActiveDSSClass := DSS.DSSClassList.Get(DSS.LastClassReferenced);
+ if DSS.ActiveDSSClass.ElementCount > 0 then
+ begin
+ myIdx := DSS.ActiveDSSClass.First;
+ repeat
+ ActiveCktElement.Enabled := FALSE;
+ myIdx := DSS.ActiveDSSClass.Next;
+ until (myIdx <= 0);
+ end;
end;
end;
-{*******************************************************************************
-* Returns the list of all PCE connected to the bus nam given at BusName *
-********************************************************************************}
-function TDSSCircuit.getPCEatBus(BusName: String; useNone: Boolean): ArrayOfString;
+
+// Aggregates profiles using the number of zones defined by the user
+procedure TDSSCircuit.AggregateProfiles(mode: String);
var
- Dss_Class: TDSSClass;
- j, i: integer;
- myBus: String;
+ F: TFileStream = NIL;
+ FileRoot,
+ myPCE,
+ TextCmd,
+ myFilename: String;
+ iElem,
+ k,
+ j,
+ i: Integer;
+ EMeter: TEnergyMeterObj;
+ pMonitor: TMonitorObj;
+ ActiveLSObject: TLoadshapeObj;
+ pLine: TLineObj;
+ myPF,
+ myWeight,
+ mykW,
+ mykvar,
+ TotalkW: Double;
+ myLoadShapes,
+ myLoads: array of String;
+ mykvarShape,
+ myLoadShape: array of Double;
+ PFSpecified,
+ UseActual: Boolean;
+ qmult: Double;
begin
- SetLength(Result, 0);
- BusName := LowerCase(BusName);
- for i := 1 to DSS.DSSClassList.Count do
+ UseActual := FALSE;
+ if AnsiLowerCase(mode) = 'actual' then
+ UseActual := TRUE;
+
+ myFileName := Create_MeTIS_Graph();
+ TextCmd := Create_MeTIS_Zones(myFileName);
+ // Gets the link branches from the MeTIS estimation
+ with solution do
begin
- Dss_Class := DSS.DSSClassList.Get(i);
- if not (DSS_Class is TCktElementClass) then
- continue;
-
- // Checks if it is a PCE class
- if not (DSS_Class.ClassType.InheritsFrom(TPCClass) or (DSS_Class.Name = 'Capacitor') or (DSS_Class.Name = 'Reactor')) then
- continue;
-
- // If it is, checks all the elements to verify if one or more are
- // connected to the bus given
+ setlength(Link_Branches, High(Locations));
+ for i := 1 to High(Locations) do
+ Link_Branches[i - 1] := Inc_Mat_Rows[get_IncMatrix_Row(Locations[i])];
+ end;
+ // Disables DER if any
+ // Disable_All_DER();
+ // Disables Monitors and EnergyMeters if any
+ EMeter := EnergyMeters.First;
+ while EMeter <> NIL do
+ begin
+ EMeter.Enabled := FALSE;
+ EMeter := EnergyMeters.Next;
+ end;
+ pMonitor := Monitors.First;
+ while pMonitor <> NIL do
+ begin
+ pMonitor.Enabled := FALSE;
+ pMonitor := Monitors.Next;
+ end;
+
+ // Add monitors and Energy Meters at link branches
+ // Creates and EnergyMeter at the feeder head
+ pLine := Lines.First;
+ DSS.DSSExecutive.Command := 'New EnergyMeter.myEMZoneFH element=' + CheckForBlanks(pLine.FullName) + ' terminal=1';
+ for i := 0 to High(Link_Branches) do
+ begin
+ DSS.DSSExecutive.Command := 'New EnergyMeter.myEMZone' + InttoStr(i) + ' element=' + Link_Branches[i] + ' terminal=1';
+ end;
+ // Gets the max number of iterations to configure the simulation using the existing loadshapes
+ iElem := DSS.LoadshapeClass.First;
+ j := 0;
+ while iElem <> 0 do
+ begin
+ ActiveLSObject := DSS.ActiveDSSObject as TLoadShapeObj;
+ k := ActiveLSObject.NumPoints;
+ if k > j then
+ j := k;
+ iElem := DSS.LoadshapeClass.Next;
+ end;
+ // Configures simulation
+ solution.Mode := TSolveMode.SNAPSHOT;
+ solution.MaxIterations := 100;
+ Solution.MaxControlIterations := 100;
+
+ // solves the circuit
+ solution.Solve();
+
+ // Creates the folder for storign the results
+ Fileroot := DSS.OutputDirectory; {CurrentDSSDir;}
+ Fileroot := Fileroot + 'Aggregated_model';
+ CreateDir(Fileroot); // Creates the folder for storing the modified circuit
+ DelFilesFromDir(Fileroot); // Removes all the files inside the new directory (if exists)
+ // Now starts aggregating the loadshapes per zone
+ EnergyMeters.First;
+ setlength(myLoadShapes, 1);
+ for i := 1 to EnergyMeters.Count do
+ begin
+ EMeter := EnergyMeters.Active;
+ if EMeter.Enabled then
+ begin
+ // First, get the total load at nominal value for the zone
+ EMeter.GetPCEatZone;
+ TotalkW := 0;
+ setlength(myLoads, 1);
+ k := 0; // the load count
+ for j := 0 to (High(EMeter.ZonePCE) - 1) do
+ begin
+ myPCE := stripextension(EMeter.ZonePCE[j]);
+ if myPCE = 'Load' then
+ begin
+ SetElementActive(EMeter.ZonePCE[j]);
+ TotalkW := TotalkW + TLoadObj(DSS.ActiveDSSObject).kWBase;
+ myLoads[k] := TLoadObj(DSS.ActiveDSSObject).Name;
+ setlength(myLoads, length(myLoads) + 1);
+ inc(k);
+ end;
+ end;
+ setlength(myLoads, length(myLoads) - 1);
+ // initializes the length of the aggregated vector
+ setlength(myLoadShape, 0); // clears the arrays first
+ setlength(mykvarShape, 0);
+
+ if length(myLoads) > 0 then
+ begin
+ SetElementActive('Load.' + myLoads[0]);
+ setlength(myLoadShape, TLoadObj(DSS.ActiveDSSObject).YearlyShapeObj.NumPoints);
+ setlength(mykvarShape, length(myLoadShape));
+ // Next, aggregate the load profiles for the zone
+ for j := 0 to High(myLoads) do
+ begin
+ SetElementActive('Load.' + myLoads[j]);
+ myWeight := 0.0;
+ myPF := TLoadObj(DSS.ActiveDSSObject).PFNominal;
+ PFSpecified := TLoadObj(DSS.ActiveDSSObject).IsPFSpecified;
+ if TLoadObj(DSS.ActiveDSSObject).YearlyShapeObj <> NIL then
+ DSS.LoadshapeClass.SetActive(TLoadObj(DSS.ActiveDSSObject).YearlyShapeObj.Name)
+ else
+ DSS.LoadshapeClass.SetActive('');
+
+ DSS.ActiveDSSObject := DSS.LoadshapeClass.ElementList.Active;
+ for iElem := 0 to High(myLoadShape) do
+ begin
+ myLoadShape[iElem] := myLoadShape[iElem] + TLoadshapeObj(DSS.ActiveDSSObject).PMult(iElem);
+
+ if TLoadshapeObj(DSS.ActiveDSSObject).QMult(iElem, qmult) then
+ begin
+ myWeight := qmult;
+ if myWeight = 0 then
+ begin
+ if PFSpecified and (myPF <> 1.0) then // Qmult not specified but PF was
+ begin // user specified the PF for this load
+ myWeight := TLoadshapeObj(DSS.ActiveDSSObject).PMult(iElem) * SQRT((1.0 / SQR(myPF) - 1));
+ if myPF < 0.0 then // watts and vare are in opposite directions
+ myWeight := -myWeight;
+ end
+ end
+ end
+ else
+ begin
+ myWeight := 0.0;
+ if PFSpecified and (myPF <> 1.0) then // Qmult not specified but PF was
+ begin // user specified the PF for this load
+ myWeight := TLoadshapeObj(DSS.ActiveDSSObject).PMult(iElem) * SQRT((1.0 / SQR(myPF) - 1));
+ if myPF < 0.0 then // watts and vare are in opposite directions
+ myWeight := -myWeight;
+ end
+ end;
+ mykvarShape[iElem] := mykvarShape[iElem] + myWeight;
+ end;
+ end;
+ TotalkW := length(myLoads);
+ // Normalizes the waveforms
+ for iElem := 0 to High(myLoadShape) do
+ begin
+ myLoadShape[iElem] := myLoadShape[iElem] / TotalkW;
+ mykvarShape[iElem] := mykvarShape[iElem] / TotalkW;
+ end;
+
+ // Saves the profile on disk
+ myLoadShapes[High(myLoadShapes)] := DSS.OutputDirectory {CurrentDSSDir} + 'loadShape_' + EMeter.Name + '.csv';
+
+ F := TBufferedFileStream.Create(myLoadShapes[High(myLoadShapes)], fmCreate);
+ for j := 0 to High(myLoadShape) do
+ FSWriteln(F, floattostr(myLoadShape[j]) + ',' + floattostr(mykvarShape[j]));
+
+ FreeAndNil(F);
+
+ setlength(myLoadShapes, length(myLoadShapes) + 1);
+ end;
+ end;
+ EnergyMeters.Next;
+ end;
+ setlength(myLoadShapes, length(myLoadShapes) - 1);
+ // Deactivate all loadshapes in the model
+ DSS.LoadshapeClass.First;
+ for j := 1 to DSS.LoadshapeClass.ElementCount do
+ begin
+ DSS.ActiveDSSObject := DSS.LoadshapeClass.ElementList.Active;
+ TLoadshapeObj(DSS.ActiveDSSObject).Enabled := FALSE;
+ DSS.LoadshapeClass.Next;
+ end;
+
+ // Declare the new loadshapes in the model
+ for j := 0 to High(myLoadShapes) do
+ begin
+ TextCmd := '';
+ if UseActual then
+ TextCmd := ' UseActual=Yes';
+ TextCmd := 'New LoadShape.myShape_' + inttostr(j) + ' npts=' +
+ floattostr(length(myLoadShape)) + ' interval=1 mult=(file=' + myLoadShapes[j] + ' col=1, header=No)' +
+ 'Qmult=(file=' + myLoadShapes[j] + ' col=2, header=No)' + TextCmd;
+ DSS.DSSExecutive.Command := TextCmd;
+ end;
+ // Assigns the new loadshapes to all the loads in the zone
+ // also, disables the energy meters created and restores the originals
+ // DSS.DSSExecutive.Command := 'solve snap';
+ k := 0;
+ EnergyMeters.First;
+ for i := 1 to EnergyMeters.Count do
+ begin
+ EMeter := EnergyMeters.Active;
+ if EMeter.Enabled then
+ begin
+ EMeter.GetPCEatZone;
+ if length(EMeter.ZonePCE) > 1 then
+ begin
+ for j := 0 to High(EMeter.ZonePCE) do
+ begin
+ myPCE := stripextension(EMeter.ZonePCE[j]);
+ if myPCE = 'Load' then
+ begin
+ // Stores the reference values for the loads in memory to be consistent in
+ // the feeder's definition
+ SetElementActive(EMeter.ZonePCE[j]);
+ mykW := TLoadObj(DSS.ActiveDSSObject).kWref;
+ mykvar := TLoadObj(DSS.ActiveDSSObject).kVARref;
+ DSS.DSSExecutive.Command := EMeter.ZonePCE[j] + '.yearly=myShape_' + inttostr(k);
+ SetElementActive(EMeter.ZonePCE[j]);
+ // Restores the nominal values for saving the file
+ TLoadObj(DSS.ActiveDSSObject).kWBase := mykW;
+ TLoadObj(DSS.ActiveDSSObject).kvarBase := mykvar;
+ TLoadObj(DSS.ActiveDSSObject).kWref := mykW;
+ TLoadObj(DSS.ActiveDSSObject).kvarref := mykvar;
+ TLoadObj(DSS.ActiveDSSObject).kVABase := mykW / Abs(TLoadObj(DSS.ActiveDSSObject).PFNominal);
+ end;
+ end;
+ inc(k);
+ end;
+ EMeter.Enabled := FALSE;
+ end
+ else
+ EMeter.Enabled := TRUE;
+ EnergyMeters.Next;
+ end;
+
+ // saves the new model
+ DSS.DssExecutive.Command := 'save circuit Dir="' + Fileroot + '"';
+end;
+
+// This routine tears the circuit into many pieces as CPUs are
+// available in the local computer (in the best case)
+function TDSSCircuit.Tear_Circuit(): Integer;
+var
+ BusName,
+ Terminal,
+ TextCmd,
+ PDElement,
+ FileName: String;
+ NodeIdx,
+ Num_Pieces,
+ j, jj, dbg,
+ i: Integer; // Generic counter variables
+ EMeter: TEnergyMeterObj;
+ pBus: TDSSBus;
+ Volts: Polar;
+ Term_volts: array of Double; // To verify the connection of the branch
+begin
+ Num_pieces := Num_SubCkts;
+ with solution do
+ begin
+ FileName := Create_METIS_Graph();
+ TextCmd := Create_METIS_Zones(FileName);
+
+ // Verifies if there was no error executing MeTIS and the zones file was created
+ if (TextCmd <> '**Error**') and fileexists(Pchar(FileName + '.part.' + inttostr(Num_pieces))) then
+ begin
+ // ***********The directory is ready for storing the new circuit****************
+ EMeter := EnergyMeters.First;
+ while EMeter <> NIL do
+ begin
+ EMeter.Enabled := FALSE;
+ EMeter := EnergyMeters.Next;
+ end;
+ // ************ Creates the meters at the tearing locations ********************
+ Result := 1; // Resets the result variable (Return)
+ setlength(PConn_Voltages, length(Locations) * 6); // Sets the memory space for storing the voltage at the point of conn
+ setlength(Link_branches, length(Locations)); // Sets the memory space for storing the link branches names
+ setlength(PConn_Names, length(Locations)); // Sets the memory space for storing the Bus names
+ DSS.SolutionAbort := FALSE;
+ j := 0;
+ for i := 0 to High(Locations) do
+ begin
+ if Locations[i] > 0 then
+ begin
+ inc(Result);
+ // Gets the name of the PDE for placing the EnergyMeter
+ with solution do
+ begin
+ PDElement := Inc_Mat_Rows[get_IncMatrix_Row(Locations[i])];
+ Link_Branches[i] := PDElement;
+ dbg := get_IncMatrix_Col(Locations[i]); // Temporary stores the given location
+ // Checks the branch orientation across the feeder by substracting the voltages around the branch
+ // Start with Bus 1
+ setlength(Term_volts, 2);
+ for dbg := 0 to 1 do
+ begin
+ BusName := Inc_Mat_Cols[Active_Cols[dbg]];
+ SetActiveBus(DSS, BusName); // Activates the Bus
+ pBus := Buses^[ActiveBusIndex];
+ jj := 1;
+ // this code so nodes come out in order from smallest to larges
+ repeat
+ NodeIdx := pBus.FindIdx(jj); // Get the index of the Node that matches jj
+ inc(jj)
+ until NodeIdx > 0;
+ Volts := ctopolardeg(Solution.NodeV^[pBus.RefNo[NodeIdx]]); // referenced to pBus
+ Term_volts[dbg] := Volts.mag;
+ end;
+
+ // Determines the best place to connect the EnergyMeter
+ Term_volts[0] := Term_volts[0] - Term_volts[1];
+ if Term_volts[0] >= 0 then
+ jj := 0
+ else
+ jj := 1;
+ BusName := Inc_Mat_Cols[Active_Cols[jj]];
+ Terminal := 'terminal=' + inttostr(jj + 1);
+
+ PConn_Names[i] := BusName;
+ SetActiveBus(DSS, BusName); // Activates the Bus
+ pBus := Buses^[ActiveBusIndex];
+
+ for jj := 1 to 3 do
+ begin
+ // this code so nodes come out in order from smallest to larges
+ NodeIdx := pBus.FindIdx(jj); // Get the index of the Node that matches jj
+
+ Volts := ctopolardeg(Solution.NodeV^[pBus.RefNo[NodeIdx]]); // referenced to pBus
+ PConn_Voltages[j] := (Volts.mag / 1000);
+ inc(j);
+ PConn_Voltages[j] := Volts.ang;
+ inc(j);
+ end;
+
+ end;
+ // Generates the OpenDSS Command;
+ DSS.DssExecutive.Command := 'New EnergyMeter.Zone_' + inttostr(i + 1) + ' element=' + PDElement + ' ' + Terminal + ' option=R action=C';
+ end
+ else
+ begin
+ if Locations[i] = 0 then // The reference bus (Actor 1)
+ begin
+ BusName := Inc_Mat_Cols[0];
+ PConn_Names[i] := BusName;
+ SetActiveBus(DSS, BusName); // Activates the Bus
+ pBus := Buses^[ActiveBusIndex];
+ // Stores the voltages for the Reference bus first
+ for jj := 1 to 3 do
+ begin
+ // this code so nodes come out in order from smallest to larges
+ NodeIdx := pBus.FindIdx(jj); // Get the index of the Node that matches jj
+
+ Volts := ctopolardeg(Solution.NodeV^[pBus.GetRef(NodeIdx)]); // referenced to pBus
+ PConn_Voltages[j] := (Volts.mag / 1000);
+ inc(j);
+ PConn_Voltages[j] := Volts.ang;
+ inc(j);
+ end;
+ end;
+ end;
+ end;
+ end
+ else
+ begin
+ if (TextCmd = '**Error**') then
+ DoErrorMsg(DSS, 'Tear_Circuit', 'MeTIS cannot start.',
+ 'The MeTIS program (pmetis.exe/kmetis.exe) cannot be executed/found.', 7006)
+ else
+ DoErrorMsg(DSS, 'Tear_Circuit', 'The graph file is incorrect.',
+ 'MeTIS cannot process the graph file because is incorrect' +
+ '(The number of edges is incorrect).', 7007);
+ end;
+ end;
+end;
+
+{$ENDIF}
+
+// Returns the list of all PDE connected to the bus name given at BusName
+function TDSSCircuit.getPDEatBus(BusName: String; useNone: Boolean): ArrayOfString;
+var
+ Dss_Class: TDSSClass;
+ j, i: Integer;
+ myBus: array of String;
+begin
+ SetLength(myBus, 2);
+ SetLength(Result, 0);
+ BusName := AnsiLowerCase(BusName);
+ for i := 1 to DSS.DSSClassList.Count do
+ begin
+ Dss_Class := DSS.DSSClassList.Get(i);
+ if (DSS_Class is TCktElementClass) then
+ begin
+ // Checks if it is a PCE class
+ if not (DSS_Class.ClassType.InheritsFrom(TPDClass)) then
+ continue;
+
+ // If it is, checks all the elements to verify if one or more are
+ // connected to the bus given
+ DSS_Class.First;
+ for j := 1 to DSS_Class.ElementCount do
+ begin
+ myBus[0] := AnsiLowerCase(StripExtension(ActiveCktElement.GetBus(1)));
+ myBus[1] := AnsiLowerCase(StripExtension(ActiveCktElement.GetBus(2)));
+ if ((myBus[0] = BusName) or (myBus[1] = BusName)) and (myBus[0] <> myBus[1]) then
+ begin
+ SetLength(Result, length(Result) + 1);
+ Result[High(Result)] := ActiveCktElement.FullName;
+ end;
+ DSS_Class.Next;
+ end;
+ end;
+ end;
+ if (length(Result) = 0) and useNone then
+ begin
+ SetLength(Result, 1);
+ Result[0] := 'None';
+ end;
+end;
+
+// Returns the list of all PCE connected to the bus nam given at BusName
+function TDSSCircuit.getPCEatBus(BusName: String; useNone: Boolean): ArrayOfString;
+var
+ Dss_Class: TDSSClass;
+ j, i: Integer;
+ myBus: String;
+begin
+ SetLength(Result, 0);
+ BusName := AnsiLowerCase(BusName);
+ for i := 1 to DSS.DSSClassList.Count do
+ begin
+ Dss_Class := DSS.DSSClassList.Get(i);
+ if not (DSS_Class is TCktElementClass) then
+ continue;
+
+ // Checks if it is a PCE class
+ if not (DSS_Class.ClassType.InheritsFrom(TPCClass) or (DSS_Class.Name = 'Capacitor') or (DSS_Class.Name = 'Reactor')) then
+ continue;
+
+ // If it is, checks all the elements to verify if one or more are
+ // connected to the bus given
DSS_Class.First;
for j := 1 to DSS_Class.ElementCount do
begin
- myBus := LowerCase(StripExtension(ActiveCktElement.GetBus(1)));
+ myBus := AnsiLowerCase(StripExtension(ActiveCktElement.GetBus(1)));
if myBus = BusName then
begin
SetLength(Result, length(Result) + 1);
- Result[High(Result)] := DSS_Class.Name + '.' + ActiveCktElement.Name;
+ Result[High(Result)] := ActiveCktElement.FullName;
end;
DSS_Class.Next;
end;
@@ -1564,554 +1834,159 @@ function TDSSCircuit.getPCEatBus(BusName: String; useNone: Boolean): ArrayOfStri
end;
end;
-{*******************************************************************************
-* Gets all PCE at given bus and returns the list as string *
-********************************************************************************}
-function TDSSCircuit.ReportPCEatBus(BusName: String): String;
-var
- i : integer;
- myPCEList : Array of String;
-
-Begin
- myPCEList := getPCEatBus(BusName);
- Result := '';
- for i := 0 to High(myPCEList) do
- if myPCEList[i] <> '' then Result := Result + myPCEList[i] + ',';
-End;
-
-{*******************************************************************************
-* Gets all PDE at given bus and returns the list as string *
-********************************************************************************}
-function TDSSCircuit.ReportPDEatBus(BusName: String): String;
+// Gets all PCE at given bus and returns the list as string
+function TDSSCircuit.ReportPCEatBus(BusName: String): String;
var
- i : integer;
- myPDEList : Array of String;
-
-Begin
- myPDEList := getPDEatBus(BusName);
- Result := '';
- for i := 0 to High(myPDEList) do
- if myPDEList[i] <> '' then Result := Result + myPDEList[i] + ',';
-End;
-
-{*******************************************************************************
-* Aggregates profiles using the number of zones defined by the user *
-********************************************************************************}
-procedure TDSSCircuit.AggregateProfiles(mode : string);
-var
- F: TFileStream = nil;
- FileRoot,
- myPCE,
- TextCmd,
- myFilename : string;
- iElem,
- k,
- j,
- i : Integer;
- EMeter : TEnergyMeterObj;
- pMonitor : TMonitorObj;
- ActiveLSObject: TLoadshapeObj;
- pLine : TLineObj;
- myPF,
- myWeight,
- mykW,
- mykvar,
- TotalkW : Double;
- myLoadShapes,
- myLoads : array of string;
- mykvarShape,
- myLoadShape : array of double;
- PFSpecified,
- UseActual : Boolean;
- qmult: Double;
-
-Begin
-
- UseActual := False;
- if LowerCase(mode) = 'actual' then
- UseActual := True;
-
- myFileName := Create_MeTIS_Graph();
- TextCmd := Create_MeTIS_Zones(myFileName);
- // Gets the link branches from the MeTIS estimation
- with solution do
- Begin
- setlength(Link_Branches,High(Locations));
- for i := 1 to High(Locations) do
- Link_Branches[i - 1] := Inc_Mat_Rows[get_IncMatrix_Row(Locations[i])];
- end;
- // Disables DER if any
-// Disable_All_DER();
- // Disables Monitors and EnergyMeters if any
- EMeter := EnergyMeters.First;
- while EMeter <> Nil do
- begin
- EMeter.Enabled := False;
- EMeter := EnergyMeters.Next;
- end;
- pMonitor := Monitors.First;
- while pMonitor <> Nil do
- begin
- pMonitor.Enabled := False;
- pMonitor := Monitors.Next;
- end;
- {----------------------------------------------------------------------------}
- // Add monitors and Energy Meters at link branches
- // Creates and EnergyMeter at the feeder head
- pLine := Lines.First;
- DSS.DSSExecutive.Command := 'New EnergyMeter.myEMZoneFH element=Line.' + pLine.Name + ' terminal=1' ;
- for i := 0 to High(Link_Branches) do
- Begin
- DSS.DSSExecutive.Command := 'New EnergyMeter.myEMZone' + InttoStr(i) + ' element=' + Link_Branches[i] + ' terminal=1' ;
- End;
- // Gets the max number of iterations to configure the simulation using the existing loadshapes
- iElem := DSS.LoadshapeClass.First;
- j := 0;
- while iElem <> 0 do
- Begin
- ActiveLSObject := DSS.ActiveDSSObject as TLoadShapeObj;
- k := ActiveLSObject.NumPoints;
- if k > j then j := k;
- iElem := DSS.LoadshapeClass.Next;
- End;
- // Configures simulation
- solution.Mode := TSolveMode.SNAPSHOT;
- solution.MaxIterations := 100;
- Solution.MaxControlIterations := 100;
-
- // solves the circuit
- solution.Solve() ;
-
- // Creates the folder for storign the results
- Fileroot := DSS.OutputDirectory; {CurrentDSSDir;}
- Fileroot := Fileroot + 'Aggregated_model';
- CreateDir(Fileroot); // Creates the folder for storing the modified circuit
- DelFilesFromDir(Fileroot,'*',True); // Removes all the files inside the new directory (if exists)
- // Now starts aggregating the loadshapes per zone
- EnergyMeters.First;
- setlength(myLoadShapes,1);
- for i := 1 to EnergyMeters.Count do
- Begin
-
- EMeter := EnergyMeters.Active;
- if EMeter.Enabled then
- Begin
- //First, get the total load at nominal value for the zone
- EMeter.GetPCEatZone;
- TotalkW := 0;
- setlength(myLoads,1);
- k := 0; // the load count
- for j := 0 to (High(EMeter.ZonePCE) - 1) do
- Begin
- myPCE := stripextension(EMeter.ZonePCE[j]);
- if myPCE = 'Load' then
- Begin
- SetElementActive(EMeter.ZonePCE[j]);
- TotalkW := TotalkW + TLoadObj(DSS.ActiveDSSObject).kWBase;
- myLoads[k] := TLoadObj(DSS.ActiveDSSObject).Name;
- setlength(myLoads,length(myLoads) + 1);
- inc(k);
- End;
- End;
- setlength(myLoads,length(myLoads) - 1);
- // initializes the length of the aggregated vector
- setlength(myLoadShape,0); // clears the arrays first
- setlength(mykvarShape,0);
-
- if length(myLoads) > 0 then
- Begin
- SetElementActive('Load.' + myLoads[0]);
- setlength(myLoadShape,TLoadObj(DSS.ActiveDSSObject).YearlyShapeObj.NumPoints);
- setlength(mykvarShape,length(myLoadShape));
- // Next, aggregate the load profiles for the zone
- for j := 0 to High(myLoads) do
- Begin
- SetElementActive('Load.' + myLoads[j]);
- myWeight := 0.0;
- myPF := TLoadObj(DSS.ActiveDSSObject).PFNominal;
- PFSpecified := TLoadObj(DSS.ActiveDSSObject).IsPFSpecified;
- DSS.LoadshapeClass.SetActive(TLoadObj(DSS.ActiveDSSObject).YearlyShape);
- DSS.ActiveDSSObject := DSS.LoadshapeClass.ElementList.Active;
- for iElem := 0 to High(myLoadShape) do
- Begin
- myLoadShape[iElem] := myLoadShape[iElem] + TLoadshapeObj(DSS.ActiveDSSObject).PMult(iElem);
-
- if TLoadshapeObj(DSS.ActiveDSSObject).QMult(iElem, qmult) then
- Begin
- myWeight := qmult;
- if myWeight = 0 then
- Begin
- If PFSpecified and (myPF <> 1.0) Then // Qmult not specified but PF was
- Begin // user specified the PF for this load
- myWeight := TLoadshapeObj(DSS.ActiveDSSObject).PMult(iElem) * SQRT((1.0/SQR(myPF) - 1));
- If myPF < 0.0 Then // watts and vare are in opposite directions
- myWeight := -myWeight;
- End
- End
- End
- else
- Begin
- myWeight := 0.0;
- If PFSpecified and (myPF <> 1.0) Then // Qmult not specified but PF was
- Begin // user specified the PF for this load
- myWeight := TLoadshapeObj(DSS.ActiveDSSObject).PMult(iElem) * SQRT((1.0/SQR(myPF) - 1));
- If myPF < 0.0 Then // watts and vare are in opposite directions
- myWeight := -myWeight;
- End
- End;
- mykvarShape[iElem] := mykvarShape[iElem] + myWeight;
- End;
- End;
- TotalkW := length(myLoads);
- // Normalizes the waveforms
- for iElem := 0 to High(myLoadShape) do
- Begin
- myLoadShape[iElem] := myLoadShape[iElem] / TotalkW;
- mykvarShape[iElem] := mykvarShape[iElem] / TotalkW;
- End;
-
- // Saves the profile on disk
- myLoadShapes[High(myLoadShapes)] := DSS.OutputDirectory {CurrentDSSDir} + 'loadShape_' + EMeter.Name + '.csv';
-
- F := TFileStream.Create(myLoadShapes[High(myLoadShapes)], fmCreate);
- for j := 0 to High(myLoadShape) do
- FSWriteln(F,floattostr(myLoadShape[j]) + ',' + floattostr(mykvarShape[j]));
-
- FreeAndNil(F);
-
- setlength(myLoadShapes,length(myLoadShapes) + 1);
- End;
- End;
- EnergyMeters.Next;
- End;
- setlength(myLoadShapes,length(myLoadShapes) - 1);
- // Deactivate all loadshapes in the model
- DSS.LoadshapeClass.First;
- for j := 1 to DSS.LoadshapeClass.ElementCount do
- Begin
- DSS.ActiveDSSObject := DSS.LoadshapeClass.ElementList.Active;
- TLoadshapeObj(DSS.ActiveDSSObject).Enabled := False;
- DSS.LoadshapeClass.Next;
- End;
-
- // Declare the new loadshapes in the model
- for j := 0 to High(myLoadShapes) do
- Begin
- TextCmd := '';
- if UseActual then TextCmd := ' UseActual=Yes';
- TextCmd := 'New LoadShape.myShape_' + inttostr(j) + ' npts=' +
- floattostr(length(myLoadShape)) + ' interval=1 mult=(file=' + myLoadShapes[j] + ' col=1, header=No)' +
- 'Qmult=(file=' + myLoadShapes[j] + ' col=2, header=No)' + TextCmd;
- DSS.DSSExecutive.Command := TextCmd;
- End;
- // Assigns the new loadshapes to all the loads in the zone
- // also, disables the energy meters created and restores the originals
-// DSS.DSSExecutive.Command := 'solve snap';
- k := 0;
- EnergyMeters.First;
- for i := 1 to EnergyMeters.Count do
- Begin
- EMeter := EnergyMeters.Active;
- if EMeter.Enabled then
- Begin
- EMeter.GetPCEatZone;
- if length(EMeter.ZonePCE) > 1 then
- Begin
- for j := 0 to High(EMeter.ZonePCE) do
- Begin
- myPCE := stripextension(EMeter.ZonePCE[j]);
- if myPCE = 'Load' then
- Begin
- // Stores the reference values for the loads in memory to be consistent in
- // the feeder's definition
- SetElementActive(EMeter.ZonePCE[j]);
- mykW := TLoadObj(DSS.ActiveDSSObject).kWref;
- mykvar := TLoadObj(DSS.ActiveDSSObject).kVARref;
- DSS.DSSExecutive.Command := EMeter.ZonePCE[j] + '.yearly=myShape_' + inttostr(k);
- SetElementActive(EMeter.ZonePCE[j]);
- // Rstores the nominal values for saving the file
- TLoadObj(DSS.ActiveDSSObject).kWBase := mykW;
- TLoadObj(DSS.ActiveDSSObject).kvarBase:= mykvar;
- TLoadObj(DSS.ActiveDSSObject).kWref := mykW;
- TLoadObj(DSS.ActiveDSSObject).kvarref := mykvar;
- TLoadObj(DSS.ActiveDSSObject).kVABase := mykW / Abs(TLoadObj(DSS.ActiveDSSObject).PFNominal);
- End;
- End;
- inc(k);
- End;
- EMeter.Enabled := False;
- End
- else
- EMeter.Enabled := True;
- EnergyMeters.Next;
- end;
+ i: Integer;
+ myPCEList: array of String;
+
+begin
+ myPCEList := getPCEatBus(BusName);
+ Result := '';
+ for i := 0 to High(myPCEList) do
+ if myPCEList[i] <> '' then
+ Result := Result + myPCEList[i] + ',';
+end;
- // saves the new model
- DSS.DssExecutive.Command := 'save circuit Dir="' + Fileroot + '"';
+// Gets all PDE at given bus and returns the list as string
+function TDSSCircuit.ReportPDEatBus(BusName: String): String;
+var
+ i: Integer;
+ myPDEList: array of String;
-End;
+begin
+ myPDEList := getPDEatBus(BusName);
+ Result := '';
+ for i := 0 to High(myPDEList) do
+ if myPDEList[i] <> '' then
+ Result := Result + myPDEList[i] + ',';
+end;
-{*******************************************************************************
-* This routine tears the circuit into many pieces as CPUs are *
-* available in the local computer (in the best case) *
-********************************************************************************}
-function TDSSCircuit.Tear_Circuit(): Integer;
+procedure TDSSCircuit.ProcessBusDefs;
var
- BusName,
- Terminal,
- TextCmd,
- PDElement,
- FileName : String;
- NodeIdx,
- Num_Pieces,
- j,jj,dbg,
- i : Integer; // Generic counter variables
- EMeter : TEnergyMeterObj;
- pBus : TDSSBus;
- Volts : Polar;
- Term_volts : Array of Double; // To verify the connection of the branch
-Begin
- Num_pieces := Num_SubCkts;
- with solution do
- Begin
-
- FileName := Create_METIS_Graph();
- TextCmd := Create_METIS_Zones(FileName);
- {******************************************************************************************}
- // Verifies if there was no error executing MeTIS and the zones file was created
- if (TextCmd <> '**Error**') and fileexists(pchar(FileName + '.part.' + inttostr(Num_pieces))) then
- Begin
- //***********The directory is ready for storing the new circuit****************
- EMeter := EnergyMeters.First;
- while EMeter <> Nil do
- begin
- EMeter.Enabled := False;
- EMeter := EnergyMeters.Next;
- end;
- //************ Creates the meters at the tearing locations ********************
- Result := 1; // Resets the result variable (Return)
- setlength(PConn_Voltages,length(Locations)*6); // Sets the memory space for storing the voltage at the point of conn
- setlength(Link_branches,length(Locations)); // Sets the memory space for storing the link branches names
- setlength(PConn_Names,length(Locations)); // Sets the memory space for storing the Bus names
- DSS.SolutionAbort := FALSE;
- j := 0;
- for i := 0 to High(Locations) do
- begin
- if Locations[i] > 0 then
- Begin
- inc(Result);
- // Gets the name of the PDE for placing the EnergyMeter
- with solution do
- Begin
- PDElement := Inc_Mat_Rows[get_IncMatrix_Row(Locations[i])];
- Link_Branches[i] := PDElement;
- dbg := get_IncMatrix_Col(Locations[i]); // Temporary stores the given location
- // Checks the branch orientation across the feeder by substracting the voltages around the branch
- // Start with Bus 1
- setlength(Term_volts,2);
- for dbg := 0 to 1 do
- Begin
- BusName := Inc_Mat_Cols[Active_Cols[dbg]];
- SetActiveBus(DSS, BusName); // Activates the Bus
- pBus := Buses^[ActiveBusIndex];
- jj := 1;
- // this code so nodes come out in order from smallest to larges
- Repeat
- NodeIdx := pBus.FindIdx(jj); // Get the index of the Node that matches jj
- inc(jj)
- Until NodeIdx>0;
- Volts := ctopolardeg(Solution.NodeV^[pBus.GetRef(NodeIdx)]); // referenced to pBus
- Term_volts[dbg] := Volts.mag;
- End;
-
- // Determines the best place to connect the EnergyMeter
- Term_volts[0] := Term_volts[0] - Term_volts[1];
- if Term_volts[0] >= 0 then jj := 0
- else jj := 1;
- BusName := Inc_Mat_Cols[Active_Cols[jj]];
- Terminal := 'terminal=' + inttostr(jj + 1);
-
- PConn_Names[i] := BusName;
- SetActiveBus(DSS, BusName); // Activates the Bus
- pBus := Buses^[ActiveBusIndex];
-
- for jj := 1 to 3 do
- Begin
- // this code so nodes come out in order from smallest to larges
- NodeIdx := pBus.FindIdx(jj); // Get the index of the Node that matches jj
-
- Volts := ctopolardeg(Solution.NodeV^[pBus.GetRef(NodeIdx)]); // referenced to pBus
- PConn_Voltages[j] := (Volts.mag/1000);
- inc(j);
- PConn_Voltages[j] := Volts.ang;
- inc(j);
- End;
-
- End;
- // Generates the OpenDSS Command;
- DSS.DssExecutive.Command := 'New EnergyMeter.Zone_' + inttostr(i + 1) + ' element=' + PDElement + ' ' + Terminal + ' option=R action=C';
- End
- else
- Begin
- if Locations[i] = 0 then // The reference bus (Actor 1)
- Begin
- BusName := Inc_Mat_Cols[0];
- PConn_Names[i] := BusName;
- SetActiveBus(DSS, BusName); // Activates the Bus
- pBus := Buses^[ActiveBusIndex];
- // Stores the voltages for the Reference bus first
- for jj := 1 to 3 do
- Begin
- // this code so nodes come out in order from smallest to larges
- NodeIdx := pBus.FindIdx(jj); // Get the index of the Node that matches jj
-
- Volts := ctopolardeg(Solution.NodeV^[pBus.GetRef(NodeIdx)]); // referenced to pBus
- PConn_Voltages[j] := (Volts.mag/1000);
- inc(j);
- PConn_Voltages[j] := Volts.ang;
- inc(j);
- End;
- End;
- End;
- end;
- End
- else
- Begin
- if (TextCmd = '**Error**') then
- DoErrorMsg('Tear_Circuit','MeTIS cannot start.',
- 'The MeTIS program (pmetis.exe/kmetis.exe) cannot be executed/found.', 7006)
- else
- DoErrorMsg('Tear_Circuit','The graph file is incorrect.',
- 'MeTIS cannot process the graph file because is incorrect' +
- '(The number of edges is incorrect).', 7007);
- End;
- End;
-End;
-//----------------------------------------------------------------------------
-
-//----------------------------------------------------------------------------
-Procedure TDSSCircuit.ProcessBusDefs;
-VAR
- BusName:String;
- NNodes, NP, Ncond, i, j, iTerm, RetVal:Integer;
- NodesOK:Boolean;
-
-BEGIN
- WITH ActiveCktElement DO
- BEGIN
- np := NPhases;
- Ncond := NConds;
-
- DSS.Parser.Token := FirstBus; // use parser functions to decode
- FOR iTerm := 1 to Nterms DO
- BEGIN
- NodesOK := TRUE;
+ CurrentBus, BusName: String;
+ NNodes, NP, Ncond, i, j, iTerm, RetVal: Integer;
+ NodesOK: Boolean;
+begin
+ with ActiveCktElement do
+ begin
+ np := NPhases;
+ Ncond := NConds;
+
+ CurrentBus := FirstBus; // use parser functions to decode
+ for iTerm := 1 to Nterms do
+ begin
+ NodesOK := TRUE;
// Assume normal phase rotation for default
- FOR i := 1 to np DO NodeBuffer^[i] := i; // set up buffer with defaults
-
- // Default all other conductors to a ground connection
- // If user wants them ungrounded, must be specified explicitly!
- For i := np + 1 to NCond DO NodeBuffer^[i] := 0;
-
- // Parser will override bus connection if any specified
- BusName := DSS.Parser.ParseAsBusName(NNodes, NodeBuffer);
-
- // Check for error in node specification
- For j := 1 to NNodes Do
- Begin
- If NodeBuffer^[j] < 0 Then
- Begin
- retval := DSSMessageDlg('Error in Node specification for Element: "'
- +ParentClass.Name+'.'+Name+'"'+CRLF+
- 'Bus Spec: "'+DSS.Parser.Token+'"',FALSE);
- NodesOK := FALSE;
- If retval=-1 Then Begin
- AbortBusProcess := TRUE;
- AppendGlobalresult(DSS, 'Aborted bus process.');
- Exit
- End;
- Break;
- End;
- End;
-
-
- // Node -Terminal Connnections
- // Caution: Magic -- AddBus replaces values in nodeBuffer to correspond
- // with global node reference number.
- If NodesOK Then
- Begin
- ActiveTerminalIdx := iTerm;
- ActiveTerminal.BusRef := AddBus(BusName, Ncond);
- SetNodeRef(iTerm, NodeBuffer); // for active circuit
- End;
- DSS.Parser.Token := NextBus;
- END;
- END;
-END;
-
-
-//----------------------------------------------------------------------------
-Procedure TDSSCircuit.AddABus;
-BEGIN
+ for i := 1 to np do
+ NodeBuffer^[i] := i; // set up buffer with defaults
+
+ // Default all other conductors to a ground connection
+ // If user wants them ungrounded, must be specified explicitly!
+ for i := np + 1 to NCond do
+ NodeBuffer^[i] := 0;
+
+ // Parser will override bus connection if any specified
+ BusName := DSS.Parser.ParseAsBusName(CurrentBus, NNodes, NodeBuffer);
+
+ // Check for error in node specification
+ for j := 1 to NNodes do
+ begin
+ if NodeBuffer^[j] < 0 then
+ begin
+ retval := DSSMessageDlg('Error in Node specification for Element: "' + ParentClass.Name + '.' + Name + '"' + CRLF +
+ 'Bus Spec: "' + DSS.Parser.Token + '"', FALSE);
+ NodesOK := FALSE;
+ if retval = -1 then
+ begin
+ AbortBusProcess := TRUE;
+ AppendGlobalresult(DSS, 'Aborted bus process.');
+ Exit
+ end;
+ Break;
+ end;
+ end;
+
+ // Node -Terminal Connnections
+ // Caution: Magic -- AddBus replaces values in nodeBuffer to correspond
+ // with global node reference number.
+ if NodesOK then
+ begin
+ ActiveTerminalIdx := iTerm;
+ ActiveTerminal.BusRef := AddBus(BusName, Ncond);
+ SetNodeRef(iTerm, NodeBuffer); // for active circuit
+ end;
+ CurrentBus := NextBus;
+ end;
+ end;
+end;
+
+
+procedure TDSSCircuit.AddABus;
+begin
//TODO: check if other growth rates are better from large systems
- If NumBuses > MaxBuses THEN BEGIN
+ if NumBuses > MaxBuses then
+ begin
Inc(MaxBuses, IncBuses);
ReallocMem(Buses, SizeOf(Buses^[1]) * MaxBuses);
- END;
-END;
+ end;
+end;
-//----------------------------------------------------------------------------
-Procedure TDSSCircuit.AddANodeBus;
-BEGIN
- If NumNodes > MaxNodes THEN BEGIN
+procedure TDSSCircuit.AddANodeBus;
+begin
+ if NumNodes > MaxNodes then
+ begin
Inc(MaxNodes, IncNodes);
ReallocMem(MapNodeToBus, SizeOf(MapNodeToBus^[1]) * MaxNodes);
- END;
-END;
-
-//----------------------------------------------------------------------------
-Function TDSSCircuit.AddBus(const BusName:String; NNodes:Integer):Integer;
+ end;
+end;
-VAR
- NodeRef, i :Integer;
-BEGIN
+function TDSSCircuit.AddBus(const BusName: String; NNodes: Integer): Integer;
-// Trap error in bus name
- IF Length(BusName) = 0 THEN BEGIN // Error in busname
- DoErrorMsg(DSS, 'TDSSCircuit.AddBus', 'BusName for Object "' + ActiveCktElement.Name + '" is null.',
- 'Error in definition of object.', 424);
- For i := 1 to ActiveCktElement.NConds DO NodeBuffer^[i] := 0;
- Result := 0;
- Exit;
- END;
+var
+ NodeRef, i: Integer;
+begin
+ // Trap error in bus name
+ if Length(BusName) = 0 then
+ begin // Error in busname
+ DoErrorMsg(DSS, 'TDSSCircuit.AddBus', 'BusName for Object "' + ActiveCktElement.Name + '" is null.',
+ 'Error in definition of object.', 424);
+ for i := 1 to ActiveCktElement.NConds do
+ NodeBuffer^[i] := 0;
+ Result := 0;
+ Exit;
+ end;
Result := BusList.Find(BusName);
- If Result=0 THEN BEGIN
- Result := BusList.Add(BusName); // Result is index of bus
- Inc(NumBuses);
- AddABus; // Allocates more memory if necessary
- Buses^[NumBuses] := TDSSBus.Create(DSS);
- END;
-
- {Define nodes belonging to the bus}
- {Replace Nodebuffer values with global reference number}
- WITH Buses^[Result] DO BEGIN
- FOR i := 1 to NNodes DO BEGIN
- NodeRef := Add(self, NodeBuffer^[i]);
- If NodeRef=NumNodes THEN BEGIN // This was a new node so Add a NodeToBus element ????
- AddANodeBus; // Allocates more memory if necessary
- MapNodeToBus^[NumNodes].BusRef := Result;
- MapNodeToBus^[NumNodes].NodeNum := NodeBuffer^[i]
- END;
- NodeBuffer^[i] := NodeRef; // Swap out in preparation to setnoderef call
- END;
- END;
-END;
-
-//----------------------------------------------------------------------------
+ if Result = 0 then
+ begin
+ Result := BusList.Add(BusName); // Result is index of bus
+ Inc(NumBuses);
+ AddABus; // Allocates more memory if necessary
+ Buses^[NumBuses] := TDSSBus.Create(DSS);
+ end;
+
+ // Define nodes belonging to the bus
+ // Replace Nodebuffer values with global reference number
+ with Buses^[Result] do
+ begin
+ for i := 1 to NNodes do
+ begin
+ NodeRef := Add(self, NodeBuffer^[i]);
+ if NodeRef = NumNodes then
+ begin // This was a new node so Add a NodeToBus element ????
+ AddANodeBus; // Allocates more memory if necessary
+ MapNodeToBus^[NumNodes].BusRef := Result;
+ MapNodeToBus^[NumNodes].NodeNum := NodeBuffer^[i]
+ end;
+ NodeBuffer^[i] := NodeRef; // Swap out in preparation to setnoderef call
+ end;
+ end;
+end;
+
function TDSSCircuit.SetElementActive(const FullObjectName: String): Integer;
// Fast way to set a cktelement active
-VAR
+var
DevIndex: Integer;
DevClassIndex: Integer;
DevType,
@@ -2122,7 +1997,7 @@ function TDSSCircuit.SetElementActive(const FullObjectName: String): Integer;
Result := 0;
ParseObjectClassandName(DSS, FullObjectName, DevType, DevName);
DevClassIndex := DSS.ClassNames.Find(DevType);
- if DevClassIndex = 0 then
+ if DevClassIndex = 0 then
DevClassIndex := DSS.LastClassReferenced;
DevCls := DSS.DSSClassList.At(DevClassIndex);
@@ -2131,10 +2006,10 @@ function TDSSCircuit.SetElementActive(const FullObjectName: String): Integer;
DSS.CmdResult := Result;
Exit;
end;
-
+
if not DuplicatesAllowed then
begin
- element := TDSSCktElement(DevCls.Find(DevName, False));
+ element := TDSSCktElement(DevCls.Find(DevName, FALSE));
if element <> NIL then
begin
DSS.ActiveDSSClass := DSS.DSSClassList.Get(DevClassIndex);
@@ -2146,732 +2021,766 @@ function TDSSCircuit.SetElementActive(const FullObjectName: String): Integer;
else
begin
Devindex := DeviceList.Find(DevName);
- while DevIndex > 0 do
+ while DevIndex > 0 do
begin
if TDSSCktElement(CktElements.At(Devindex)).ParentClass = DevCls then // we got a match
- BEGIN
+ begin
DSS.ActiveDSSClass := DSS.DSSClassList.Get(DevClassIndex);
DSS.LastClassReferenced := DevClassIndex;
Result := Devindex;
ActiveCktElement := CktElements.Get(Result);
break;
- END;
+ end;
Devindex := Devicelist.FindNext; // Could be duplicates
end;
end;
DSS.CmdResult := Result;
-END;
+end;
-//----------------------------------------------------------------------------
-Procedure TDSSCircuit.Set_ActiveCktElement(Value:TDSSCktElement);
-BEGIN
+procedure TDSSCircuit.Set_ActiveCktElement(Value: TDSSCktElement);
+begin
FActiveCktElement := Value;
DSS.ActiveDSSObject := Value;
-END;
-
-//----------------------------------------------------------------------------
-Procedure TDSSCircuit.AddCktElement();
-
-
-BEGIN
-
- // Update lists that keep track of individual circuit elements
- Inc(NumDevices);
-
- // Resize DeviceList if no. of devices greatly exceeds allocation
- If Cardinal(NumDevices)> 2*DeviceList.InitialAllocation Then ReAllocDeviceList;
- DeviceList.Add(ActiveCktElement.Name);
- CktElements.Add(ActiveCktElement);
-
- {Build Lists of PC and PD elements}
- CASE (ActiveCktElement.DSSObjType and BaseClassMask) OF
- PD_ELEMENT: PDElements.Add(ActiveCktElement);
- PC_ELEMENT: PCElements.Add(ActiveCktElement);
- CTRL_ELEMENT: DSSControls.Add(ActiveCktElement);
- METER_ELEMENT :MeterElements.Add(ActiveCktElement);
- Else
- {Nothing}
- End;
-
- {Build lists of Special elements and generic types}
- CASE (ActiveCktElement.DSSObjType and CLASSMASK) OF
- MON_ELEMENT :Monitors.Add(ActiveCktElement);
- ENERGY_METER :EnergyMeters.Add(ActiveCktElement);
- SENSOR_ELEMENT :Sensors.Add(ActiveCktElement);
- GEN_ELEMENT :Generators.Add(ActiveCktElement);
- SOURCE :Sources.Add(ActiveCktElement);
- CAP_CONTROL :CapControls.Add(ActiveCktElement);
- SWT_CONTROL :SwtControls.Add(ActiveCktElement);
- REG_CONTROL :RegControls.Add(ActiveCktElement);
- LOAD_ELEMENT :Loads.Add(ActiveCktElement);
- CAP_ELEMENT :ShuntCapacitors.Add(ActiveCktElement);
- REACTOR_ELEMENT :Reactors.Add(ActiveCktElement);
- RELAY_CONTROL :Relays.Add(ActiveCktElement);
- FUSE_CONTROL :Fuses.Add(ActiveCktElement);
- RECLOSER_CONTROL :Reclosers.Add(ActiveCktElement);
-
- { Keep Lines, Transformer, and Lines and Faults in PDElements and separate lists
- so we can find them quickly.}
- XFMR_ELEMENT :Transformers.Add(ActiveCktElement);
- LINE_ELEMENT :Lines.Add(ActiveCktElement);
- FAULTOBJECT :Faults.Add(ActiveCktElement);
-
- STORAGE_ELEMENT:StorageElements.Add(ActiveCktElement);
- PVSYSTEM_ELEMENT:PVSystems.Add(ActiveCktElement);
- END;
-
- ActiveCktElement.Handle := CktElements.Count;
-
-END;
-
-//----------------------------------------------------------------------------
-Procedure TDSSCircuit.DoResetMeterZones;
-
-BEGIN
-
- { Do this only if meterzones unlocked . Normally, Zones will remain unlocked
- so that all changes to the circuit will result in rebuilding the lists}
- If Not MeterZonesComputed or Not ZonesLocked Then
- Begin
- If LogEvents Then LogThisEvent(DSS, 'Resetting Meter Zones');
- DSS.EnergyMeterClass.ResetMeterZonesAll;
- MeterZonesComputed := True;
- If LogEvents Then LogThisEvent(DSS, 'Done Resetting Meter Zones');
- End;
-
- FreeTopology;
-
-END;
-
-//----------------------------------------------------------------------------
-Procedure TDSSCircuit.SaveBusInfo;
-Var
- i :Integer;
-
-Begin
-
-{Save existing bus definitions and names for info that needs to be restored}
- SavedBuses := Allocmem(Sizeof(SavedBuses^[1]) * NumBuses);
- SavedBusNames := Allocmem(Sizeof(SavedBusNames^[1]) * NumBuses);
-
- For i := 1 to NumBuses Do Begin
- SavedBuses^[i] := Buses^[i];
- SavedBusNames^[i] := BusList.NameOfIndex(i);
- End;
- SavedNumBuses := NumBuses;
-
-End;
-
-//----------------------------------------------------------------------------
-Procedure TDSSCircuit.RestoreBusInfo;
-
-Var
- i,j,idx, jdx:Integer;
- pBus:TDSSBus;
-
-Begin
-
-// Restore kV bases, other values to buses still in the list
- For i := 1 to SavedNumBuses Do
- Begin
- idx := BusList.Find(SavedBusNames^[i]);
- If idx <> 0 Then
- With Buses^[idx] Do Begin
- pBus := SavedBuses^[i];
- kvBase := pBus.kVBase;
- x := pBus.x;
- Y := pBus.y;
- CoordDefined := pBus.CoordDefined;
- Keep := pBus.Keep;
- {Restore Voltages in new bus def that existed in old bus def}
- If assigned(pBus.VBus) Then Begin
- For j := 1 to pBus.NumNodesThisBus Do Begin
- jdx := FindIdx(pBus.GetNum(j)); // Find index in new bus for j-th node in old bus
- If jdx > 0 Then Vbus^[jdx] := pBus.VBus^[j];
- End;
- End;
- End;
- SavedBusNames^[i] := ''; // De-allocate string
- End;
-
- If Assigned(SavedBuses) Then For i := 1 to SavedNumBuses Do SavedBuses^[i].Free; // gets rid of old bus voltages, too
-
- ReallocMem(SavedBuses, 0);
- ReallocMem(SavedBusNames, 0);
-
-End;
-
-//----------------------------------------------------------------------------
-Procedure TDSSCircuit.ReProcessBusDefs;
-
-// Redo all Buslists, nodelists
-
-VAR
- CktElementSave :TDSSCktElement;
- i:integer;
+end;
-BEGIN
- If LogEvents Then LogThisEvent(DSS, 'Reprocessing Bus Definitions');
+procedure TDSSCircuit.AddCktElement(Obj: TDSSCktElement);
+begin
+ // Update lists that keep track of individual circuit elements
+ Inc(NumDevices);
+
+ // Resize DeviceList if no. of devices greatly exceeds allocation
+ if Cardinal(NumDevices) > 2 * DeviceList.InitialAllocation then
+ ReAllocDeviceList;
+ DeviceList.Add(Obj.Name);
+ CktElements.Add(Obj);
+
+ // Build Lists of PC and PD elements
+ case (Obj.DSSObjType and BaseClassMask) of
+ PD_ELEMENT:
+ PDElements.Add(Obj);
+ PC_ELEMENT:
+ PCElements.Add(Obj);
+ CTRL_ELEMENT:
+ DSSControls.Add(Obj);
+ METER_ELEMENT:
+ MeterElements.Add(Obj);
+ else
+ // Nothing
+ end;
- AbortBusProcess := FALSE;
- SaveBusInfo; // So we don't have to keep re-doing this
- // Keeps present definitions of bus objects until new ones created
+ // Build lists of Special elements and generic types
+ case (Obj.DSSObjType and CLASSMASK) of
+ MON_ELEMENT:
+ Monitors.Add(Obj);
+ ENERGY_METER:
+ EnergyMeters.Add(Obj);
+ SENSOR_ELEMENT:
+ Sensors.Add(Obj);
+ GEN_ELEMENT:
+ Generators.Add(Obj);
+ SOURCE:
+ Sources.Add(Obj);
+ CAP_CONTROL:
+ CapControls.Add(Obj);
+ SWT_CONTROL:
+ SwtControls.Add(Obj);
+ REG_CONTROL:
+ RegControls.Add(Obj);
+ LOAD_ELEMENT:
+ Loads.Add(Obj);
+ CAP_ELEMENT:
+ ShuntCapacitors.Add(Obj);
+ REACTOR_ELEMENT:
+ Reactors.Add(Obj);
+ RELAY_CONTROL:
+ Relays.Add(Obj);
+ FUSE_CONTROL:
+ Fuses.Add(Obj);
+ RECLOSER_CONTROL:
+ Reclosers.Add(Obj);
+
+ // Keep Lines, Transformer, and Lines and Faults in PDElements and separate lists
+ // so we can find them quickly.
+ AUTOTRANS_ELEMENT:
+ AutoTransformers.Add(Obj);
+ XFMR_ELEMENT:
+ Transformers.Add(Obj);
+ LINE_ELEMENT:
+ Lines.Add(Obj);
+ FAULTOBJECT:
+ Faults.Add(Obj);
+
+ STORAGE_ELEMENT:
+ StorageElements.Add(Obj);
+ PVSYSTEM_ELEMENT:
+ PVSystems.Add(Obj);
+ INV_CONTROL:
+ InvControls.Add(Obj);
+ EXP_CONTROL:
+ ExpControls.Add(Obj);
+ end;
- // get rid of old bus lists
- BusList.Free; // Clears hash list of Bus names for adding more
- BusList := TBusHashListType.Create(NumDevices); // won't have many more buses than this
+ Obj.Handle := CktElements.Count;
+end;
- NumBuses := 0; // Leave allocations same, but start count over
- NumNodes := 0;
+procedure TDSSCircuit.DoResetMeterZones;
+begin
+ // Do this only if meterzones unlocked . Normally, Zones will remain unlocked
+ // so that all changes to the circuit will result in rebuilding the lists
+ if not MeterZonesComputed or not ZonesLocked then
+ begin
+ if LogEvents then
+ LogThisEvent(DSS, 'Resetting Meter Zones');
+ DSS.EnergyMeterClass.ResetMeterZonesAll;
+ MeterZonesComputed := TRUE;
+ if LogEvents then
+ LogThisEvent(DSS, 'Done Resetting Meter Zones');
+ end;
+ FreeTopology;
+end;
- // Now redo all enabled circuit elements
- CktElementSave := ActiveCktElement;
- ActiveCktElement := CktElements.First;
- WHILE ActiveCktElement <> nil DO BEGIN
- IF ActiveCktElement.Enabled THEN ProcessBusDefs;
- IF AbortBusProcess then Exit;
- ActiveCktElement := CktElements.Next;
- END;
+procedure TDSSCircuit.SaveBusInfo;
+var
+ i: Integer;
+begin
+ // Save existing bus definitions and names for info that needs to be restored
+ SavedBuses := Allocmem(Sizeof(SavedBuses^[1]) * NumBuses);
+ SavedBusNames := Allocmem(Sizeof(SavedBusNames^[1]) * NumBuses);
- ActiveCktElement := CktElementSave; // restore active circuit element
+ for i := 1 to NumBuses do
+ begin
+ SavedBuses^[i] := Buses^[i];
+ SavedBusNames^[i] := BusList.NameOfIndex(i);
+ end;
+ SavedNumBuses := NumBuses;
+end;
- FOR i := 1 to NumBuses Do Buses^[i].AllocateBusVoltages;
- FOR i := 1 to NumBuses Do Buses^[i].AllocateBusCurrents;
+procedure TDSSCircuit.RestoreBusInfo;
+var
+ i, j, idx, jdx: Integer;
+ pBus: TDSSBus;
+begin
+ // Restore kV bases, other values to buses still in the list
+ for i := 1 to SavedNumBuses do
+ begin
+ idx := BusList.Find(SavedBusNames^[i]);
+ if idx <> 0 then
+ with Buses^[idx] do
+ begin
+ pBus := SavedBuses^[i];
+ kvBase := pBus.kVBase;
+ x := pBus.x;
+ Y := pBus.y;
+ CoordDefined := pBus.CoordDefined;
+ Keep := pBus.Keep;
+ // Restore Voltages in new bus def that existed in old bus def
+ if assigned(pBus.VBus) then
+ begin
+ for j := 1 to pBus.NumNodesThisBus do
+ begin
+ jdx := FindIdx(pBus.GetNum(j)); // Find index in new bus for j-th node in old bus
+ if jdx > 0 then
+ Vbus^[jdx] := pBus.VBus^[j];
+ end;
+ end;
+ end;
+ SavedBusNames^[i] := ''; // De-allocate string
+ end;
- RestoreBusInfo; // frees old bus info, too
- DoResetMeterZones; // Fix up meter zones to correspond
+ if Assigned(SavedBuses) then
+ for i := 1 to SavedNumBuses do
+ SavedBuses^[i].Free; // gets rid of old bus voltages, too
- BusNameRedefined := False; // Get ready for next time
-END;
+ ReallocMem(SavedBuses, 0);
+ ReallocMem(SavedBusNames, 0);
+end;
-//----------------------------------------------------------------------------
-Procedure TDSSCircuit.Set_BusNameRedefined(Value:Boolean);
-BEGIN
- FBusNameRedefined := Value;
+procedure TDSSCircuit.ReProcessBusDefs;
+// Redo all Buslists, nodelists
+var
+ CktElementSave: TDSSCktElement;
+ i: Integer;
+begin
+ if LogEvents then
+ LogThisEvent(DSS, 'Reprocessing Bus Definitions');
- IF Value THEN Begin
- Solution.SystemYChanged := True; // Force Rebuilding of SystemY if bus def has changed
- Control_BusNameRedefined := True; // So controls will know buses redefined
- End;
-END;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Function TDSSCircuit.Get_Losses:Complex;
-
-Var
- pdelem :TPDElement;
-Begin
-
-{Return total losses in all PD Elements}
-
- pdelem := PDElements.First;
- Result := cZERO;
- While pdelem <> nil Do Begin
- IF pdelem.enabled Then Begin
- {Ignore Shunt Elements}
- If Not pdElem.IsShunt Then Caccum(Result, pdelem.losses);
- End;
- pdelem := PDElements.Next;
- End;
-
-End;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Procedure TDSSCircuit.DebugDump(Var F:TFileStream);
-
-VAR
- i,j:Integer;
- sout: String;
-BEGIN
-
- FSWriteln(F, 'NumBuses= ', IntToStr(NumBuses));
- FSWriteln(F, 'NumNodes= ', IntToStr(NumNodes));
- FSWriteln(F, 'NumDevices= ', IntToStr(NumDevices));
- FSWriteln(F,'BusList:');
- For i := 1 to NumBuses Do BEGIN
- FSWrite(F,' ',Pad(BusList.NameOfIndex(i),12));
- FSWrite(F,' (', IntToStr(Buses^[i].NumNodesThisBus),' Nodes)');
- FOR j := 1 to Buses^[i].NumNodesThisBus Do FSWrite(F,' ',IntToStr(Buses^[i].Getnum(j)));
- FSWriteln(F);
- END;
- FSWriteln(F,'DeviceList:');
- For i := 1 to NumDevices Do BEGIN
- FSWrite(F,' ',Pad(DeviceList.NameOfIndex(i),12));
- ActiveCktElement := CktElements.Get(i);
- If Not ActiveCktElement.Enabled THEN FSWrite(F, ' DISABLED');
- FSWriteln(F);
- END ;
- FSWriteln(F,'NodeToBus Array:');
- For i := 1 to NumNodes DO
- BEGIN
- j := MapNodeToBus^[i].BusRef;
- WriteStr(sout, ' ',i:2,' ',j:2,' (=',BusList.NameOfIndex(j),'.',MapNodeToBus^[i].NodeNum:0,')');
- FSWrite(F, sout);
- FSWriteln(F);
- END;
+ AbortBusProcess := FALSE;
+ SaveBusInfo; // So we don't have to keep re-doing this
+ // Keeps present definitions of bus objects until new ones created
+ // get rid of old bus lists
+ BusList.Free; // Clears hash list of Bus names for adding more
+ BusList := TBusHashListType.Create(NumDevices); // won't have many more buses than this
+ NumBuses := 0; // Leave allocations same, but start count over
+ NumNodes := 0;
-END;
+ // Now redo all enabled circuit elements
+ CktElementSave := ActiveCktElement;
+ ActiveCktElement := CktElements.First;
+ while ActiveCktElement <> NIL do
+ begin
+ if ActiveCktElement.Enabled then
+ ProcessBusDefs;
+ if AbortBusProcess then
+ Exit;
+ ActiveCktElement := CktElements.Next;
+ end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Procedure TDSSCircuit.InvalidateAllPCElements;
+ ActiveCktElement := CktElementSave; // restore active circuit element
-VAR
- p:TDSSCktElement;
+ for i := 1 to NumBuses do
+ Buses^[i].AllocateBusState;
+ RestoreBusInfo; // frees old bus info, too
+ DoResetMeterZones; // Fix up meter zones to correspond
-BEGIN
+ BusNameRedefined := FALSE; // Get ready for next time
+end;
- p := PCElements.First;
- WHILE (p <> nil)
- DO BEGIN
- p.YprimInvalid := True;
- p := PCElements.Next;
- END;
+procedure TDSSCircuit.Set_BusNameRedefined(Value: Boolean);
+begin
+ FBusNameRedefined := Value;
- Solution.SystemYChanged := True; // Force rebuild of matrix on next solution
+ if Value then
+ begin
+ Solution.SystemYChanged := TRUE; // Force Rebuilding of SystemY if bus def has changed
+ Control_BusNameRedefined := TRUE; // So controls will know buses redefined
+ end;
+end;
-END;
+function TDSSCircuit.Get_Losses: Complex;
+var
+ pdelem: TPDElement;
+begin
+ // Return total losses in all PD Elements
+ pdelem := PDElements.First;
+ Result := cZERO;
+ while pdelem <> NIL do
+ begin
+ if pdelem.enabled then
+ begin
+ // Ignore Shunt Elements
+ if not pdElem.IsShunt then
+ Result += pdelem.losses;
+ end;
+ pdelem := PDElements.Next;
+ end;
+end;
+procedure TDSSCircuit.DebugDump(var F: TFileStream);
+var
+ i, j: Integer;
+ sout: String;
+begin
+ FSWriteln(F, 'NumBuses= ', IntToStr(NumBuses));
+ FSWriteln(F, 'NumNodes= ', IntToStr(NumNodes));
+ FSWriteln(F, 'NumDevices= ', IntToStr(NumDevices));
+ FSWriteln(F, 'BusList:');
+ for i := 1 to NumBuses do
+ begin
+ FSWrite(F, ' ', Pad(BusList.NameOfIndex(i), 12));
+ FSWrite(F, ' (', IntToStr(Buses^[i].NumNodesThisBus), ' Nodes)');
+ for j := 1 to Buses^[i].NumNodesThisBus do
+ FSWrite(F, ' ', IntToStr(Buses^[i].Getnum(j)));
+ FSWriteln(F);
+ end;
+ FSWriteln(F, 'DeviceList:');
+ for i := 1 to NumDevices do
+ begin
+ FSWrite(F, ' ', Pad(DeviceList.NameOfIndex(i), 12));
+ ActiveCktElement := CktElements.Get(i);
+ if not ActiveCktElement.Enabled then
+ FSWrite(F, ' DISABLED');
+ FSWriteln(F);
+ end;
+ FSWriteln(F, 'NodeToBus Array:');
+ for i := 1 to NumNodes do
+ begin
+ j := MapNodeToBus^[i].BusRef;
+ WriteStr(sout, ' ', i: 2, ' ', j: 2, ' (=', BusList.NameOfIndex(j), '.', MapNodeToBus^[i].NodeNum: 0, ')');
+ FSWrite(F, sout);
+ FSWriteln(F);
+ end;
+end;
-// - - ------------------------------------------------------
-Procedure TDSSCircuit.Set_LoadMultiplier(Value :Double);
+procedure TDSSCircuit.InvalidateAllPCElements;
+var
+ p: TDSSCktElement;
+begin
+ p := PCElements.First;
+ while (p <> NIL) do
+ begin
+ p.YprimInvalid := TRUE;
+ p := PCElements.Next;
+ end;
-Begin
+ Solution.SystemYChanged := TRUE; // Force rebuild of matrix on next solution
+end;
- If (Value <> FLoadMultiplier)
- Then // We may have to change the Y matrix if the load multiplier has changed
- Case Solution.LoadModel Of
- ADMITTANCE: InvalidateAllPCElements
- Else
+procedure TDSSCircuit.Set_LoadMultiplier(Value: Double);
+begin
+ if (Value <> FLoadMultiplier) then // We may have to change the Y matrix if the load multiplier has changed
+ case Solution.LoadModel of
+ ADMITTANCE:
+ InvalidateAllPCElements
+ else
{nada}
- End;
-
- FLoadMultiplier := Value;
+ end;
-End;
+ FLoadMultiplier := Value;
+end;
procedure TDSSCircuit.TotalizeMeters;
-
-{ Totalize all energymeters in the problem}
-
-Var
- pEM:TEnergyMeterObj;
+// Totalize all energymeters in the problem
+var
+ pEM: TEnergyMeterObj;
i: Integer;
-
begin
- For i := 1 to NumEMRegisters Do RegisterTotals[i] := 0.;
-
- pEM := EnergyMeters.First;
- While pEM <> Nil Do With PEM Do Begin
+ for i := 1 to NumEMRegisters do
+ RegisterTotals[i] := 0.;
- For i := 1 to NumEMRegisters Do RegisterTotals[i] := RegisterTotals[i] + Registers[i] * TotalsMask[i];
+ pEM := EnergyMeters.First;
+ while pEM <> NIL do
+ with PEM do
+ begin
+ for i := 1 to NumEMRegisters do
+ RegisterTotals[i] := RegisterTotals[i] + Registers[i] * TotalsMask[i];
- pEM := EnergyMeters.Next;
- End;
+ pEM := EnergyMeters.Next;
+ end;
end;
-FUNCTION TDSSCircuit.ComputeCapacity: Boolean;
-Var
- CapacityFound :Boolean;
+function TDSSCircuit.ComputeCapacity: Boolean;
+var
+ CapacityFound: Boolean;
- FUNCTION SumSelectedRegisters(Const mtrRegisters:TRegisterArray; Regs: pIntegerArray; count: Integer): Double;
- VAR
- i :Integer;
+ function SumSelectedRegisters(const mtrRegisters: TRegisterArray; Regs: pIntegerArray; count: Integer): Double;
+ var
+ i: Integer;
begin
- Result := 0.0;
- FOR i := 1 to count Do Begin
- Result := Result + mtrRegisters[regs^[i]];
- End;
+ Result := 0.0;
+ for i := 1 to count do
+ begin
+ Result := Result + mtrRegisters[regs^[i]];
+ end;
end;
begin
- Result := FALSE;
- If (EnergyMeters.Count = 0) Then Begin
- DoSimpleMsg(DSS, 'Cannot compute system capacity with EnergyMeter objects!', 430);
- Exit;
- End;
-
- If (NumUeRegs = 0) Then Begin
- DoSimpleMsg(DSS, 'Cannot compute system capacity with no UE resisters defined. Use SET UEREGS=(...) command.', 431);
- Exit;
- End;
-
- Solution.Mode := TSolveMode.SNAPSHOT;
- LoadMultiplier := CapacityStart;
- CapacityFound := False;
-
- Repeat
- DSS.EnergyMeterClass.ResetAll;
- Solution.Solve;
- DSS.EnergyMeterClass.SampleAll;
- TotalizeMeters;
-
- // Check for non-zero in UEregs
- IF SumSelectedRegisters(RegisterTotals, UEregs, NumUEregs) <> 0.0 Then CapacityFound := True;
- // LoadMultiplier is a property ...
- IF Not CapacityFound Then LoadMultiplier := LoadMultiplier + CapacityIncrement;
- Until (LoadMultiplier > 1.0) or CapacityFound;
- If LoadMultiplier>1.0 Then LoadMultiplier := 1.0;
- Result := TRUE;
-end;
+ Result := FALSE;
+ if (EnergyMeters.Count = 0) then
+ begin
+ DoSimpleMsg(DSS, _('Cannot compute system capacity with EnergyMeter objects!'), 430);
+ Exit;
+ end;
+
+ if (NumUeRegs = 0) then
+ begin
+ DoSimpleMsg(DSS, _('Cannot compute system capacity with no UE resisters defined. Use SET UEREGS=(...) command.'), 431);
+ Exit;
+ end;
-Function TDSSCircuit.Save(Dir:String):Boolean;
-{Save the present circuit - Enabled devices only}
+ Solution.Mode := TSolveMode.SNAPSHOT;
+ LoadMultiplier := CapacityStart;
+ CapacityFound := FALSE;
-var
- i:Integer;
- Success:Boolean;
- CurrDir,SaveDir :String;
+ repeat
+ DSS.EnergyMeterClass.ResetAll;
+ Solution.Solve;
+ DSS.EnergyMeterClass.SampleAll;
+ TotalizeMeters;
+
+ // Check for non-zero in UEregs
+ if SumSelectedRegisters(RegisterTotals, UEregs, NumUEregs) <> 0.0 then
+ CapacityFound := TRUE;
+ // LoadMultiplier is a property ...
+ if not CapacityFound then
+ LoadMultiplier := LoadMultiplier + CapacityIncrement;
+ until (LoadMultiplier > 1.0) or CapacityFound;
+ if LoadMultiplier > 1.0 then
+ LoadMultiplier := 1.0;
+ Result := TRUE;
+end;
+function TDSSCircuit.Save(Dir: String): Boolean;
+// Save the present circuit - Enabled devices only
+var
+ i: Integer;
+ Success: Boolean;
+ CurrDir, SaveDir: String;
begin
- Result := FALSE;
-
-// Make a new subfolder in the present folder based on the circuit name and
-// a unique sequence number
- SaveDir := DSS.CurrentDSSDir; // remember where to come back to
- Success := FALSE;
- If Length(Dir)=0 Then Begin
- dir := Name;
-
- CurrDir := Dir;
- For i := 0 to 999 Do // Find a unique dir name
- Begin
- If Not DirectoryExists(CurrDir) Then
- Begin
- If CreateDir(CurrDir) Then
- Begin
- DSS.SetCurrentDSSDir(CurrDir);
- Success := TRUE;
- Break;
- End;
- End;
- CurrDir := dir + Format('%.3d',[i]);
- End;
- End Else Begin
- If Not DirectoryExists(Dir) Then Begin
- CurrDir := dir;
- If CreateDir(CurrDir) Then
- Begin
- DSS.SetCurrentDSSDir(CurrDir);
- Success := TRUE;
- End;
- End Else Begin // Exists - overwrite
- CurrDir := Dir;
- DSS.SetCurrentDSSDir(CurrDir);
- Success := TRUE;
- End;
- End;
-
- If Not Success Then
- Begin
- DoSimpleMsg(DSS, 'Could not create a folder "'+Dir+'" for saving the circuit.', 432);
- Exit;
- End;
-
- DSS.SavedFileList.Clear; {This list keeps track of all files saved}
+ Result := FALSE;
+ // Make a new subfolder in the present folder based on the circuit name and
+ // a unique sequence number
+ SaveDir := DSS.CurrentDSSDir; // remember where to come back to
+ Success := FALSE;
+ if Length(Dir) = 0 then
+ begin
+ dir := Name;
+
+ CurrDir := Dir;
+ for i := 0 to 999 do // Find a unique dir name
+ begin
+ if not DirectoryExists(CurrDir) then
+ begin
+ if CreateDir(CurrDir) then
+ begin
+ DSS.SetCurrentDSSDir(CurrDir);
+ Success := TRUE;
+ Break;
+ end;
+ end;
+ CurrDir := dir + Format('%.3d', [i]);
+ end;
+ end
+ else
+ begin
+ if not DirectoryExists(Dir) then
+ begin
+ CurrDir := dir;
+ if CreateDir(CurrDir) then
+ begin
+ DSS.SetCurrentDSSDir(CurrDir);
+ Success := TRUE;
+ end;
+ end
+ else
+ begin // Exists - overwrite
+ CurrDir := Dir;
+ DSS.SetCurrentDSSDir(CurrDir);
+ Success := TRUE;
+ end;
+ end;
+
+ if not Success then
+ begin
+ DoSimpleMsg(DSS, 'Could not create a folder "%s" for saving the circuit.', [Dir], 432);
+ Exit;
+ end;
+
+ DSS.SavedFileList.Clear; // This list keeps track of all files saved
// Initialize so we will know when we have saved the circuit elements
- For i := 1 to CktElements.Count Do TDSSCktElement(CktElements.Get(i)).HasBeenSaved := False;
+ for i := 1 to CktElements.Count do
+ Exclude(TDSSCktElement(CktElements.Get(i)).Flags, Flg.HasBeenSaved);
// Initialize so we don't save a class twice
- For i := 1 to DSS.DSSClassList.Count Do TDssClass(DSS.DSSClassList.Get(i)).Saved := FALSE;
-
- {Ignore Feeder Class -- gets saved with Energymeters}
- // FeederClass.Saved := TRUE; // will think this class is already saved
-
- {Define voltage sources first}
- Success := WriteVsourceClassFile(DSS, GetDssClassPtr(DSS, 'vsource'), TRUE);
- {Write library files so that they will be available to lines, loads, etc}
- {Use default filename=classname}
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'wiredata'),'', FALSE);
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'cndata'),'', FALSE);
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'tsdata'),'', FALSE);
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'linegeometry'),'', FALSE);
+ for i := 1 to DSS.DSSClassList.Count do
+ TDssClass(DSS.DSSClassList.Get(i)).Saved := FALSE;
+
+ // Define voltage sources first
+ Success := WriteVsourceClassFile(DSS, GetDssClassPtr(DSS, 'vsource'), TRUE);
+ // Write library files so that they will be available to lines, loads, etc
+ // Use default filename=classname
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'wiredata'), '', FALSE);
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'cndata'), '', FALSE);
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'tsdata'), '', FALSE);
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'linegeometry'), '', FALSE);
// If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'linecode'),'', FALSE);
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'linespacing'),'', FALSE);
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'linecode'),'', FALSE);
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'xfmrcode'),'', FALSE);
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'loadshape'),'', FALSE);
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'TShape'),'', FALSE);
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'priceshape'),'', FALSE);
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'growthshape'),'', FALSE);
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'XYcurve'),'', FALSE);
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'TCC_Curve'),'', FALSE);
- If Success Then Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'Spectrum'),'', FALSE);
- If Success Then Success := SaveFeeders; // Save feeders first
- If Success Then Success := SaveDSSObjects; // Save rest ot the objects
- If Success Then Success := SaveVoltageBases;
- If Success Then Success := SaveBusCoords;
- If Success Then Success := SaveMasterFile;
-
- If Success Then
- Begin
- DSS.GlobalResult := 'Circuit saved in directory: ' + DSS.CurrentDSSDir;
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'linespacing'), '', FALSE);
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'linecode'), '', FALSE);
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'xfmrcode'), '', FALSE);
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'loadshape'), '', FALSE);
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'TShape'), '', FALSE);
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'priceshape'), '', FALSE);
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'growthshape'), '', FALSE);
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'XYcurve'), '', FALSE);
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'TCC_Curve'), '', FALSE);
+ if Success then
+ Success := WriteClassFile(DSS, GetDssClassPtr(DSS, 'Spectrum'), '', FALSE);
+ if Success then
+ Success := SaveFeeders; // Save feeders first
+ if Success then
+ Success := SaveDSSObjects; // Save rest ot the objects
+ if Success then
+ Success := SaveVoltageBases;
+ if Success then
+ Success := SaveBusCoords;
+ if Success then
+ Success := SaveMasterFile;
+
+ if Success then
+ begin
+ DSS.GlobalResult := Format('Circuit saved in directory: %s', [DSS.CurrentDSSDir]);
// DoSimpleMsg('Circuit saved in directory: ' + CurrentDSSDir, 433)
- End
- Else
- DoSimpleMsg(DSS, 'Error attempting to save circuit in ' + DSS.CurrentDSSDir, 434);
+ end
+ else
+ DoSimpleMsg(DSS, 'Error attempting to save circuit in %s', [DSS.CurrentDSSDir], 434);
// Return to Original directory
DSS.SetCurrentDSSDir(SaveDir);
Result := TRUE;
-
end;
function TDSSCircuit.SaveDSSObjects: Boolean;
-Var
+var
- Dss_Class:TDSSClass;
- i:integer;
+ Dss_Class: TDSSClass;
+ i: Integer;
begin
- Result := FALSE;
-
- // Write Files for all populated DSS Classes Except Solution Class
- For i := 1 to DSS.DSSClassList.Count Do
- Begin
- Dss_Class := DSS.DSSClassList.Get(i);
- If (DSS_Class = DSS.SolutionClass) or Dss_Class.Saved Then Continue; // Cycle to next
- {use default filename=classname}
- IF Not WriteClassFile(DSS, Dss_Class,'', (DSS_Class is TCktElementClass) ) Then Exit; // bail on error
- DSS_Class.Saved := TRUE;
- End;
+ Result := FALSE;
- Result := TRUE;
+ // Write Files for all populated DSS Classes Except Solution Class
+ for i := 1 to DSS.DSSClassList.Count do
+ begin
+ Dss_Class := DSS.DSSClassList.Get(i);
+ if Dss_Class.Saved then
+ Continue; // Cycle to next
+ //use default filename=classname
+ if not WriteClassFile(DSS, Dss_Class, '', (DSS_Class is TCktElementClass)) then
+ Exit; // bail on error
+ DSS_Class.Saved := TRUE;
+ end;
+ Result := TRUE;
end;
function TDSSCircuit.SaveVoltageBases: Boolean;
-Var
- F: TFileStream = nil;
- VBases:string;
-Begin
+var
+ F: TFileStream = NIL;
+ VBases: String;
+begin
Result := FALSE;
- Try
- F := TFileStream.Create(DSS.CurrentDSSDir + 'BusVoltageBases.DSS', fmCreate);
+ try
+ F := TBufferedFileStream.Create(DSS.CurrentDSSDir + 'BusVoltageBases.dss', fmCreate);
DSS.DssExecutive.Command := 'get voltagebases';
VBases := DSS.GlobalResult;
- FSWriteln(F, 'Set Voltagebases='+VBases);
+ FSWriteln(F, 'Set Voltagebases=' + VBases);
FreeAndNil(F);
Result := TRUE;
- Except
- On E:Exception Do
- DoSimpleMsg(DSS, 'Error Saving BusVoltageBases File: '+E.Message, 43501);
- End;
+ except
+ On E: Exception do
+ DoSimpleMsg(DSS, 'Error Saving BusVoltageBases File: %s', [E.Message], 43501);
+ end;
FreeAndNil(F);
-End;
+end;
function TDSSCircuit.SaveMasterFile: Boolean;
+var
+ F: TFileStream = NIL;
+ i: Integer;
+begin
+ Result := FALSE;
+ try
+ F := TBufferedFileStream.Create(DSS.CurrentDSSDir + 'Master.dss', fmCreate);
+ FSWriteln(F, 'Clear');
+ FSWriteln(F, 'Set DefaultBaseFreq=', FloatToStr(DSS.DefaultBaseFreq));
+ FSWriteln(F, 'New Circuit.' + Name);
+ FSWriteln(F);
+ If PositiveSequence Then
+ FSWriteln(F, 'Set Cktmodel=', DSS.CktModelEnum.OrdinalToString(Integer(PositiveSequence)));
+ if DuplicatesAllowed then
+ FSWriteln(F, 'set allowdup=yes');
+ FSWriteln(F);
-Var
- F: TFileStream = nil;
- i:integer;
+ // Write Redirect for all populated DSS Classes Except Solution Class
+ for i := 1 to DSS.SavedFileList.Count do
+ begin
+ FSWrite(F, 'Redirect ');
+ FSWriteln(F, ExtractRelativePath(DSS.CurrentDSSDir, DSS.SavedFileList.Strings[i - 1]));
+ end;
-begin
- Result := FALSE;
- Try
- F := TFileStream.Create(DSS.CurrentDSSDir + 'Master.DSS', fmCreate);
- FSWriteln(F, 'Clear');
- FSWriteln(F,'New Circuit.' + Name);
- FSWriteln(F);
- If PositiveSequence Then FSWriteln(F, 'Set Cktmodel=Positive');
- If DuplicatesAllowed Then FSWriteln(F, 'set allowdup=yes');
- FSWriteln(F);
-
- // Write Redirect for all populated DSS Classes Except Solution Class
- For i := 1 to DSS.SavedFileList.Count Do
- Begin
- FSWrite(F, 'Redirect ');
- FSWriteln(F, DSS.SavedFileList.Strings[i-1]);
- End;
-
- FSWriteln(F,'MakeBusList');
- FSWriteln(F,'Redirect BusVoltageBases.dss ! set voltage bases');
-
- If FileExists('buscoords.dss') Then
- Begin
- FSWriteln(F, 'Buscoords buscoords.dss');
- End;
-
- FreeAndNil(F);
- Result := TRUE;
- Except
- On E:Exception Do DoSimpleMsg(DSS, 'Error Saving Master File: '+E.Message, 435);
- End;
- FreeAndNil(F);
+ FSWriteln(F, 'MakeBusList');
+ FSWriteln(F, 'Redirect BusVoltageBases.dss ! set voltage bases');
+
+ if FileExists('BusCoords.dss') then
+ begin
+ FSWriteln(F, 'BusCoords BusCoords.dss');
+ end;
+
+ FreeAndNil(F);
+ Result := TRUE;
+ except
+ On E: Exception do
+ DoSimpleMsg(DSS, 'Error Saving Master File: %s', [E.Message], 435);
+ end;
+ FreeAndNil(F);
end;
function TDSSCircuit.SaveFeeders: Boolean;
-Var
- i:Integer;
- SaveDir, CurrDir:String;
- Meter:TEnergyMeterObj;
+var
+ i: Integer;
+ SaveDir, CurrDir: String;
+ Meter: TEnergyMeterObj;
begin
-
- Result := TRUE;
-{Write out all energy meter zones to separate subdirectories}
- SaveDir := DSS.CurrentDSSDir;
- For i := 1 to EnergyMeters.Count Do
- Begin
+ Result := TRUE;
+ // Write out all energy meter zones to separate subdirectories
+ SaveDir := DSS.CurrentDSSDir;
+ for i := 1 to EnergyMeters.Count do
+ begin
Meter := EnergyMeters.Get(i); // Recast pointer
- CurrDir := Meter.Name;
- if not Meter.Enabled then // Only active meters
+ CurrDir := Meter.Name;
+ if not Meter.Enabled then // Only active meters
continue;
-
- If DirectoryExists(CurrDir) Then
- Begin
+
+ if DirectoryExists(CurrDir) then
+ begin
DSS.SetCurrentDSSDir(CurrDir);
Meter.SaveZone(CurrDir);
DSS.SetCurrentDSSDir(SaveDir);
- End
- Else Begin
- If CreateDir(CurrDir) Then
- Begin
+ end
+ else
+ begin
+ if CreateDir(CurrDir) then
+ begin
DSS.SetCurrentDSSDir(CurrDir);
Meter.SaveZone(CurrDir);
DSS.SetCurrentDSSDir(SaveDir);
- End
- Else Begin
- DoSimpleMsg(DSS, 'Cannot create directory: '+CurrDir, 436);
+ end
+ else
+ begin
+ DoSimpleMsg(DSS, 'Cannot create directory: "%s"', [CurrDir], 436);
Result := FALSE;
DSS.SetCurrentDSSDir(SaveDir); // back to whence we came
Break;
- End;
- End;
- End; {For}
-
+ end;
+ end;
+ end;
end;
function TDSSCircuit.SaveBusCoords: Boolean;
-Var
- F: TFileStream = nil;
+var
+ F: TFileStream = NIL;
i: Integer;
begin
+ Result := FALSE;
+ try
+ F := TBufferedFileStream.Create(DSS.CurrentDSSDir + 'BusCoords.dss', fmCreate);
- Result := FALSE;
-
- Try
- F := TFileStream.Create(DSS.CurrentDSSDir + 'BusCoords.dss', fmCreate);
-
- For i := 1 to NumBuses Do
- Begin
- If Buses^[i].CoordDefined then
- begin
- FSWrite(F, CheckForBlanks(BusList.NameOfIndex(i)));
- FSWriteLn(F, Format(', %-g, %-g', [Buses^[i].X, Buses^[i].Y]));
- end;
- End;
+ for i := 1 to NumBuses do
+ begin
+ if Buses^[i].CoordDefined then
+ begin
+ FSWrite(F, CheckForBlanks(BusList.NameOfIndex(i)));
+ FSWriteLn(F, Format(', %-g, %-g', [Buses^[i].X, Buses^[i].Y]));
+ end;
+ end;
- FreeAndNil(F);
+ FreeAndNil(F);
- Result := TRUE;
+ Result := TRUE;
- Except
- On E:Exception Do DoSimpleMsg(DSS, 'Error creating Buscoords.dss.', 437);
- End;
- FreeAndNil(F);
+ except
+ On E: Exception do
+ DoSimpleMsg(DSS, _('Error creating %s.'), ['BusCoords.dss'], 437);
+ end;
+ FreeAndNil(F);
end;
procedure TDSSCircuit.ReallocDeviceList;
-
-Var
- TempList:THashList;
- i:Integer;
-
+var
+ TempList: THashList;
+ i: Integer;
begin
-{Reallocate the device list to improve the performance of searches}
- If LogEvents Then LogThisEvent(DSS, 'Reallocating Device List');
- TempList := THashList.Create(2*NumDevices);
+ // Reallocate the device list to improve the performance of searches
+ if LogEvents then
+ LogThisEvent(DSS, _('Reallocating Device List'));
+ TempList := THashList.Create(2 * NumDevices);
- For i := 1 to DeviceList.Count Do
- Begin
+ for i := 1 to DeviceList.Count do
+ begin
Templist.Add(DeviceList.NameOfIndex(i));
- End;
+ end;
DeviceList.Free; // Throw away the old one.
Devicelist := TempList;
-
end;
procedure TDSSCircuit.Set_CaseName(const Value: String);
begin
- FCaseName := Value;
- DSS.CircuitName_ := Value + '_';
+ FCaseName := Value;
+ DSS.CircuitName_ := Value + '_';
end;
-function TDSSCircuit.Get_Name:String;
+function TDSSCircuit.Get_Name: String;
begin
- Result:=LocalName;
+ Result := LocalName;
end;
-Function TDSSCircuit.GetBusAdjacentPDLists: TAdjArray;
+function TDSSCircuit.GetBusAdjacentPDLists: TAdjArray;
begin
- if not Assigned (BusAdjPD) then BuildActiveBusAdjacencyLists(self, BusAdjPD, BusAdjPC);
- Result := BusAdjPD;
+ if not Assigned(BusAdjPD) then
+ BuildActiveBusAdjacencyLists(self, BusAdjPD, BusAdjPC);
+ Result := BusAdjPD;
end;
-Function TDSSCircuit.GetBusAdjacentPCLists: TAdjArray;
+function TDSSCircuit.GetBusAdjacentPCLists: TAdjArray;
begin
- if not Assigned (BusAdjPC) then BuildActiveBusAdjacencyLists(self, BusAdjPD, BusAdjPC);
- Result := BusAdjPC;
+ if not Assigned(BusAdjPC) then
+ BuildActiveBusAdjacencyLists(self, BusAdjPD, BusAdjPC);
+ Result := BusAdjPC;
end;
-Function TDSSCircuit.GetTopology: TCktTree;
+function TDSSCircuit.GetTopology: TCktTree;
var
- i: Integer;
- elem: TDSSCktElement;
+ i: Integer;
+ elem: TDSSCktElement;
begin
- if Not assigned(Branch_List) then begin
- {Initialize all Circuit Elements and Buses to not checked, then build a new tree}
- elem := CktElements.First;
- WHILE assigned (elem) Do Begin
- elem.Checked := False;
- For i := 1 to elem.Nterms Do elem.TerminalsChecked[i - 1] := FALSE;
- elem.IsIsolated := TRUE; // till proven otherwise
- elem := CktElements.Next;
- End;
- FOR i := 1 to NumBuses Do Buses^[i].BusChecked := FALSE;
- Branch_List := GetIsolatedSubArea(self, Sources.First, TRUE); // calls back to build adjacency lists
- end;
- Result := Branch_List;
+ if not assigned(Branch_List) then
+ begin
+ // Initialize all Circuit Elements and Buses to not checked, then build a new tree
+ elem := CktElements.First;
+ while assigned(elem) do
+ begin
+ Exclude(elem.Flags, Flg.Checked);
+ for i := 1 to elem.Nterms do
+ elem.TerminalsChecked[i - 1] := FALSE;
+ Include(elem.Flags, Flg.IsIsolated); // till proven otherwise
+ elem := CktElements.Next;
+ end;
+ for i := 1 to NumBuses do
+ Buses^[i].BusChecked := FALSE;
+ Branch_List := GetIsolatedSubArea(self, Sources.First, TRUE); // calls back to build adjacency lists
+ end;
+ Result := Branch_List;
end;
-Procedure TDSSCircuit.FreeTopology;
+procedure TDSSCircuit.FreeTopology;
begin
- if Assigned (Branch_List) then Branch_List.Free;
- Branch_List := nil;
- if Assigned (BusAdjPC) then FreeAndNilBusAdjacencyLists (BusAdjPD, BusAdjPC);
+ if Assigned(Branch_List) then
+ Branch_List.Free;
+ Branch_List := NIL;
+ if Assigned(BusAdjPC) then
+ FreeAndNilBusAdjacencyLists(BusAdjPD, BusAdjPC);
end;
-PROCEDURE TDSSCircuit.ClearBusMarkers;
-Var
- i:Integer;
-Begin
- For i := 0 to BusMarkerList.count-1 do TBusMarker(BusMarkerList.Items[i]).Free;
+procedure TDSSCircuit.ClearBusMarkers;
+var
+ i: Integer;
+begin
+ for i := 0 to BusMarkerList.count - 1 do
+ TBusMarker(BusMarkerList.Items[i]).Free;
BusMarkerList.Clear;
-End ;
-
-{====================================================================}
-{ TBusMarker }
-{====================================================================}
+end;
constructor TBusMarker.Create;
begin
- inherited;
- BusName := '';
- AddMarkerColor := {$IFDEF FPC}0{$ELSE}clBlack{$ENDIF};
- AddMarkerCode := 4;
- AddMarkerSize := 1;
+ inherited;
+ BusName := '';
+ AddMarkerColor := clBlack;
+ AddMarkerCode := 4;
+ AddMarkerSize := 1;
end;
destructor TBusMarker.Destroy;
begin
- BusName := '';
- inherited;
+ BusName := '';
+ inherited;
end;
-
-
end.
diff --git a/src/Common/CktElement.pas b/src/Common/CktElement.pas
index 5bc473cde..0dc8c3004 100644
--- a/src/Common/CktElement.pas
+++ b/src/Common/CktElement.pas
@@ -6,17 +6,12 @@
All rights reserved.
----------------------------------------------------------
}
-{
- 2-17-00 Modified Get_ConductorClosed to handle Index=0
- 7-14-01 Changed way Enabled property works.
- 8-17-06 Caught BusnameRedefined error when nconds changed
-}
interface
uses
Classes,
- Ucomplex,
+ UComplex, DSSUcomplex,
Ucmatrix,
ArrayDef,
Terminal,
@@ -31,18 +26,17 @@ TDSSCktElement = class(TDSSObject)
PROTECTED
FEnabled: Boolean;
PRIVATE
- FBusNames: pStringArray;
- FEnabledProperty: Integer;
- FActiveTerminal: Integer;
+ FBusNames: pStringArray; // Bus + Nodes (a.1.2.3.0)
+ FActiveTerminal: Int8;
FYPrimInvalid: Boolean;
FHandle: Integer;
procedure Set_Freq(Value: Double); // set freq and recompute YPrim.
- procedure Set_Nconds(Value: Integer);
- procedure Set_NPhases(Value: Integer);
- procedure Set_ActiveTerminal(value: Integer);
- function Get_ConductorClosed(Index: Integer): Boolean;
+ procedure Set_Nconds(Value: Int8);
+ function Get_ActiveTerminal(): Int8; inline;
+ procedure Set_ActiveTerminal(value: Int8);
+ function Get_ConductorClosed(Index: Integer): Boolean; inline;
procedure Set_YprimInvalid(const Value: Boolean);
function Get_FirstBus: String;
function Get_NextBus: String;
@@ -54,15 +48,10 @@ TDSSCktElement = class(TDSSObject)
procedure DoYprimCalcs(Ymatrix: TCMatrix);
-{$IFDEF DSS_CAPI}
PUBLIC
-{$ELSE}
- PROTECTED
-{$ENDIF}
-
- Fnterms: Integer;
- Fnconds: Integer; // no. conductors per terminal
- Fnphases: Integer; // Phases, this device
+ Fnterms: Int8;
+ Fnconds: Int8; // no. conductors per terminal
+ Fnphases: Integer; // Phases, this device -- TODO: Int8 someday...
ComplexBuffer: pComplexArray;
@@ -76,29 +65,16 @@ TDSSCktElement = class(TDSSObject)
procedure Set_Enabled(Value: Boolean); VIRTUAL;
procedure Set_ConductorClosed(Index: Integer; Value: Boolean); VIRTUAL;
- procedure Set_NTerms(Value: Integer); VIRTUAL;
+ procedure Set_NTerms(Value: Int8);
procedure Set_Handle(Value: Integer);
PUBLIC
- {Total Noderef array for element}
+ // Total Noderef array for element
NodeRef: pIntegerArray; // Need fast access to this
Yorder: Integer;
- LastTerminalChecked: Integer; // Flag used in tree searches
-
- //TODO: use bit flags to reduce the size of this?
- Checked,
- HasEnergyMeter,
- HasSensorObj,
- IsIsolated,
- HasControl,
- IsMonitored,
- IsPartofFeeder,
- Drawn: Boolean; // Flag used in tree searches etc
-
- HasOCPDevice: Boolean; // Fuse, Relay, or Recloser
- HasAutoOCPDevice: Boolean; // Relay or Recloser only
- HasSwtControl: Boolean; // Has a remotely-controlled Switch
+ // LastTerminalChecked: Int8; // Flag used in tree searches -- UNUSED
+
ControlElementList: TDSSPointerList; //Pointer to control for this device
Iterminal: pComplexArray; // Others need this
@@ -117,6 +93,7 @@ TDSSCktElement = class(TDSSObject)
constructor Create(ParClass: TDSSClass);
destructor Destroy; OVERRIDE;
+ procedure MakeLike(OtherObj: Pointer); override;
function GetYPrim(var Ymatrix: TCmatrix; Opt: Integer): Integer; VIRTUAL; //returns values of array
function GetYPrimValues(Opt: Integer): pComplexArray; VIRTUAL;
@@ -128,12 +105,12 @@ TDSSCktElement = class(TDSSObject)
function InjCurrents: Integer; VIRTUAL; // Applies to PC Elements Puts straight into Solution Array
function GetBus(i: Integer): String; // Get bus name by index
- procedure SetBus(i: Integer; const s: String); // Set bus name by index
+ procedure SetBus(i: Integer; const s: String); virtual; // Set bus name by index
procedure SetNodeRef(iTerm: Integer; NodeRefArray: pIntegerArray); VIRTUAL; // Set NodeRef Array for fast solution with intrinsics
procedure RecalcElementData; VIRTUAL; ABSTRACT;
procedure CalcYPrim; VIRTUAL;
- procedure MakePosSequence; VIRTUAL; // Make a positive Sequence Model
+ procedure MakePosSequence(); VIRTUAL; // Make a positive Sequence Model
procedure GetTermVoltages(iTerm: Integer; VBuffer: PComplexArray);
procedure GetPhasePower(PowerBuffer: pComplexArray); VIRTUAL;
@@ -141,17 +118,15 @@ TDSSCktElement = class(TDSSObject)
procedure GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Complex); VIRTUAL;
procedure GetSeqLosses(var PosSeqLosses, NegSeqLosses, ZeroModeLosses: complex); VIRTUAL;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
property Handle: Integer READ FHandle WRITE Set_Handle;
property Enabled: Boolean READ FEnabled WRITE Set_Enabled;
property YPrimInvalid: Boolean READ FYPrimInvalid WRITE set_YprimInvalid;
property YPrimFreq: Double READ FYprimFreq WRITE Set_Freq;
- property NTerms: Integer READ Fnterms WRITE Set_NTerms;
- property NConds: Integer READ Fnconds WRITE Set_Nconds;
- property NPhases: Integer READ Fnphases WRITE Set_NPhases;
+ property NTerms: Int8 READ Fnterms WRITE Set_NTerms;
+ property NConds: Int8 READ Fnconds WRITE Set_Nconds;
+ property NPhases: Integer READ Fnphases;
property FirstBus: String READ Get_FirstBus;
property NextBus: String READ Get_NextBus; // null string if no more values
property Losses: Complex READ Get_Losses;
@@ -159,7 +134,7 @@ TDSSCktElement = class(TDSSObject)
property MaxPower[idxTerm: Integer]: Complex READ Get_MaxPower; // Total power in active terminal
property MaxCurrent[idxTerm: Integer]: Double READ Get_MaxCurrent; // Max current in active terminal
property MaxVoltage[idxTerm: Integer]: Double READ Get_MaxVoltage; // Max voltage in active terminal
- property ActiveTerminalIdx: Integer READ FActiveTerminal WRITE Set_ActiveTerminal;
+ property ActiveTerminalIdx: Int8 READ Get_ActiveTerminal WRITE Set_ActiveTerminal;
property Closed[Index: Integer]: Boolean READ Get_ConductorClosed WRITE Set_ConductorClosed;
procedure SumCurrents;
@@ -181,10 +156,9 @@ implementation
TypInfo,
CktElementClass;
-var
- cEpsilon : Complex;
+const
+ cEpsilon : Complex = (re: EPSILON; im: 0.0);
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
constructor TDSSCktElement.Create(ParClass: TDSSClass);
begin
inherited Create(ParClass);
@@ -196,6 +170,8 @@ constructor TDSSCktElement.Create(ParClass: TDSSClass);
FBusNames := NIL;
Vterminal := NIL;
Iterminal := NIL; // present value of terminal current
+ Terminals := NIL;
+ TerminalsChecked := NIL;
ComplexBuffer := NIL;
PublicDataStruct := NIL; // pointer to fixed struct of data to be shared
@@ -211,32 +187,19 @@ constructor TDSSCktElement.Create(ParClass: TDSSClass);
YPrimInvalid := TRUE;
FEnabled := TRUE;
- HasEnergyMeter := FALSE;
- HasSensorObj := FALSE;
- HasOCPDevice := FALSE;
- HasAutoOCPDevice := FALSE;
- HasSwtControl := FALSE;
- HasControl := FALSE;
- IsMonitored := FALSE; // indicates some control is monitoring this element
- IsPartofFeeder := FALSE;
- IsIsolated := FALSE;
-
- Drawn := FALSE;
-
- // Make list for a small number of controls with an increment of 1
+
+ // Make list for a small number of controls with an increment of 1
ControlElementList := TDSSPointerList.Create(1);
- FActiveTerminal := 1;
- LastTerminalChecked := 0;
+ FActiveTerminal := 0;
+ // LastTerminalChecked := 0;
-{ Indicates which solution Itemp is computed for }
+ // Indicates which solution Itemp is computed for
IterminalSolutionCount := -1;
BaseFrequency := ActiveCircuit.Fundamental;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TDSSCktElement.Destroy;
var
i: Integer;
@@ -250,6 +213,7 @@ destructor TDSSCktElement.Destroy;
FBusNames^[i] := ''; // Free up strings
SetLength(Terminals, 0);
+ SetLength(TerminalsChecked, 0);
Reallocmem(FBusNames, 0);
Reallocmem(Iterminal, 0);
Reallocmem(Vterminal, 0);
@@ -269,7 +233,7 @@ destructor TDSSCktElement.Destroy;
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
procedure TDSSCktElement.Set_YprimInvalid(const Value: Boolean);
begin
FYPrimInvalid := value;
@@ -277,13 +241,18 @@ procedure TDSSCktElement.Set_YprimInvalid(const Value: Boolean);
// If this device is in the circuit, then we have to rebuild Y on a change in Yprim
ActiveCircuit.Solution.SystemYChanged := TRUE;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TDSSCktElement.Set_ActiveTerminal(value: Integer);
+
+function TDSSCktElement.Get_ActiveTerminal(): Int8; inline;
+begin
+ Result := FActiveTerminal + 1;
+end;
+
+procedure TDSSCktElement.Set_ActiveTerminal(value: Int8);
begin
if (Value > 0) and (Value <= fNterms) then
begin
- FActiveTerminal := Value;
- ActiveTerminal := @Terminals[Value - 1];
+ FActiveTerminal := Value - 1;
+ ActiveTerminal := @Terminals[FActiveTerminal];
end;
end;
@@ -292,7 +261,7 @@ procedure TDSSCktElement.Set_Handle(value: Integer);
FHandle := value;
end;
-function TDSSCktElement.Get_ConductorClosed(Index: Integer): Boolean;
+function TDSSCktElement.Get_ConductorClosed(Index: Integer): Boolean; inline;
// return state of selected conductor
// if index=0 return true if all phases closed, else false
var
@@ -303,7 +272,7 @@ function TDSSCktElement.Get_ConductorClosed(Index: Integer): Boolean;
Result := TRUE;
for i := 1 to Fnphases do
begin
- if not Terminals[FActiveTerminal - 1].ConductorsClosed[i - 1] then
+ if not Terminals[FActiveTerminal].ConductorsClosed[i - 1] then
begin
Result := FALSE;
Break;
@@ -312,19 +281,19 @@ function TDSSCktElement.Get_ConductorClosed(Index: Integer): Boolean;
end
else
if (Index > 0) and (Index <= Fnconds) then
- Result := Terminals[FActiveTerminal - 1].ConductorsClosed[Index - 1]
+ Result := Terminals[FActiveTerminal].ConductorsClosed[Index - 1]
else
Result := FALSE;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
procedure TDSSCktElement.Set_ConductorClosed(Index: Integer; Value: Boolean);
var
i: Integer;
begin
if (Index = 0) then
begin // Do all conductors
- for i := 1 to Fnphases do
- Terminals[FActiveTerminal - 1].ConductorsClosed[i - 1] := Value;
+ for i := 0 to Fnphases - 1 do
+ Terminals[FActiveTerminal].ConductorsClosed[i] := Value;
// DSS.ActiveCircuit.Solution.SystemYChanged := TRUE; // force Y matrix rebuild
YPrimInvalid := TRUE; // this also sets the global SystemYChanged flag
end
@@ -332,21 +301,20 @@ procedure TDSSCktElement.Set_ConductorClosed(Index: Integer; Value: Boolean);
begin
if (Index > 0) and (Index <= Fnconds) then
begin
- Terminals[FActiveTerminal - 1].ConductorsClosed[index - 1] := Value;
+ Terminals[FActiveTerminal].ConductorsClosed[index - 1] := Value;
// DSS.ActiveCircuit.Solution.SystemYChanged := TRUE;
YPrimInvalid := TRUE;
end;
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TDSSCktElement.Set_NConds(Value: Integer);
+procedure TDSSCktElement.Set_NConds(Value: Int8);
begin
-// Check for an almost certain programming error
+ // Check for an almost certain programming error
if Value <= 0 then
begin
- DoSimpleMsg(Format('Invalid number of terminals (%d) for "%s.%s"',
- [Value, Parentclass.Name, name]), 749);
+ DoSimpleMsg('Invalid number of terminals (%d) for "%s"',
+ [Value, FullName], 749);
Exit;
end;
@@ -356,25 +324,16 @@ procedure TDSSCktElement.Set_NConds(Value: Integer);
Set_Nterms(fNterms); // ReallocTerminals NEED MORE EFFICIENT WAY TO DO THIS
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TDSSCktElement.Set_NPhases(Value: Integer);
-begin
- if Value > 0 then
- Fnphases := Value;
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TDSSCktElement.Set_NTerms(Value: Integer);
+procedure TDSSCktElement.Set_NTerms(Value: Int8);
var
i: Integer;
NewBusNames: pStringArray;
begin
-
-// Check for an almost certain programming error
+ // Check for an almost certain programming error
if Value <= 0 then
begin
- DoSimpleMsg(Format('Invalid number of terminals (%d) for "%s.%s"',
- [Value, Parentclass.Name, name]), 749);
+ DoSimpleMsg('Invalid number of terminals (%d) for "%s"',
+ [Value, FullName], 749);
Exit;
end;
@@ -383,16 +342,15 @@ procedure TDSSCktElement.Set_NTerms(Value: Integer);
if (value = FNterms) and ((Value * Fnconds) = Yorder) then
Exit;
- {Sanity Check}
+ // Sanity Check
if Fnconds > 101 then
begin
- DoSimpleMsg(Format('Warning: Number of conductors is very large (%d) for Circuit Element: "%s.%s.' +
- 'Possible error in specifying the Number of Phases for element.',
- [Fnconds, Parentclass.Name, name]), 750);
+ DoSimpleMsg('Warning: Number of conductors is very large (%d) for Circuit Element: "%s". Possible error in specifying the Number of Phases for element.',
+ [Fnconds, FullName], 750);
end;
- {ReAllocate BusNames }
+ // ReAllocate BusNames
// because they are Strings, we have to do it differently
if Value < fNterms then
@@ -402,11 +360,11 @@ procedure TDSSCktElement.Set_NTerms(Value: Integer);
if FBusNames = NIL then
begin
// First allocation
- { Always allocate arrays of strings with AllocMem so that the pointers are all nil
- else Delphi thinks non-zero values are pointing to an existing string.}
+ // Always allocate arrays of strings with AllocMem so that the pointers are all nil
+ // else Delphi thinks non-zero values are pointing to an existing string.
FBusNames := AllocMem(Sizeof(FBusNames^[1]) * Value); // fill with zeros or strings will crash
for i := 1 to Value do
- FBusNames^[i] := Name + '_' + IntToStr(i); // Make up a bus name to stick in.
+ FBusNames[i] := Name + '_' + IntToStr(i); // Make up a bus name to stick in.
// This is so devices like transformers which may be defined on multiple commands
// will have something in the BusNames array.
end
@@ -414,9 +372,9 @@ procedure TDSSCktElement.Set_NTerms(Value: Integer);
begin
NewBusNames := AllocMem(Sizeof(FBusNames^[1]) * Value); // make some new space
for i := 1 to fNterms do
- NewBusNames^[i] := FBusNames^[i]; // copy old into new
+ NewBusNames[i] := FBusNames^[i]; // copy old into new
for i := 1 to fNterms do
- FBusNames^[i] := ''; // decrement usage counts by setting to nil string
+ FBusNames[i] := ''; // decrement usage counts by setting to nil string
for i := fNterms + 1 to Value do
NewBusNames^[i] := Name + '_' + IntToStr(i); // Make up a bus name to stick in.
ReAllocMem(FBusNames, 0); // dispose of old array storage
@@ -424,10 +382,11 @@ procedure TDSSCktElement.Set_NTerms(Value: Integer);
end;
end;
- {Reallocate Terminals if Nconds or NTerms changed}
+ // Reallocate Terminals if Nconds or NTerms changed
SetLength(Terminals, Value);
- SetLength(TerminalsChecked, 0);
SetLength(TerminalsChecked, Value);
+ for i := 1 to Value do
+ TerminalsChecked[i - 1] := False;
FNterms := Value; // Set new number of terminals
Yorder := FNterms * Fnconds;
@@ -439,7 +398,6 @@ procedure TDSSCktElement.Set_NTerms(Value: Integer);
Terminals[i - 1].Init(Fnconds);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TDSSCktElement.Set_Enabled(Value: Boolean);
// If disabled, but defined, just have to processBusDefs. Adding a bus OK
// If being removed from circuit, could remove a node or bus so have to rebuild
@@ -450,7 +408,7 @@ procedure TDSSCktElement.Set_Enabled(Value: Boolean);
FEnabled := Value;
ActiveCircuit.BusNameRedefined := TRUE; // forces rebuilding of Y matrix and bus lists
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
function TDSSCktElement.GetYPrim(var Ymatrix: TCmatrix; Opt: Integer): Integer;
//returns pointer to actual YPrim
begin
@@ -464,7 +422,7 @@ function TDSSCktElement.GetYPrim(var Ymatrix: TCmatrix; Opt: Integer): Integer;
end;
Result := 0;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
function TDSSCktElement.GetYPrimValues(Opt: Integer): pComplexArray;
// Return a pointer to the Beginning the storage arrays for fast access
var
@@ -483,45 +441,43 @@ function TDSSCktElement.GetYPrimValues(Opt: Integer): pComplexArray;
Result := YPrim_Shunt.GetValuesArrayPtr(Norder);
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TDSSCktElement.GetLosses(var TotalLosses, LoadLosses,
NoLoadLosses: Complex);
begin
- {For no override, Default behavior is:
- Just return total losses and set LoadLosses=total losses and noload losses =0}
+ // For no override, Default behavior is:
+ // Just return total losses and set LoadLosses=total losses and noload losses =0
TotalLosses := Losses; // Watts, vars
LoadLosses := TotalLosses;
NoLoadLosses := CZERO;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TDSSCktElement.InjCurrents: Integer; // Applies to PC Elements
+function TDSSCktElement.InjCurrents: Integer; // Applies to PC Elements
begin
Result := 0;
- DoErrorMsg(('Improper call to InjCurrents for Element: ' + Name + '.'), '****',
+ DoErrorMsg(Format(_('Improper call to InjCurrents for Element: "%s".'), [FullName]), '****',
'Called CktElement class base function instead of actual.', 753)
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
procedure TDSSCktElement.SetNodeRef(iTerm: Integer; NodeRefArray: pIntegerArray);
// Also allocates VTemp & Itemp
var
Size, Size2: Integer;
begin
-// Allocate NodeRef and move new values into it.
+ // Allocate NodeRef and move new values into it.
Size := Yorder * SizeOf(NodeRef^[1]);
Size2 := SizeOf(NodeRef^[1]) * Fnconds; // Size for one terminal
ReallocMem(NodeRef, Size); // doesn't do anything if already properly allocated
Move(NodeRefArray^[1], NodeRef^[(iTerm - 1) * Fnconds + 1], Size2); // Zap
Move(NodeRefArray^[1], Terminals[iTerm - 1].TermNodeRef[0], Size2); // Copy in Terminal as well
-// Allocate temp array used to hold voltages and currents for calcs
+ // Allocate temp array used to hold voltages and currents for calcs
ReallocMem(Vterminal, Yorder * SizeOf(Vterminal^[1]));
ReallocMem(Iterminal, Yorder * SizeOf(Iterminal^[1]));
ReallocMem(ComplexBuffer, Yorder * SizeOf(ComplexBuffer^[1]));
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
function TDSSCktElement.Get_FirstBus: String;
begin
if FNterms > 0 then
@@ -532,7 +488,7 @@ function TDSSCktElement.Get_FirstBus: String;
else
Result := '';
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
function TDSSCktElement.Get_NextBus: String;
begin
Result := '';
@@ -545,7 +501,7 @@ function TDSSCktElement.Get_NextBus: String;
BusIndex := FNterms;
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
function TDSSCktElement.GetBus(i: Integer): String; // Get bus name by index
begin
@@ -554,24 +510,24 @@ function TDSSCktElement.GetBus(i: Integer): String; // Get bus name by index
else
Result := '';
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
procedure TDSSCktElement.SetBus(i: Integer; const s: String); // Set bus name by index
begin
if i <= FNterms then
begin
- FBusNames^[i] := lowercase(S);
+ FBusNames^[i] := AnsiLowerCase(S);
ActiveCircuit.BusNameRedefined := TRUE; // Set Global Flag to signal circuit to rebuild busdefs
end
else
- DoSimpleMsg(Format('Attempt to set bus name for non-existent circuit element terminal(%d): "%s"', [i, s]), 7541);
+ DoSimpleMsg('Attempt to set bus name for non-existent circuit element terminal(%d): "%s"', [i, s], 7541);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
procedure TDSSCktElement.Set_Freq(Value: Double);
begin
if Value > 0.0 then
FYprimFreq := Value;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
procedure TDSSCktElement.CalcYPrim;
begin
if YPrim_Series <> NIL then
@@ -586,25 +542,24 @@ procedure TDSSCktElement.CalcYPrim;
YPrimInvalid := False;
{$ENDIF}
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
procedure TDSSCktElement.ComputeIterminal;
begin
-// to save time, only recompute if a different solution than last time it was computed.
+ // to save time, only recompute if a different solution than last time it was computed.
if IterminalSolutionCount <> ActiveCircuit.Solution.SolutionCount then
begin
GetCurrents(Iterminal);
IterminalSolutionCount := ActiveCircuit.Solution.SolutionCount;
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
function TDSSCktElement.MaxTerminalOneIMag: Double;
-{ Get max of phase currents on the first terminal; Requires computing Iterminal
-}
+// Get max of phase currents on the first terminal; Requires computing Iterminal
var
i: Integer;
begin
Result := 0.0;
- if not FEnabled then
+ if (not FEnabled) or (NodeRef = NIL) then
Exit;
for i := 1 to Fnphases do
@@ -614,7 +569,6 @@ function TDSSCktElement.MaxTerminalOneIMag: Double;
Result := Sqrt(Result); // just do the sqrt once and save a little time
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TDSSCktElement.Get_Current_Mags(cMBuffer: pDoubleArray);
var
i: Integer;
@@ -623,14 +577,13 @@ procedure TDSSCktElement.Get_Current_Mags(cMBuffer: pDoubleArray);
cMBuffer^[i] := cabs(Iterminal^[i]);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function TDSSCktElement.Get_Power(idxTerm: Integer): Complex; // Get total complex power in active terminal
var
i, k, n: Integer;
begin
Result := CZERO;
ActiveTerminalIdx := idxTerm;
- if not FEnabled then
+ if (not FEnabled) or (NodeRef = NIL) then
Exit;
ComputeIterminal;
@@ -643,14 +596,14 @@ function TDSSCktElement.Get_Power(idxTerm: Integer): Complex; // Get total co
begin
n := ActiveTerminal^.TermNodeRef[i - 1]; // don't bother for grounded node
if n > 0 then
- Caccum(Result, Cmul(NodeV^[n], conjg(Iterminal[k + i])));
+ Result += NodeV^[n] * cong(Iterminal[k + i]);
end;
end;
- {If this is a positive sequence circuit, then we need to multiply by 3 to get the 3-phase power}
+ // If this is a positive sequence circuit, then we need to multiply by 3 to get the 3-phase power
if ActiveCircuit.PositiveSequence then
- Result := cMulReal(Result, 3.0);
+ Result := Result * 3.0;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
function TDSSCktElement.Get_Losses: Complex;
// get total losses in circuit element, all phases, all terminals.
// Returns complex losses (watts, vars)
@@ -659,28 +612,35 @@ function TDSSCktElement.Get_Losses: Complex;
begin
Result := CZERO;
- if not FEnabled then
+ if (not FEnabled) or (NodeRef = NIL) then
Exit;
ComputeIterminal;
// Method: Sum complex power going into all conductors of all terminals
with ActiveCircuit.Solution do
- for k := 1 to Yorder do
+ if ActiveCircuit.PositiveSequence then
begin
- n := NodeRef^[k];
- if n > 0 then
+ for k := 1 to Yorder do
begin
- if ActiveCircuit.PositiveSequence then
- Caccum(Result, CmulReal(Cmul(NodeV^[n], conjg(Iterminal^[k])), 3.0))
- else
- Caccum(Result, Cmul(NodeV^[n], conjg(Iterminal^[k])));
+ n := NodeRef^[k];
+ if n > 0 then
+ Result += NodeV^[n] * cong(Iterminal^[k]) * 3.0
+ end;
+ end
+ else
+ begin
+ for k := 1 to Yorder do
+ begin
+ n := NodeRef^[k];
+ if n > 0 then
+ Result += NodeV^[n] * cong(Iterminal^[k]);
end;
end;
end;
function TDSSCktElement.Get_MaxVoltage(idxTerm: Integer): Double;
-{Get Voltage at the specified terminal 09/17/2019}
+// Get Voltage at the specified terminal 09/17/2019
var
volts: Complex;
ClassIdx,
@@ -693,7 +653,7 @@ function TDSSCktElement.Get_MaxVoltage(idxTerm: Integer): Double;
begin
ActiveTerminalIdx := idxTerm; // set active Terminal
Result := 0.0;
- if not FEnabled then
+ if (not FEnabled) or (NodeRef = NIL) then
Exit;
ComputeIterminal;
@@ -722,14 +682,14 @@ function TDSSCktElement.Get_MaxVoltage(idxTerm: Integer): Double;
if not (ClassIdx = XFMR_ELEMENT) then // Only for transformers
volts := NodeV^[nref]
else
- volts := csub(NodeV^[nref], NodeV^[nrefN]);
+ volts := NodeV^[nref] - NodeV^[nrefN];
end;
Result := cabs(volts);
end;
function TDSSCktElement.Get_MaxPower(idxTerm: Integer): Complex;
-{Get power in the phase with the max current and return equivalent power as if it were balanced in all phases
- 2/12/2019}
+//Get power in the phase with the max current and return equivalent power as if it were balanced in all phases
+// 2/12/2019
var
volts: Complex;
ClassIdx,
@@ -742,7 +702,7 @@ function TDSSCktElement.Get_MaxPower(idxTerm: Integer): Complex;
begin
ActiveTerminalIdx := idxTerm; // set active Terminal
Result := CZERO;
- if not FEnabled then
+ if (not FEnabled) or (NodeRef = NIL) then
Exit;
ComputeIterminal;
@@ -763,31 +723,29 @@ function TDSSCktElement.Get_MaxPower(idxTerm: Integer): Complex;
end;
end;
-
ClassIdx := DSSObjType and CLASSMASK; // gets the parent class descriptor (int)
nref := ActiveTerminal^.TermNodeRef[MaxPhase - 1]; // reference to the phase voltage with the max current
nrefN := ActiveTerminal^.TermNodeRef[Fnconds - 1]; // reference to the ground terminal (GND or other phase)
with ActiveCircuit.Solution do // Get power into max phase of active terminal
begin
-
if not (ClassIdx = XFMR_ELEMENT) then // Only for transformers
volts := NodeV^[nref]
else
- volts := csub(NodeV^[nref], NodeV^[nrefN]);
- Result := Cmul(volts, conjg(Iterminal[k + MaxPhase]));
+ volts := NodeV^[nref] - NodeV^[nrefN];
+ Result := volts * cong(Iterminal[k + MaxPhase]);
end;
- // Compute equivalent total power of all phases assuming equal to max power in all phases
+ // Compute equivalent total power of all phases assuming equal to max power in all phases
with Result do
begin
re := re * Fnphases; // let compiler handle type coercion
im := im * Fnphases;
end;
- {If this is a positive sequence circuit (Fnphases=1),
- then we need to multiply by 3 to get the 3-phase power}
+ // If this is a positive sequence circuit (Fnphases=1),
+ // then we need to multiply by 3 to get the 3-phase power
if ActiveCircuit.PositiveSequence then
- Result := cMulReal(Result, 3.0);
+ Result := Result * 3.0;
end;
function TDSSCktElement.Get_MaxCurrent(idxTerm: Integer): Double;
@@ -798,7 +756,7 @@ function TDSSCktElement.Get_MaxCurrent(idxTerm: Integer): Double;
begin
ActiveTerminalIdx := idxTerm; // set active Terminal
Result := 0.0;
- if not FEnabled then
+ if (not FEnabled) or (NodeRef = NIL) then
Exit;
ComputeIterminal;
@@ -815,14 +773,14 @@ function TDSSCktElement.Get_MaxCurrent(idxTerm: Integer): Double;
end;
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
procedure TDSSCktElement.GetPhasePower(PowerBuffer: pComplexArray);
// Get the power in each phase (complex losses) of active terminal
// neutral conductors are ignored by this routine
var
i, n: Integer;
begin
- if not FEnabled then
+ if (not FEnabled) or (NodeRef = NIL) then
begin
FillByte(PowerBuffer^, Yorder * (SizeOf(Double) * 2), 0);
Exit;
@@ -837,13 +795,13 @@ procedure TDSSCktElement.GetPhasePower(PowerBuffer: pComplexArray);
if n > 0 then
begin
if ActiveCircuit.PositiveSequence then
- PowerBuffer^[i] := CmulReal(Cmul(NodeV^[n], conjg(Iterminal^[i])), 3.0)
+ PowerBuffer^[i] := NodeV^[n] * cong(Iterminal^[i]) * 3.0
else
- PowerBuffer^[i] := Cmul(NodeV^[n], conjg(Iterminal^[i]));
+ PowerBuffer^[i] := NodeV^[n] * cong(Iterminal^[i]);
end;
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
procedure TDSSCktElement.GetPhaseLosses(var Num_Phases: Integer; LossBuffer: pComplexArray);
// Get the losses in each phase (complex losses); Power difference coming out
// each phase. Note: This can be misleading if the nodev voltage is greatly unbalanced.
@@ -854,7 +812,7 @@ procedure TDSSCktElement.GetPhaseLosses(var Num_Phases: Integer; LossBuffer: pCo
begin
Num_Phases := Fnphases;
- if not FEnabled then
+ if (not FEnabled) or (NodeRef = NIL) then
begin
FillByte(LossBuffer^, Fnphases * (SizeOf(Double) * 2), 0);
Exit;
@@ -873,16 +831,16 @@ procedure TDSSCktElement.GetPhaseLosses(var Num_Phases: Integer; LossBuffer: pCo
if n > 0 then
begin
if ActiveCircuit.PositiveSequence then
- Caccum(cLoss, CmulReal(Cmul(NodeV^[n], conjg(Iterminal^[k])), 3.0))
+ cLoss += NodeV^[n] * cong(Iterminal^[k]) * 3.0
else
- Caccum(cLoss, Cmul(NodeV^[n], conjg(Iterminal^[k])));
+ cLoss += NodeV^[n] * cong(Iterminal^[k]);
end;
end;
LossBuffer^[i] := cLoss;
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TDSSCktElement.DumpProperties(F: TFileStream; Complete: Boolean);
+
+procedure TDSSCktElement.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i, j: Integer;
begin
@@ -943,9 +901,9 @@ procedure TDSSCktElement.DumpProperties(F: TFileStream; Complete: Boolean);
FSWriteln(F);
end;
end;
- end; {If complete}
+ end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
procedure TDSSCktElement.DoYprimCalcs(Ymatrix: TCMatrix);
var
i, j, k, ii, jj, ElimRow: Integer;
@@ -953,10 +911,9 @@ procedure TDSSCktElement.DoYprimCalcs(Ymatrix: TCMatrix);
RowEliminated: pIntegerArray;
ElementOpen: Boolean;
begin
- {Now Account for Open Conductors
- Perform a Kron Reduction on rows where I is forced to zero.
- Then for any conductor that is open, zero out row and column.
- }
+ // Now Account for Open Conductors
+ // Perform a Kron Reduction on rows where I is forced to zero.
+ // Then for any conductor that is open, zero out row and column.
with Ymatrix do
begin
ElementOpen := FALSE;
@@ -988,23 +945,22 @@ procedure TDSSCktElement.DoYprimCalcs(Ymatrix: TCMatrix);
begin
Yij := GetElement(ii, jj);
Ynj := GetElement(ElimRow, jj);
- SetElemSym(ii, jj, Csub(Yij, Cdiv(cmul(Yin, Ynj), Ynn)));
+ SetElemSym(ii, jj, Yij - ((Yin * Ynj) / Ynn));
end;
end;
end;
- // Now zero out row and column
+ // Now zero out row and column
ZeroRow(ElimRow);
ZeroCol(ElimRow);
- // put a small amount on the diagonal in case node gets isolated
+ // put a small amount on the diagonal in case node gets isolated
SetElement(ElimRow, ElimRow, cEpsilon);
end;
end;
k := k + Fnconds;
end;
- { Clean up at end of loop.
- Add in cEpsilon to diagonal elements of remaining rows to avoid leaving a bus hanging.
- This happens on low-impedance simple from-to elements when one terminal opened.
- }
+ // Clean up at end of loop.
+ // Add in cEpsilon to diagonal elements of remaining rows to avoid leaving a bus hanging.
+ // This happens on low-impedance simple from-to elements when one terminal opened.
if ElementOpen then
begin
for ii := 1 to Yorder do
@@ -1015,20 +971,20 @@ procedure TDSSCktElement.DoYprimCalcs(Ymatrix: TCMatrix);
end;
end;
end;
-//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+
procedure TDSSCktElement.SumCurrents;
// sum Terminal Currents into System Currents Array
// Primarily for Newton Iteration
var
i: Integer;
begin
- if not FEnabled then
+ if (not FEnabled) or (NodeRef = NIL) then
Exit;
ComputeIterminal;
with ActiveCircuit.Solution do
for i := 1 to Yorder do
- Caccum(Currents^[NodeRef^[i]], Iterminal^[i]); // Noderef=0 is OK
+ Currents^[NodeRef^[i]] += Iterminal^[i]; // Noderef=0 is OK
end;
procedure TDSSCktElement.GetTermVoltages(iTerm: Integer; VBuffer: PComplexArray);
@@ -1040,7 +996,7 @@ procedure TDSSCktElement.GetTermVoltages(iTerm: Integer; VBuffer: PComplexArray)
try
ncond := NConds;
- {return Zero if terminal number improperly specified}
+ // return Zero if terminal number improperly specified
if (iTerm < 1) or (iTerm > fNterms) then
begin
for i := 1 to Ncond do
@@ -1054,33 +1010,14 @@ procedure TDSSCktElement.GetTermVoltages(iTerm: Integer; VBuffer: PComplexArray)
except
On E: Exception do
- DoSimpleMsg('Error filling voltage buffer in GetTermVoltages for Circuit Element:' + DSSclassName + '.' + Name + CRLF +
- 'Probable Cause: Invalid definition of element.' + CRLF +
- 'System Error Message: ' + E.Message, 755);
+ DoSimpleMsg('Error filling voltage buffer in GetTermVoltages for Circuit Element: "%s". Probable Cause: Invalid definition of element. System Error Message: %s', [FullName, E.Message], 755);
end;
end;
-procedure TDSSCktElement.InitPropertyValues(ArrayOffset: Integer);
-begin
- PropertyValue[ArrayOffset + 1] := Format('%-g', [BaseFrequency]); // Base freq
- PropertyValue[ArrayOffset + 2] := 'true'; // Enabled
- FEnabledProperty := ArrayOffset + 2; // keep track of this
-
- inherited InitPropertyValues(ArrayOffset + 2);
-end;
-
-function TDSSCktElement.GetPropertyValue(Index: Integer): String;
-begin
- if Index = FEnabledProperty then
- Result := StrTOrF(Enabled)
- else
- Result := inherited GetPropertyValue(Index);
-end;
-
procedure TDSSCktElement.GetSeqLosses(var PosSeqLosses, NegSeqLosses, ZeroModeLosses: complex);
begin
-{ For the base class, just return CZERO}
-{Derived classes have to supply appropriate function}
+// For the base class, just return CZERO
+// Derived classes have to supply appropriate function
PosSeqLosses := CZERO;
NegSeqLosses := CZERO;
ZeroModeLosses := CZERO;
@@ -1105,7 +1042,7 @@ function IsGroundBus(const S: String): Boolean;
Result := FALSE;
end;
-procedure TDSSCktElement.MakePosSequence;
+procedure TDSSCktElement.MakePosSequence();
var
i: Integer;
grnd: Boolean;
@@ -1120,21 +1057,55 @@ procedure TDSSCktElement.MakePosSequence;
end;
procedure TDSSCktElement.ComputeVterminal;
-{Put terminal voltages in an array}
+// Put terminal voltages in an array
var
i: Integer;
+ vterm: PDouble;
+ nref: PInteger;
+ nv0, nv: PDouble;
begin
with ActiveCircuit.solution do
+ begin
+ vterm := PDouble(VTerminal);
+ nref := PInteger(NodeRef);
+ nv0 := PDouble(NodeV);
for i := 1 to Yorder do
- VTerminal^[i] := NodeV^[NodeRef^[i]];
+ begin
+ nv := nv0 + 2 * nref^;
+ vterm^ := nv^;
+ (vterm + 1)^ := (nv + 1)^;
+ inc(vterm, 2);
+ inc(nref);
+ // VTerminal^[i] := NodeV^[NodeRef^[i]];
+ end;
+ end;
end;
procedure TDSSCktElement.ZeroITerminal; inline;
+var
+ i: Integer;
+ it: PDouble;
begin
- FillDWord(ITerminal^, Yorder * ((SizeOf(Double) * 2) div 4), 0);
+ // Somehow this is slower?! FillDWord(ITerminal^, Yorder * ((SizeOf(Double) * 2) div 4), 0);
+ it := PDouble(Iterminal);
+ for i := 1 to Yorder do
+ begin
+ it^ := 0;
+ (it + 1)^ := 0;
+ inc(it, 2);
+ end;
+ //for i := 1 to Yorder do
+ // ITerminal[i] := CZERO;
end;
-initialization
- cEpsilon.re := EPSILON;
- cEpsilon.im := 0.0;
+procedure TDSSCktElement.MakeLike(OtherObj: Pointer);
+var
+ OtherCktObj: TDSSCktElement;
+begin
+ OtherCktObj := TDSSCktElement(OtherObj);
+ BaseFrequency := OtherCktObj.BaseFrequency;
+ Enabled := TRUE;
+end;
+
+
end.
diff --git a/src/Common/CktElementClass.pas b/src/Common/CktElementClass.pas
index 0b220c3bd..5e9990fb2 100644
--- a/src/Common/CktElementClass.pas
+++ b/src/Common/CktElementClass.pas
@@ -6,9 +6,6 @@
All rights reserved.
----------------------------------------------------------
}
-{ Created 5/17/01 RCD to balance inheritance tree for Circuit Elements}
-
-{$M+}
interface
@@ -16,109 +13,115 @@ interface
DSSClass;
type
- TCktElementClass = class(TDSSClass)
- PRIVATE
+{$SCOPEDENUMS ON}
+ TCktElementProp = (
+ INVALID = 0,
+ basefreq = 1,
+ enabled = 2
+ );
+{$SCOPEDENUMS OFF}
+ TCktElementClass = class(TDSSClass)
PROTECTED
- procedure ClassEdit(const ActiveCktElemObj: Pointer; const ParamPointer: Integer);
- procedure ClassMakeLike(const OtherObj: Pointer);
-
- procedure CountProperties; // Add no. of intrinsic properties
- procedure DefineProperties; // Add Properties of this class to propName
-
+ procedure CountPropertiesAndAllocate; override;
+ procedure DefineProperties; override;
PUBLIC
- NumCktElemClassProps: Integer;
- constructor Create(dssContext: TDSSContext);
+ constructor Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
destructor Destroy; OVERRIDE;
- PUBLISHED
-
+ function BeginEdit(ptr: Pointer; SetActive_: Boolean=True): Pointer; override;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
end;
implementation
uses
CktElement,
- ParserDel,
Utilities,
DSSGlobals,
DSSHelper,
DSSObjectHelper;
-{ TCktElementClass }
-
-procedure TCktElementClass.ClassEdit(const ActiveCktElemObj: Pointer;
- const ParamPointer: Integer);
+type
+ TObj = TDSSCktElement;
+ TProp = TCktElementProp;
+const
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
+
+procedure TCktElementClass.CountPropertiesAndAllocate;
+begin
+ NumProperties := NumProperties + NumPropsThisClass;
+ inherited CountPropertiesAndAllocate;
+end;
+constructor TCktElementClass.Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
begin
- // continue parsing with contents of Parser
- if ParamPointer > 0 then
- with TDSSCktElement(ActiveCktElemObj) do
- begin
-
- case ParamPointer of
- 1:
- BaseFrequency := Parser.Dblvalue;
- 2:
- Enabled := InterpretYesNo(Parser.StrValue);
- else
- inherited ClassEdit(ActiveCktElemObj, ParamPointer - NumCktElemClassProps)
- end;
- end;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
+ inherited Create(dssContext, DSSClsType, DSSClsName);
+ ClassParents.Add('CktElement');
end;
-procedure TCktElementClass.ClassMakeLike(const OtherObj: Pointer);
+procedure TCktElementClass.DefineProperties;
var
- OtherCktObj: TDSSCktElement;
+ obj: TObj = NIL; // NIL (0) on purpose
begin
+ PopulatePropertyNames(ActiveProperty, NumPropsThisClass, PropInfo, False, 'CktElement');
- OtherCktObj := TDSSCktElement(OtherObj);
+ // Special boolean property
+ PropertyType[ActiveProperty + ord(TProp.enabled)] := TPropertyType.EnabledProperty;
+ PropertyOffset[ActiveProperty + ord(TProp.enabled)] := 1; // dummy value
- with TDSSCktElement(ActiveDSSObject) do
- begin
- BaseFrequency := OtherCktObj.BaseFrequency;
- Enabled := TRUE;
- end;
+ // double properties (default type)
+ PropertyOffset[ActiveProperty + ord(TProp.basefreq)] := ptruint(@obj.BaseFrequency);
+ ActiveProperty := ActiveProperty + NumPropsThisClass;
+ inherited DefineProperties;
end;
-procedure TCktElementClass.CountProperties;
-
+function TCktElementClass.BeginEdit(ptr: Pointer; SetActive_: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- NumProperties := NumProperties + NumCktElemClassProps;
- inherited CountProperties;
+ // This is the default action, some classes do a bit more
+ if ptr <> NIL then
+ Obj := TObj(ptr)
+ else
+ Obj := ElementList.Active;
-end;
-
-constructor TCktElementClass.Create(dssContext: TDSSContext);
-begin
+ if (Obj <> NIL) and (Flg.EditionActive in Obj.Flags) then
+ begin
+ DosimpleMsg('%s: Object already being edited!', [Obj.FullName], 37737);
+ Exit;
+ end;
- inherited Create(dssContext);
- NumCktElemClassProps := 2;
+ if SetActive_ then
+ begin
+ //TODO: e.g. DSS.ActiveCapControlObj := Obj; -- if ever required for all elements
+ ActiveCircuit.ActiveCktElement := Obj;
+ end;
+ Include(Obj.Flags, Flg.EditionActive);
+ Result := Obj;
end;
-procedure TCktElementClass.DefineProperties;
-
-// Define the properties for the base power delivery element class
-
+function TCktElementClass.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+var
+ Obj: TObj;
begin
- PropertyName^[ActiveProperty + 1] := 'basefreq';
- PropertyName^[ActiveProperty + 2] := 'enabled';
-
- PropertyHelp^[ActiveProperty + 1] := 'Base Frequency for ratings.';
- PropertyHelp^[ActiveProperty + 2] := '{Yes|No or True|False} Indicates whether this element is enabled.';
-
- ActiveProperty := ActiveProperty + NumCktElemClassProps;
-
- inherited DefineProperties;
+ Obj := TObj(ptr);
+ Exclude(Obj.Flags, Flg.EditionActive);
+ // This is the default action, many classes do more.
+ TObj(ptr).RecalcElementData();
+ Result := True;
end;
destructor TCktElementClass.Destroy;
begin
inherited Destroy;
-
end;
-end.
+end.
\ No newline at end of file
diff --git a/src/Common/CmdForms.pas b/src/Common/CmdForms.pas
index 2dbee5366..aa6484937 100644
--- a/src/Common/CmdForms.pas
+++ b/src/Common/CmdForms.pas
@@ -29,7 +29,8 @@ interface
Progress = 3,
ProgressCaption = 4,
ProgressFormCaption = 5,
- ProgressPercent = 6
+ ProgressPercent = 6,
+ FireOffEditor = 7
);
{$SCOPEDENUMS OFF}
@@ -47,8 +48,6 @@ function DSSMessageDlg(const Msg: String; err: Boolean): Integer;
procedure DSSInfoMessageDlg(const Msg: String);
procedure CloseDownForms;
procedure ShowTreeView(const Fname: String);
-function MakeChannelSelection(NumFieldsToSkip: Integer; const Filename: String): Boolean;
-procedure ShowHeapUsage; // copied from Lazarus form; not used in command line yet
implementation
@@ -65,7 +64,8 @@ implementation
Strutils,
ArrayDef,
DSSHelper,
- DSSPointerList;
+ DSSPointerList,
+ Utilities;
const
colwidth = 25;
@@ -80,20 +80,13 @@ procedure WriteLnCB(s: String; mtype: DSSMessageType);
WriteLn(s);
end;
-
-
-procedure ShowHeapUsage;
-var
- hstat: TFPCHeapStatus;
- s: String;
-begin
- hstat := GetFPCHeapStatus;
- s := Format('Heap Memory Used: %dK', [hstat.CurrHeapUsed div 1024]);
- DSSInfoMessageDlg(s);
-end;
-
procedure InitProgressForm;
begin
+ if (@DSSPrime.DSSMessageCallback) <> NIL then
+ begin
+ DSSPrime.DSSMessageCallback(DSSPrime, PChar('0'), ord(DSSMessageType.ProgressPercent));
+ Exit;
+ end;
end;
procedure ShowPctProgress(Count: Integer);
@@ -133,17 +126,25 @@ procedure ProgressFormCaption(const S: String);
procedure ProgressHide;
begin
+ if NoFormsAllowed then
+ Exit;
+
+ if (@DSSPrime.DSSMessageCallback) <> NIL then
+ DSSPrime.DSSMessageCallback(DSSPrime, PChar('-1'), ord(DSSMessageType.ProgressPercent));
end;
procedure ShowAboutBox;
begin
WriteLnCB(
- 'OpenDSS (Electric Power Distribution System Simulator), DSS C-API library version' + CRLF +
+ 'DSS C-API library version' + CRLF +
VersionString + CRLF +
- 'Copyright (c) 2008-2019, Electric Power Research Institute, Inc.' + CRLF +
- 'Copyright (c) 2016-2017, Battelle Memorial Institute' + CRLF +
- 'Copyright (c) 2017-2021, Paulo Meira' + CRLF +
- 'All rights reserved.',
+ 'An alternative implementation of OpenDSS' + CRLF +
+ 'OpenDSS is EPRI''s Electric Power Distribution System Simulator' + CRLF +
+ 'Copyright (c) 2008-2022, Electric Power Research Institute, Inc.' + CRLF +
+ 'Copyright (c) 2016-2021, Battelle Memorial Institute' + CRLF +
+ 'Copyright (c) 2017-2022, Paulo Meira, DSS Extensions contributors' + CRLF +
+ 'All rights reserved.' + CRLF +
+ 'Please check the repository commit history and specific files for detailed credits.',
DSSMessageType.Info
);
end;
@@ -228,7 +229,7 @@ procedure AddHelpForClasses(DSSClassList: TDSSPointerList; BaseClass: Word; bPro
DSSPrime.DSSMessageCallback(DSSPrime, PChar(pDSSClass.name), ord(DSSMessageType.Help));
if bProperties = TRUE then
for j := 1 to pDSSClass.NumProperties do
- DSSPrime.DSSMessageCallback(DSSPrime, PChar(' ' + pDSSClass.PropertyName[j] + ': ' + pDSSClass.PropertyHelp^[j]), ord(DSSMessageType.Help));
+ DSSPrime.DSSMessageCallback(DSSPrime, PChar(' ' + pDSSClass.PropertyName[j] + ': ' + pDSSClass.GetPropertyHelp(j)), ord(DSSMessageType.Help));
end;
end
else
@@ -237,10 +238,10 @@ procedure AddHelpForClasses(DSSClassList: TDSSPointerList; BaseClass: Word; bPro
begin
pDSSClass := HelpList.Items[i - 1];
WriteLnCB(pDSSClass.name, DSSMessageType.Help);
-
+
if bProperties = TRUE then
for j := 1 to pDSSClass.NumProperties do
- WriteLnCB(' ' + pDSSClass.PropertyName[j] + ': ' + pDSSClass.PropertyHelp^[j], DSSMessageType.Help);
+ WriteLnCB(' ' + pDSSClass.PropertyName[j] + ': ' + pDSSClass.GetPropertyHelp(j), DSSMessageType.Help);
end;
end;
@@ -250,23 +251,23 @@ procedure AddHelpForClasses(DSSClassList: TDSSPointerList; BaseClass: Word; bPro
procedure ShowGeneralHelp;
begin
WriteLnCB(
- 'For specific help, enter:' + CRLF +
- ' "help command [cmd]" lists all executive commands, or' + CRLF +
- ' if [cmd] provided, details on that command' + CRLF +
- ' "help option [opt]" lists all simulator options, or' + CRLF +
- ' if [opt] provided, details on that option' + CRLF +
- ' "help show [opt]" lists the options to "show" various outputs, or' + CRLF +
- ' if [opt] provided, details on that output' + CRLF +
- ' "help export [fmt]" lists the options to "export" in various formats, or' + CRLF +
- ' if [fmt] provided, details on that format' + CRLF +
- ' "help class [cls]" lists the names of all available circuit model classes, or' + CRLF +
- ' if [cls] provided, details on that class' + CRLF +
- 'You may truncate any help topic name, which returns all matching entries',
+ _('For specific help, enter:') + CRLF +
+ _(' "help command [cmd]" lists all executive commands, or') + CRLF +
+ _(' if [cmd] provided, details on that command') + CRLF +
+ _(' "help option [opt]" lists all simulator options, or') + CRLF +
+ _(' if [opt] provided, details on that option') + CRLF +
+ _(' "help show [opt]" lists the options to "show" various outputs, or') + CRLF +
+ _(' if [opt] provided, details on that output') + CRLF +
+ _(' "help export [fmt]" lists the options to "export" in various formats, or') + CRLF +
+ _(' if [fmt] provided, details on that format') + CRLF +
+ _(' "help class [cls]" lists the names of all available circuit model classes, or') + CRLF +
+ _(' if [cls] provided, details on that class') + CRLF +
+ _('You may truncate any help topic name, which returns all matching entries'),
DSSMessageType.Help
);
end;
-procedure ShowAnyHelp(const num: Integer; cmd: pStringArray; hlp: pStringArray; const opt: String);
+procedure ShowAnyHelp(const num: Integer; cmd: pStringArray; const prefix: String; const opt: String);
var
i: Integer;
lst: TStringList;
@@ -297,11 +298,11 @@ procedure ShowAnyHelp(const num: Integer; cmd: pStringArray; hlp: pStringArray;
begin
for i := 1 to num do
begin
- if AnsiStartsStr(opt, LowerCase(cmd[i])) then
+ if AnsiStartsStr(opt, AnsiLowerCase(cmd[i])) then
begin
- WriteLnCB(UpperCase(cmd[i]), DSSMessageType.Help);
+ WriteLnCB(AnsiUpperCase(cmd[i]), DSSMessageType.Help);
WriteLnCB('======================', DSSMessageType.Help);
- WriteLnCB(hlp[i], DSSMessageType.Help);
+ WriteLnCB(DSSHelp(prefix + '.' + cmd[i]), DSSMessageType.Help);
WriteLnCB(msg, DSSMessageType.Help);
end;
end;
@@ -318,57 +319,55 @@ procedure ShowClassHelp(DSSClassList: TDSSPointerList; const opt: String);
pDSSClass := DSSClassList.First;
while pDSSClass <> NIL do
begin
- if AnsiStartsStr(opt, LowerCase(pDSSClass.name)) then
+ if AnsiStartsStr(opt, AnsiLowerCase(pDSSClass.name)) then
begin
- WriteLnCB(UpperCase(pDSSClass.name), DSSMessageType.Help);
+ WriteLnCB(AnsiUpperCase(pDSSClass.name), DSSMessageType.Help);
WriteLnCB('======================', DSSMessageType.Help);
for i := 1 to pDSSClass.NumProperties do
- WriteLnCB(' ' + pDSSClass.PropertyName[i] + ': ' + pDSSClass.PropertyHelp^[i], DSSMessageType.Help);
+ WriteLnCB(' ' + pDSSClass.PropertyName[i] + ': ' + pDSSClass.GetPropertyHelp(i), DSSMessageType.Help);
end;
pDSSClass := DSSClassList.Next;
end;
end
else
begin
- WriteLnCB('== Power Delivery Elements ==', DSSMessageType.Help);
+ WriteLnCB(_('== Power Delivery Elements =='), DSSMessageType.Help);
AddHelpForClasses(DSSClassList, PD_ELEMENT, FALSE);
- WriteLnCB('== Power Conversion Elements ==', DSSMessageType.Help);
+ WriteLnCB(_('== Power Conversion Elements =='), DSSMessageType.Help);
AddHelpForClasses(DSSClassList, PC_ELEMENT, FALSE);
- WriteLnCB('== Control Elements ==', DSSMessageType.Help);
+ WriteLnCB(_('== Control Elements =='), DSSMessageType.Help);
AddHelpForClasses(DSSClassList, CTRL_ELEMENT, FALSE);
- WriteLnCB('== Metering Elements ==', DSSMessageType.Help);
+ WriteLnCB(_('== Metering Elements =='), DSSMessageType.Help);
AddHelpForClasses(DSSClassList, METER_ELEMENT, FALSE);
- WriteLnCB('== Supporting Elements ==', DSSMessageType.Help);
+ WriteLnCB(_('== Supporting Elements =='), DSSMessageType.Help);
AddHelpForClasses(DSSClassList, 0, FALSE);
- WriteLnCB('== Other Elements ==', DSSMessageType.Help);
+ WriteLnCB(_('== Other Elements =='), DSSMessageType.Help);
AddHelpForClasses(DSSClassList, NON_PCPD_ELEM, FALSE);
end;
end;
-//{$DEFINE EXPORT_HELP}
-{$IFDEF EXPORT_HELP}
-function StringToMD(const s: String): String;
+function StringToHTML(const s: String): String; //TODO
begin
Result := s;
Result := StringReplace(Result, CRLF, ' ', [rfReplaceAll]);
- Result := StringReplace(Result, '|', '\|', [rfReplaceAll]);
- Result := StringReplace(Result, '*', '\*', [rfReplaceAll]);
- Result := StringReplace(Result, '~', '\~', [rfReplaceAll]);
+ // Result := StringReplace(Result, '|', '\|', [rfReplaceAll]);
+ // Result := StringReplace(Result, '*', '\*', [rfReplaceAll]);
+ // Result := StringReplace(Result, '~', '\~', [rfReplaceAll]);
end;
-procedure AddHelpForClassesMD(BaseClass: Word);
+procedure AddHelpForClassesMD(DSS: TDSSContext; BaseClass: Word);
var
HelpList: TList;
pDSSClass: TDSSClass;
i, j: Integer;
begin
HelpList := TList.Create();
- pDSSClass := DSSClassList.First;
+ pDSSClass := DSS.DSSClassList.First;
while pDSSClass <> NIL do
begin
if (pDSSClass.DSSClassType and BASECLASSMASK) = BaseClass then
HelpList.Add(pDSSClass);
- pDSSClass := DSSClassList.Next;
+ pDSSClass := DSS.DSSClassList.Next;
end;
HelpList.Sort(@CompareClassNames);
@@ -376,22 +375,26 @@ procedure AddHelpForClassesMD(BaseClass: Word);
for i := 1 to HelpList.Count do
begin
pDSSClass := HelpList.Items[i - 1];
- writeln('#### `', pDSSClass.name, '` properties');
+ writeln(Format(_('#### `%s` properties'), [pDSSClass.name]));
writeln();
- writeln('| Number | Name | Description |');
- writeln('| - | - | - |');
-
+
+ writeln('
');
+ writeln(Format('
%s
%s
%s
Migrated?
', [_('Number'), _('Name'), _('Description')]));
+
for j := 1 to pDSSClass.NumProperties do
- writeln('| ', IntToStr(j), ' | ', pDSSClass.PropertyName[j], ' | ', StringToMD(pDSSClass.PropertyHelp^[j]), ' |');
+ writeln(Format(
+ '
', [what, _('Description')]));
for i := 1 to num do
for j := 1 to num do
if cmd[j] = lst[i - 1] then
begin
- writeln('| ', cmd[j], ' | ', StringToMD(hlp[j]), ' |');
+ writeln(Format('
');
lst.Free;
writeln();
end;
-procedure ShowAllHelpMD();
+procedure ShowAllHelpMD(DSS: TDSSContext); // TODO: use plain HTML for everything instead?
//var
// pDSSClass: TDSSClass;
// i: Integer;
begin
- writeln('# DSS Extensions: OpenDSS Commands and Properties');
+ writeln(_('# DSS Extensions: OpenDSS Commands and Properties'));
writeln();
writeln('---');
writeln();
- writeln('**This document was generated from:** `', VersionString, '`');
+ writeln(Format(_('**This document was generated from:** `%s`'), [VersionString]));
writeln();
- writeln('*Generated with the legacy models disabled (i.e. OpenDSS v9+ compatibility mode).*');
+ writeln(_('*Generated with the legacy models disabled (i.e. OpenDSS v9+ compatibility mode).*'));
writeln();
writeln('---');
writeln();
- writeln('## About this');
+ writeln(_('## About this'));
writeln();
- writeln('This is a document automatically generated from the commands, options and properties for the DSS language (script level) exposed in the DSS Extensions version of the OpenDSS engine. A separate document will be developed in the future to detail **API** functions and general usage recommendations for the projects under DSS Extensions.');
+ writeln(_('This is a document automatically generated from the commands, options and properties for the DSS language (script level) exposed in the DSS Extensions version of the OpenDSS engine. A separate document will be developed in the future to detail **API** functions and general usage recommendations for the projects under DSS Extensions.'));
writeln();
- writeln('Since the extensive majority of properties and elements are compatible, this document can be useful when using either the official OpenDSS implementation or the DSS Extensions version (DSS C-API engine), consumed through the projects DSS Python (`dss_python`), OpenDSSDirect.py, OpenDSSDirect.jl, DSS Sharp (`dss_sharp`), and DSS MATLAB (`dss_matlab`). If you are using the official OpenDSS, when in doubt check the official documentation and/or source code.');
+ writeln(_('Since the extensive majority of properties and elements are compatible, this document can be useful when using either the official OpenDSS implementation or the DSS Extensions version (DSS C-API engine), consumed through the projects DSS Python (`dss_python`), OpenDSSDirect.py, OpenDSSDirect.jl, DSS Sharp (`dss_sharp`), and DSS MATLAB (`dss_matlab`). If you are using the official OpenDSS, when in doubt check the official documentation and/or source code.'));
writeln();
- writeln('As a final note, keep in mind that not all commands are implemented in the DSS Extensions engine, interactive commands like plots are missing (on purpose).');
+ writeln(_('As a final note, keep in mind that not all commands are implemented in the DSS Extensions engine, interactive commands like plots are missing (on purpose).'));
writeln();
writeln('---');
- writeln('## Commands');
+ writeln(_('## Commands'));
writeln();
- ShowAnyHelpMD(NumExecCommands, pStringArray(@ExecCommand), pStringArray(@CommandHelp), 'Command');
+
+ ShowAnyHelpMD(NumExecCommands, pStringArray(@ExecCommand), 'Command');
writeln('---');
- writeln('## Execution Options');
+ writeln(_('## Execution Options'));
writeln();
- ShowAnyHelpMD(NumExecOptions, pStringArray(@ExecOption), pStringArray(@OptionHelp), 'Option');
+ ShowAnyHelpMD(NumExecOptions, pStringArray(@ExecOption), 'Executive');
writeln('---');
- writeln('## `Show` options');
+ writeln(_('## `Show` options'));
writeln();
- ShowAnyHelpMD(NumShowOptions, pStringArray(@ShowOption), pStringArray(@ShowHelp), 'Option');
+ ShowAnyHelpMD(NumShowOptions, pStringArray(@ShowOption), 'ShowOption');
writeln('---');
- writeln('## `Export` options');
+ writeln(_('## `Export` options'));
writeln();
- ShowAnyHelpMD(NumExportOptions, pStringArray(@ExportOption), pStringArray(@ExportHelp), 'Option');
+ ShowAnyHelpMD(NumExportOptions, pStringArray(@ExportOption), 'ExportOption');
writeln('---');
- writeln('## Elements');
+ writeln(_('## Elements'));
writeln();
writeln('---');
- writeln('### Power Delivery Elements');
+ writeln(_('### Power Delivery Elements'));
writeln();
- AddHelpForClassesMD(PD_ELEMENT);
+ AddHelpForClassesMD(DSS, PD_ELEMENT);
writeln('---');
- writeln('### Power Conversion Elements');
+ writeln(_('### Power Conversion Elements'));
writeln();
- AddHelpForClassesMD(PC_ELEMENT);
+ AddHelpForClassesMD(DSS, PC_ELEMENT);
writeln('---');
- writeln('### Control Elements');
+ writeln(_('### Control Elements'));
writeln();
- AddHelpForClassesMD(CTRL_ELEMENT);
+ AddHelpForClassesMD(DSS, CTRL_ELEMENT);
writeln('---');
- writeln('### Metering Elements');
+ writeln(_('### Metering Elements'));
writeln();
- AddHelpForClassesMD(METER_ELEMENT);
+ AddHelpForClassesMD(DSS, METER_ELEMENT);
writeln('---');
- writeln('### Supporting Elements');
+ writeln(_('### Supporting Elements'));
writeln();
- AddHelpForClassesMD(0);
+ AddHelpForClassesMD(DSS, 0);
writeln('---');
- writeln('### Other Elements');
+ writeln(_('### Other Elements'));
writeln();
- AddHelpForClassesMD(NON_PCPD_ELEM);
+ AddHelpForClassesMD(DSS, NON_PCPD_ELEM);
end;
-{$ENDIF}
procedure ShowHelpForm(dssContext: TObject);
var
@@ -497,26 +501,24 @@ procedure ShowHelpForm(dssContext: TObject);
begin
DSS := TDSSContext(dssContext);
DSS.Parser.NextParam;
- Param := LowerCase(DSS.Parser.StrValue);
+ Param := AnsiLowerCase(DSS.Parser.StrValue);
DSS.Parser.NextParam;
- OptName := LowerCase(DSS.Parser.StrValue);
+ OptName := AnsiLowerCase(DSS.Parser.StrValue);
-{$IFDEF EXPORT_HELP}
if ANSIStartsStr('markdown', param) then
- ShowAllHelpMD(TODO)
+ ShowAllHelpMD(DSS)
else
-{$ENDIF}
if ANSIStartsStr('com', param) then
- ShowAnyHelp(NumExecCommands, pStringArray(@ExecCommand), pStringArray(@CommandHelp), OptName)
+ ShowAnyHelp(NumExecCommands, pStringArray(@ExecCommand), OptName, 'Command')
else
if ANSIStartsStr('op', param) then
- ShowAnyHelp(NumExecOptions, pStringArray(@ExecOption), pStringArray(@OptionHelp), OptName)
+ ShowAnyHelp(NumExecOptions, pStringArray(@ExecOption), OptName, 'Executive')
else
if ANSIStartsStr('sh', param) then
- ShowAnyHelp(NumShowOptions, pStringArray(@ShowOption), pStringArray(@ShowHelp), OptName)
+ ShowAnyHelp(NumShowOptions, pStringArray(@ShowOption), OptName, 'ShowOption')
else
if ANSIStartsStr('e', param) then
- ShowAnyHelp(NumExportOptions, pStringArray(@ExportOption), pStringArray(@ExportHelp), OptName)
+ ShowAnyHelp(NumExportOptions, pStringArray(@ExportOption), OptName, 'ExportOption')
else
if ANSIStartsStr('cl', param) then
ShowClassHelp(DSS.DSSClassList, OptName)
@@ -540,9 +542,4 @@ procedure CloseDownForms;
begin
end;
-function MakeChannelSelection(NumFieldsToSkip: Integer; const Filename: String): Boolean;
-begin
- Result := FALSE;
-end;
-
end.
diff --git a/src/Common/ControlQueue.pas b/src/Common/ControlQueue.pas
index 05382e819..1485445e7 100644
--- a/src/Common/ControlQueue.pas
+++ b/src/Common/ControlQueue.pas
@@ -7,12 +7,6 @@
----------------------------------------------------------
}
-{
- 11-1-00 added Handle and delete function
-}
-
-{$M+}
-
interface
uses
@@ -87,6 +81,7 @@ TControlQueue = class(Tobject)
implementation
uses
+ BufStream,
DSSGlobals,
sysutils,
Utilities,
@@ -116,7 +111,6 @@ function TControlQueue.Push(const Hour: Integer; const Sec: Double; const code,
begin
-
Inc(ctrlHandle); // just a serial number
{Normalize the time }
@@ -332,7 +326,6 @@ procedure TControlQueue.DeleteFromQueue(i: Integer; popped: Boolean);
Freemem(ActionList.Items[i], Sizeof(TActionRecord));
ActionList.Delete(i);
-
end;
function TControlQueue.DoActions(const Hour: Integer; const sec: Double): Boolean;
@@ -350,7 +343,6 @@ function TControlQueue.DoActions(const Hour: Integer; const sec: Double): Boolea
Result := FALSE;
if ActionList.Count > 0 then
begin
-
t.Hour := Hour;
t.Sec := Sec;
pElem := Pop(t, Code, ProxyHdl, hdl);
@@ -363,7 +355,6 @@ function TControlQueue.DoActions(const Hour: Integer; const sec: Double): Boolea
pElem := Pop(t, Code, ProxyHdl, hdl);
end;
end;
-
end;
function TControlQueue.DoMultiRate(const Hour: Integer; const sec: Double): Boolean;
@@ -487,11 +478,10 @@ procedure TControlQueue.Set_Trace(const Value: Boolean);
FreeAndNil(TraceFile);
if DebugTrace then
begin
- TraceFile := TFileStream.Create(DSS.OutputDirectory + 'Trace_ControlQueue.CSV', fmCreate);
+ TraceFile := TBufferedFileStream.Create(DSS.OutputDirectory + 'Trace_ControlQueue.csv', fmCreate);
FSWriteLn(TraceFile, '"Hour", "sec", "Control Iteration", "Element", "Action Code", "Trace Parameter", "Description"');
FSFlush(TraceFile);
end;
-
end;
procedure TControlQueue.ShowQueue(const Filenm: String);
@@ -502,7 +492,7 @@ procedure TControlQueue.ShowQueue(const Filenm: String);
begin
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Handle, Hour, Sec, ActionCode, ProxyDevRef, Device');
for i := 0 to ActionList.Count - 1 do
@@ -519,14 +509,11 @@ procedure TControlQueue.ShowQueue(const Filenm: String);
FreeAndNil(F);
FireOffEditor(DSS, FileNm);
end;
-
-
end;
procedure TControlQueue.WriteTraceRecord(const ElementName: String; const Code: Integer; TraceParameter: Double; const s: String);
begin
-
try
if (not DSS.InShowResults) then
begin
@@ -547,7 +534,6 @@ procedure TControlQueue.WriteTraceRecord(const ElementName: String; const Code:
end;
end;
-
end;
procedure TControlQueue.Delete(Hdl: Integer);
diff --git a/src/Common/DSSCallBackRoutines.pas b/src/Common/DSSCallBackRoutines.pas
index dd09efd64..12c188c00 100644
--- a/src/Common/DSSCallBackRoutines.pas
+++ b/src/Common/DSSCallBackRoutines.pas
@@ -11,10 +11,62 @@ interface
uses
ArrayDef,
- uComplex;
-
-{$INCLUDE DSSCallBackStructDef.pas}
-
+ UComplex, DSSUcomplex;
+
+TYPE
+
+ // NOTE: Maxlen argument is to better accommodate Fortran strings. VB also
+ // Caller must allocate space for pchar values
+ pDSSCallBacks = ^TDSSCallBacks; {Pointer to callback structure}
+ TDSSCallBacks = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}Packed{$ENDIF} Record
+
+ MsgCallBack: Procedure (S : pAnsiChar; Maxlen:UInt32);Stdcall; {Make use of DSS Message handling}
+
+ // Routines for using DSS Parser. This allows you to write models that accept
+ // syntax like other DSS scripts.
+ GetIntValue: Procedure(var i : Int32);Stdcall; {Get next param as an Int32}
+ GetDblValue: Procedure(var x : Double); Stdcall; {Get next param as a double}
+ GetStrValue: Procedure(s : pAnsiChar; maxlen : UInt32); Stdcall;
+ //Get next param as a string <= maxlen characters (UInt32 = 32-bit unsigned)
+ //caller must allocate space for s (Maxlen chars)
+ LoadParser: Procedure(S : pAnsiChar; maxlen : UInt32); Stdcall; // Copies a string into a special instance of the DSS parser
+ NextParam: Function (ParamName : pAnsiChar; Maxlen : UInt32):Int32; Stdcall;
+ // Advance to the next parameter and
+ // Get name of the param just retrieved, if one was given.
+ // Returns length of parameter found. If 0, then end of string.
+ // This is to handle the syntax "paramname=paramvalue" commonly used in DSS scripts
+ // Copies the string to the location specified by s up to maxlen characters.
+ // Caller must allocate space (Maxlen chars)
+
+ DoDSSCommand: Procedure(S : pAnsiChar; Maxlen : UInt32); StdCall;
+ GetActiveElementBusNames: Procedure(Name1 : pAnsiChar; Len1 : UInt32; Name2 : pAnsiChar; Len2 : UInt32); StdCall;
+ GetActiveElementVoltages: Procedure(Var NumVoltages : Int32; V : pComplexArray); StdCall;
+ GetActiveElementCurrents: Procedure(Var NumCurrents : Int32; Curr : pComplexArray); StdCall;
+ GetActiveElementLosses: Procedure(Var TotalLosses, LoadLosses, NoLoadLosses : Complex); StdCall;
+ GetActiveElementPower: Procedure(Terminal : Int32; Var TotalPower : Complex); StdCall;
+ GetActiveElementNumCust: Procedure(Var NumCust, TotalCust : Int32); StdCall;
+ GetActiveElementNodeRef: Procedure(Maxsize : Int32; NodeReferenceArray : pIntegerArray); StdCall;// calling program must allocate
+ GetActiveElementBusRef: Function(Terminal : Int32) : Int32; StdCall;
+ GetActiveElementTerminalInfo: Procedure(Var NumTerminals, NumConds, NumPhases : Int32); StdCall;
+ GetPtrToSystemVarray: Procedure(var V : Pointer; var iNumNodes : Int32); StdCall; // Returns pointer to Solution.V and size
+ GetActiveElementIndex: Function() : Int32; StdCall;
+ IsActiveElementEnabled: Function() : Boolean; StdCall;
+ IsBusCoordinateDefined: Function(BusRef : Int32) : Boolean; StdCall;
+ GetBusCoordinate: Procedure(BusRef : Int32; Var X, Y : Double); StdCall;
+ GetBuskVBase: Function(BusRef : Int32) : Double; StdCall;
+ GetBusDistFromMeter: Function(BusRef : Int32) : Double; StdCall;
+
+ GetDynamicsStruct: Procedure(var pDynamicsStruct : Pointer); StdCall; // Returns pointer to dynamics variables structure
+ GetStepSize: Function() : Double; StdCall; // Return just 'h' from dynamics record
+ GetTimeSec: Function() : Double; StdCall; // returns t in sec from top of hour
+ GetTimeHr: Function() : Double; StdCall; // returns time as a double in hours
+
+ GetPublicDataPtr: Procedure(var pPublicData : Pointer; Var PublicDataBytes : Int32); StdCall;
+ GetActiveElementName: Function(FullName : pAnsiChar; MaxNameLen : UInt32) : Int32; StdCall;
+ GetActiveElementPtr: Function() : Pointer; StdCall; // Returns pointer to active circuit element
+ ControlQueuePush: Function(Const Hour:Int32; Const Sec:Double; Const Code, ProxyHdl:Int32; Owner:Pointer):Int32; StdCall;
+ GetResultStr: Procedure(S : pAnsiChar; Maxlen : UInt32); StdCall;
+ end;
var
CallBackRoutines: TDSSCallBacks;
@@ -27,11 +79,7 @@ implementation
ParserDel,
DSSGlobals,
Executive,
-{$IFNDEF FPC}
- AnsiStrings,
-{$ELSE}
SysUtils,
-{$ENDIF}
CktElement,
Math,
PDElement,
@@ -39,7 +87,7 @@ implementation
DSSHelper;
var
- CallBackParser: TParser;
+ CallBackParser: TDSSParser;
CB_ParamName,
CB_Param: String;
@@ -359,7 +407,6 @@ procedure GetDynamicsStructCallBack(var DynamicsStruct: Pointer); STDCALL;
begin
DynamicsStruct := @DSSPrime.ActiveCircuit.Solution.DynaVars;
end;
-
end;
{====================================================================================================================}
@@ -380,7 +427,6 @@ function GetTimeSecCallBack: Double; STDCALL;
begin
Result := DSSPrime.ActiveCircuit.Solution.DynaVars.t;
end;
-
end;
{====================================================================================================================}
@@ -398,7 +444,6 @@ function GetTimeHrCallBack: Double; STDCALL;
procedure GetPublicDataPtrCallBack(var pPublicData: Pointer; var PublicDataBytes: Integer); STDCALL;
begin
-
if Assigned(DSSPrime.ActiveCircuit.ActiveCktElement) then
with DSSPrime.ActiveCircuit do
with ActiveCktElement do
@@ -406,12 +451,10 @@ procedure GetPublicDataPtrCallBack(var pPublicData: Pointer; var PublicDataBytes
pPublicData := PublicDataStruct;
PublicDataBytes := PublicDataSize;
end;
-
end;
-function GetActiveElementNameCallBack(FullName: pAnsiChar; Maxlen: Cardinal): Integer; STDCALL;
-{Maxlen is num of chars the calling program allocates for the string}
-
+function GetActiveElementNameCallBack(ElFullName: pAnsiChar; Maxlen: Cardinal): Integer; STDCALL;
+// Maxlen is num of chars the calling program allocates for the string
var
S: String;
begin
@@ -420,10 +463,9 @@ function GetActiveElementNameCallBack(FullName: pAnsiChar; Maxlen: Cardinal): In
with DSSPrime.ActiveCircuit do
with ActiveCktElement do
begin
- S := ParentClass.Name + '.' + Name;
-
- StrlCopy(FullName, pAnsiChar(Ansistring(S)), Maxlen);
- Result := Length(FullName);
+ S := FullName;
+ StrlCopy(ElFullName, pAnsiChar(Ansistring(S)), Maxlen);
+ Result := Length(ElFullName);
end;
end;
@@ -487,7 +529,7 @@ initialization
GetResultStr := GetResultStrCallBack;
end;
- CallBackParser := TParser.Create;
+ CallBackParser := TDSSParser.Create;
{====================================================================================================================}
diff --git a/src/Common/DSSCallBackStructDef.pas b/src/Common/DSSCallBackStructDef.pas
deleted file mode 100644
index b43f5e482..000000000
--- a/src/Common/DSSCallBackStructDef.pas
+++ /dev/null
@@ -1,57 +0,0 @@
-TYPE
-
- {NOTE: Maxlen argument is to better accommodate Fortran strings. VB also}
- { Caller must allocate space for pchar values }
- pDSSCallBacks = ^TDSSCallBacks; {Pointer to callback structure}
- TDSSCallBacks = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}Packed{$ENDIF} Record
-
- MsgCallBack: Procedure (S : pAnsiChar; Maxlen:Cardinal);Stdcall; {Make use of DSS Message handling}
-
- {Routines for using DSS Parser. This allows you to write models that accept
- syntax like other DSS scripts.}
- GetIntValue: Procedure(var i : Integer);Stdcall; {Get next param as an integer}
- GetDblValue: Procedure(var x : Double); Stdcall; {Get next param as a double}
- GetStrValue: Procedure(s : pAnsiChar; maxlen : Cardinal); Stdcall;
- {Get next param as a string <= maxlen characters (Cardinal = 32-bit unsigned)}
- {caller must allocate space for s (Maxlen chars)}
- LoadParser: Procedure(S : pAnsiChar; maxlen : Cardinal); Stdcall; // Copies a string into a special instance of the DSS parser
- NextParam: Function (ParamName : pAnsiChar; Maxlen : Cardinal):Integer; Stdcall;
- {Advance to the next parameter and
- Get name of the param just retrieved, if one was given.
- Returns length of parameter found. If 0, then end of string.
- This is to handle the syntax "paramname=paramvalue" commonly used in DSS scripts
- Copies the string to the location specified by s up to maxlen characters.
- Caller must allocate space (Maxlen chars)}
-
- DoDSSCommand: Procedure(S : pAnsiChar; Maxlen : Cardinal); StdCall;
- GetActiveElementBusNames: Procedure(Name1 : pAnsiChar; Len1 : Cardinal; Name2 : pAnsiChar; Len2 : Cardinal); StdCall;
- GetActiveElementVoltages: Procedure(Var NumVoltages : Integer; V : pComplexArray); StdCall;
- GetActiveElementCurrents: Procedure(Var NumCurrents : Integer; Curr : pComplexArray); StdCall;
- GetActiveElementLosses: Procedure(Var TotalLosses, LoadLosses, NoLoadLosses : Complex); StdCall;
- GetActiveElementPower: Procedure(Terminal : Integer; Var TotalPower : Complex); StdCall;
- GetActiveElementNumCust: Procedure(Var NumCust, TotalCust : Integer); StdCall;
- GetActiveElementNodeRef: Procedure(Maxsize : Integer; NodeReferenceArray : pIntegerArray); StdCall;// calling program must allocate
- GetActiveElementBusRef: Function(Terminal : Integer) : Integer; StdCall;
- GetActiveElementTerminalInfo: Procedure(Var NumTerminals, NumConds, NumPhases : Integer); StdCall;
- GetPtrToSystemVarray: Procedure(var V : Pointer; var iNumNodes : Integer); StdCall; // Returns pointer to Solution.V and size
- GetActiveElementIndex: Function() : Integer; StdCall;
- IsActiveElementEnabled: Function() : Boolean; StdCall;
- IsBusCoordinateDefined: Function(BusRef : Integer) : Boolean; StdCall;
- GetBusCoordinate: Procedure(BusRef : Integer; Var X, Y : Double); StdCall;
- GetBuskVBase: Function(BusRef : Integer) : Double; StdCall;
- GetBusDistFromMeter: Function(BusRef : Integer) : Double; StdCall;
-
- GetDynamicsStruct: Procedure(var pDynamicsStruct : Pointer); StdCall; // Returns pointer to dynamics variables structure
- GetStepSize: Function() : Double; StdCall; // Return just 'h' from dynamics record
- GetTimeSec: Function() : Double; StdCall; // returns t in sec from top of hour
- GetTimeHr: Function() : Double; StdCall; // returns time as a double in hours
-
- GetPublicDataPtr: Procedure(var pPublicData : Pointer; Var PublicDataBytes : Integer); StdCall;
- GetActiveElementName: Function(FullName : pAnsiChar; MaxNameLen : Cardinal) : Integer; StdCall;
- GetActiveElementPtr: Function() : Pointer; StdCall; // Returns pointer to active circuit element
- ControlQueuePush: Function(Const Hour:Integer; Const Sec:Double; Const Code, ProxyHdl:Integer; Owner:Pointer):Integer; StdCall;
- GetResultStr: Procedure(S : pAnsiChar; Maxlen : Cardinal); StdCall;
-
- End;
-
-
diff --git a/src/Common/DSSClass.pas b/src/Common/DSSClass.pas
index b51980d4a..2d92d424d 100644
--- a/src/Common/DSSClass.pas
+++ b/src/Common/DSSClass.pas
@@ -1,15 +1,11 @@
unit DSSClass;
{
- ----------------------------------------------------------
+ ----------------------------------------------------------
Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
All rights reserved.
----------------------------------------------------------
}
-{
- Base Class for all DSS collection classes.
- Keeps track of objects of each class, dispatches edits, etc
-}
interface
@@ -21,17 +17,210 @@ interface
DSSPointerList,
NamedObject,
ParserDel,
+{$IFDEF DSS_CAPI_PM}
SyncObjs,
- UComplex,
- CAPI_Types;
+{$ENDIF}
+ UComplex, DSSUcomplex,
+ contnrs,
+ CAPI_Types,
+ gettext;
type
{$SCOPEDENUMS ON}
+ TDSSObjectFlag = (
+ EditionActive,
+ HasBeenSaved, // originally from TDSSObject
+
+ // Originally from TDSSCktElement
+ Checked,
+ Flag, // General purpose Flag for each object don't assume inited
+ HasEnergyMeter,
+ HasSensorObj,
+ IsIsolated,
+ HasControl,
+ IsMonitored, // indicates some control is monitoring this element
+ // IsPartofFeeder, -- UNUSED
+ // Drawn, // Flag used in tree searches etc -- UNUSED
+ HasOCPDevice, // Fuse, Relay, or Recloser
+ HasAutoOCPDevice, // Relay or Recloser only
+ // HasSwtControl // Has a remotely-controlled Switch -- UNUSED
+ NeedsRecalc // Used for Edit command loops
+ );
+ TDSSObjectFlags = set of TDSSObjectFlag;
+ Flg = TDSSObjectFlag;
+
TActorStatus = (
Busy = 0,
Idle = 1
);
+ TDSSObjectProp = (
+ INVALID = 0,
+ like = 1
+ );
+
+ TPropertyFlag = (
+ CustomSet, // Implemented only for a few types -- parse and pass value to the object.
+ CustomSetRaw, // Only for some LoadShape props -- pass string instead of parsing first.
+ CustomGet,
+ IsFilename, // for strings
+ IgnoreInvalid,
+ NonPositive,
+ NonNegative,
+ NonZero,
+ Transform_Abs,
+ Transform_LowerCase,
+ ScaledByFunction, // Used only in Line and LineCode
+ WriteByFunction,
+ ReadByFunction,
+ RealPart,
+ ImagPart,
+ GreaterThanOne,
+ IntegerStructIndex, // used in LineGeometry, Transformer, AutoTrans, XfmrCode
+ OnArray, // only used for LineGeometry
+ IntervalUnits,
+ AltIndex, // only used for LineGeometry (nphases vs. nconds etc.)
+ SizeIsFunction,
+ SilentReadOnly, //TODO: SilentRO=ignore writes. We might want to change this in the future to error out instead of ignoring
+ ConditionalReadOnly, // only implemented for MappedStringEnumProperty
+ ConditionalValue, // for sym comp in LineCode
+ IntegerToDouble, // for double arrays -- read integer, convert to double
+ CheckForVar, // for object references
+ AllowNone, // for arrays
+ ArrayMaxSize, // for arrays
+ ValueOffset, // only implemented for integers
+ FullNameAsArray, // special case for LineGeometry, when reading wire as an array of strings through the Obj_* API
+ Redundant,
+ Util, // things like X and Y from XYcurve that don't have value as data
+ Unused
+ );
+
+ TPropertyFlags = set of TPropertyFlag;
+
+ TPropertyType = (
+ DoubleProperty = 0,
+ EnabledProperty,
+ MakeLikeProperty,
+ BooleanActionProperty,
+ StringEnumActionProperty,
+
+ //TODO: use Flags for the following (i.e. transform them into DoubleProperty)
+
+ // For (double,string,integer)-on-array,
+ // offset is the offset pointer to the pointer/array
+ // offset2 is the offset pointer to the element index (Integer)
+ DoubleOnArrayProperty, //1-based
+ // For (double,string,integer)-on-array,
+ // offset is the offset pointer to the pointer/array
+ // offset2 is the offset pointer to the element index (Integer)
+ // step is the size of the step used in offset2 (direct integer)
+ DoubleOnStructArrayProperty,
+
+ StringSilentROFunctionProperty, //TODO: SilentRO=ignore writes. We might want to change this in the future to error out instead of ignoring
+
+ DoubleArrayProperty,
+ DoubleDArrayProperty, // -> For dynamic arrays
+ DoubleVArrayProperty, // -> Use ParseAsVector
+ DoubleFArrayProperty, // -> For fixed-size arrays, with size in offset2
+ ComplexPartSymMatrixProperty,
+ DoubleSymMatrixProperty,
+
+ IntegerArrayProperty,
+ StringListProperty,
+
+ // A string with a name of an object
+ // offset is the offset pointer to the pointer to the object
+ // offset2 is the pointer to the TDSSClass; if NIL, assumes any CktElement
+ DSSObjectReferenceProperty,
+
+ // List of strings with names of objects
+ // offset is the offset pointer to the pointer to the object
+ // offset2 is the pointer to the TDSSClass; if NIL, assumes any CktElement
+ DSSObjectReferenceArrayProperty,
+
+ DoubleArrayOnStructArrayProperty,
+
+ IntegerProperty,
+ StringProperty,
+ ComplexProperty,
+ BooleanProperty,
+ BusProperty,
+ ComplexPartsProperty,
+
+ MappedStringEnumProperty,
+ MappedIntEnumProperty,
+ MappedStringEnumArrayProperty,
+ MappedStringEnumOnStructArrayProperty,
+ MappedStringEnumArrayOnStructArrayProperty,
+
+ // For (double,string,integer)-on-array,
+ // offset is the offset pointer to the pointer/array
+ // offset2 is the offset pointer to the element index (Integer)
+ StringOnArrayProperty, //1-based
+
+ // For (double,string,integer)-on-array,
+ // offset is the offset pointer to the pointer/array
+ // offset2 is the offset pointer to the element index (Integer)
+ // step is the size of the step used in offset2 (direct integer)
+ IntegerOnStructArrayProperty,
+ StringOnStructArrayProperty,
+
+ BusOnStructArrayProperty,
+ BusesOnStructArrayProperty // AutoTrans, Transformer
+
+ // OtherProperty
+ );
{$SCOPEDENUMS OFF}
+
+ ArrayOfDouble = Array of Double;
+ ArrayOfInteger = Array of Integer;
+ ArrayOfString = Array of String;
+ ArrayOfPointer = Array of Pointer;
+
+ PropertyTypeArray = Array[1..100] of TPropertyType;
+ pPropertyTypeArray = ^PropertyTypeArray;
+
+ TDoublePropertyFunction = function (obj: Pointer): Double;
+ TPropertyScaleFunction = function (obj: Pointer; getter: Boolean): Double;
+ TIntegerPropertyFunction = function (obj: Pointer): Integer;
+ TStringPropertyFunction = function (obj: Pointer): String;
+ TStringListPropertyFunction = function (obj: Pointer): TStringList;
+ TDoublesPropertyFunction = procedure (obj: Pointer; var ResultPtr: PDouble; ResultCount: PAPISize);
+ TObjRefsPropertyFunction = procedure (obj: Pointer; var ResultPtr: PPointer; ResultCount: PAPISize);
+
+ // TDoubleArrayPropertyFunction = function (obj: Pointer): Array of Double;
+ TWriteDoublePropertyFunction = procedure (obj: Pointer; Value: double);
+ TWriteObjRefPropertyFunction = procedure (obj: Pointer; Value: Pointer);
+ TWriteIntegerPropertyFunction = procedure (obj: Pointer; Value: Integer);
+ TWriteStringListPropertyFunction = procedure (obj: Pointer; Value: TStringList);
+ TWriteObjRefsPropertyFunction = procedure (obj: Pointer; Values: PPointer; ValueCount: Integer);
+ TWriteDoublesPropertyFunction = procedure (obj: Pointer; Values: PDouble; ValueCount: Integer);
+ TEnumActionProcedure = TWriteIntegerPropertyFunction;
+ TActionProcedure = procedure (obj: Pointer);
+
+ BooleanArray = Array[1..100] of Boolean;
+ pBooleanArray = ^BooleanArray;
+
+ TDSSEnum = class(TObject)
+ public
+ Sequential: Boolean; // are the main ordinals (without aliases) sequential/contiguous?
+ MinOrdinal: Integer;
+ MaxOrdinal: Integer;
+ MinChars, MaxChars: Integer; // minimum and maximum number of chars that are required to disambiguate strings
+ Names, LowerNames: Array of String;
+ Ordinals: Array of Integer;
+ Name: String;
+ //public
+ DefaultValue: Integer;
+ UseFirstFound, AllowLonger: Boolean;
+ Hybrid: Boolean;
+
+ constructor Create(EnumName: String; IsSequential: Boolean; MinCh, MaxCh: Integer; EnumNames: Array of String; EnumOrds: Array of Integer);
+ destructor Destroy; override;
+ function OrdinalToString(Value: Integer): String;
+ function StringToOrdinal(Value: String): Integer;
+ function IsOrdinalValid(Value: Integer): Boolean;
+ end;
+
TAction = record
ActionCode: Integer;
DeviceHandle: Integer;
@@ -44,55 +233,57 @@ TDSSContext = class;
dss_callback_plot_t = function (DSS: TDSSContext; jsonParams: PChar): Integer; CDECL;
dss_callback_message_t = function (DSS: TDSSContext; messageStr: PChar; messageType: Integer): Integer; CDECL;
- // Collection of all DSS Classes
- TDSSClasses = class(TObject)
- public
- DSS: TDSSContext;
-
- constructor Create(dssContext: TDSSContext);
- destructor Destroy; override;
- PROCEDURE New(Value:Pointer);
- End;
-
- // Base for all collection classes
+ // Base for all collection classes
TDSSClass = class(TObject)
type
THashListType = {$IFDEF DSS_CAPI_HASHLIST}TAltHashList;{$ELSE}THashList;{$ENDIF}
private
- Procedure Set_Active(value:Integer);
+ procedure Set_Active(value:Integer);
function Get_ElementCount: Integer;
function Get_First: Integer;
function Get_Next: Integer;
- Procedure ResynchElementNameList;
+ procedure ResynchElementNameList;
Protected
- Class_Name: String;
ActiveElement: Integer; // index of present ActiveElement
- CommandList: TCommandlist;
ActiveProperty: Integer;
ElementNameList: THashListType;
- Function AddObjectToList(Obj:Pointer):Integer; // Used by NewObject
+ Function AddObjectToList(Obj:Pointer; Activate: Boolean = True):Integer; // Used by NewObject
Function Get_FirstPropertyName:String;
Function Get_NextPropertyName:String;
- Function MakeLike(Const ObjName:String):Integer; Virtual;
+ Procedure CountPropertiesAndAllocate;virtual;
+ procedure DefineProperties;virtual;
- Procedure CountProperties; // Add no. of intrinsic properties
- Procedure AllocatePropertyArrays;
- Procedure DefineProperties; // Add Properties of this class to propName
- procedure ClassEdit(Const ActiveObj:Pointer; Const ParamPointer:Integer);
-
-
+ procedure PopulatePropertyNames(PropOffset: Integer; NumProps: Integer; EnumInfo: Pointer; ReplacePct: Boolean = True; PropSource: String = '');
public
DSS: TDSSContext;
-
+ ClassParents: TStringList;
+ Class_Name: String;
+ CommandList: TCommandlist;
NumProperties: Integer;
- PropertyName,
- PropertyHelp: pStringArray;
- PropertyIdxMap,
- RevPropertyIdxMap: pIntegerArray; // maps property to internal command number
+
+ // TODO: move to array of records
+ PropertyName: pStringArray;
+ PropertyRedundantWith: pIntegerArray;
+ PropertySource: pStringArray;
+ PropertyScale, PropertyValueOffset: pDoubleArray;
+ PropertyTrapZero: pDoubleArray;
+ PropertyInverse: pBooleanArray;
+ PropertyType: pPropertyTypeArray;
+ PropertyWriteFunction, PropertyReadFunction: PPointerArray;
+ PropertyOffset: pPtrIntArray; // For most simple properties
+ PropertyOffset2: pPtrIntArray; // For separate complex quantities, double-on-array, ...
+ PropertyOffset3: pPtrIntArray; // For setters in e.g. object refs
+
+ PropertyStructArrayIndexOffset, PropertyStructArrayIndexOffset2,
+ PropertyStructArrayOffset,
+ PropertyStructArrayStep,
+ PropertyStructArrayCountOffset: PtrUint;
+
+ PropertyFlags: Array of TPropertyFlags; //TODO: 0 is unused until things are migrated later
DSSClassType: Integer;
@@ -101,22 +292,28 @@ TDSSClass = class(TObject)
Saved: Boolean;
- constructor Create(dssContext: TDSSContext);
+ constructor Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
destructor Destroy; override;
-
- Procedure AddProperty(const PropName:String; CmdMapIndex:Integer; const HelpString:String);
- Procedure ReallocateElementNameList;
- Function Edit:Integer;Virtual; // uses global parser
- Function NewObject(const ObjName:String):Integer; Virtual;
+ Procedure ReallocateElementNameList;
+
+ // function CustomParse(ptr: Pointer; Idx: Integer; Param: String): Boolean; virtual;
+
+ function BeginEdit(ptr: Pointer; SetActive: Boolean=True): Pointer; virtual;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; virtual;
+ function Edit(Parser: TDSSParser): Integer;
+
+ function NewObject(const ObjName: String; Activate: Boolean = True):Pointer; Virtual; overload;
+ function NewObject(const ObjName: String; Activate: Boolean; out Idx: Integer):Pointer; overload; // for compatibility, when the index is required
Function SetActive(const ObjName:String):Boolean;
- Function GetActiveObj:Pointer; // Get address of active obj of this class
+ Function GetActiveObj:Pointer; // Get address of active obj of this class
Function Find(const ObjName:String; const ChangeActive: Boolean=True): Pointer; virtual; // Find an obj of this class by name
Function PropertyIndex(Const Prop:String):Integer;
Property FirstPropertyName:String read Get_FirstPropertyName;
Property NextPropertyName:String read Get_NextPropertyName;
+ function GetPropertyHelp(idx: Integer): String;
Property Active:Integer read ActiveElement write Set_Active;
Property ElementCount:Integer read Get_ElementCount;
@@ -127,17 +324,20 @@ TDSSClass = class(TObject)
protected
// DSSContext convenience functions
procedure DoErrorMsg(Const S, Emsg, ProbCause: String; ErrNum: Integer);inline;
- procedure DoSimpleMsg(Const S: String; ErrNum:Integer);inline;
- function InterpretDblArray(const s: String; MaxValues: Integer; ResultArray: pDoubleArray): Integer;inline;
- function InterpretIntArray(const s: String; MaxValues: Integer; ResultArray: pIntegerArray): Integer;inline;
- procedure InterpretAndAllocStrArray(const s: String; var Size: Integer; var ResultArray: pStringArray);inline;
- procedure InterpretTStringListArray(const s: String; var ResultList: TStringList);inline;
- function InterpretTimeStepSize(const s: String): Double;inline;
- function InterpretColorName(const s: String): Integer;inline;
- function InterpretComplex(const s: String): Complex;inline;
- function GetCktElementIndex(const FullObjName: String): Integer;inline;
- function AdjustInputFilePath(const param: String): String;inline;
- END;
+ procedure DoSimpleMsg(Const S: String; ErrNum:Integer);inline;overload;
+ procedure DoSimpleMsg(Const S: String; fmtArgs: Array of Const; ErrNum:Integer);inline;overload;
+ end;
+
+ TProxyClass = class(TDSSClass) // use for the property system (object references with multiple options)
+ public
+ TargetClasses: Array Of TDSSClass;
+ TargetClassNames: Array Of String;
+
+ constructor Create(dssContext: TDSSContext; Targets: Array Of String);
+ destructor Destroy; override;
+ procedure DefineProperties; override;
+ function Find(const ObjName: String; const ChangeActive: Boolean): Pointer; override;
+ end;
TDSSContext = class(TObject)
protected
@@ -147,7 +347,6 @@ TDSSContext = class(TObject)
FXYCurveClass: TDSSClass;
FGrowthShapeClass: TDSSClass;
FSpectrumClass: TDSSClass;
- FSolutionClass: TDSSClass;
FEnergyMeterClass: TDSSClass;
FMonitorClass: TDSSClass;
FSensorClass: TDSSClass;
@@ -258,11 +457,14 @@ TDSSContext = class(TObject)
FActiveCircuit: TNamedObject;
FActiveDSSObject :TNamedObject;
{$IFDEF DSS_CAPI_PM}
- FActorThread: TThread; //TODO: Currently only for solution, extend later
+ FActorThread: TThread; //TODO: Currently only for solution, extend later (send redirect command to the other thread, etc.)
{$ENDIF}
CurrentDSSDir_internal: String;
+ FSolutionAbort: LongInt; // changed to LongInt to enable InterLockedIncrement and others
+ function get_SolutionAbort(): Boolean;
+ procedure set_SolutionAbort(val: Boolean);
public
Parent: TDSSContext;
@@ -270,23 +472,20 @@ TDSSContext = class(TObject)
DSSMessageCallback: dss_callback_message_t;
// Parallel Machine state
- ADiakoptics: Boolean;
{$IFDEF DSS_CAPI_PM}
Children: array of TDSSContext;
ActiveChild: TDSSContext;
ActiveChildIndex: Integer;
CPU: Integer;
-
+
IsSolveAll: Boolean;
AllActors: Boolean;
Parallel_enabled: Boolean;
ConcatenateReports: Boolean;
+ ConcatenateReportsLock: TCriticalSection;
ActorPctProgress: Integer;
-
- ActorMsg: TEvent;
-
ActorStatus: TActorStatus;
- ActorMA_Msg: TEvent;
+ ThreadStatusEvent: TEvent;
{$ENDIF}
_Name: String;
@@ -302,8 +501,7 @@ TDSSContext = class(TObject)
GR_Counts_PByte: Array[0..1] of TAPISize;
// Original global state
- DSSClasses: TDSSClasses;
- ClassNames :TClassNamesHashListType;
+ ClassNames: TClassNamesHashListType;
DSSClassList :TDSSPointerList; // pointers to the base class types
Circuits :TDSSPointerList;
DSSObjs :TDSSPointerList;
@@ -311,9 +509,10 @@ TDSSContext = class(TObject)
NumIntrinsicClasses,
NumUserClasses: Integer;
- ActiveDSSClass :TDSSClass;
- AuxParser :TParser; // Auxiliary parser for use by anybody for reparsing values
- Parser: TParser;
+ ActiveDSSClass: TDSSClass;
+ AuxParser: TDSSParser; // Auxiliary parser for use by anybody for reparsing values
+ PropParser: TDSSParser; // Parser dedicated for parsing in SetObjPropertyValue
+ Parser: TDSSParser;
ParserVars: TParserVar;
LastClassReferenced:Integer; // index of class of last thing edited
@@ -327,7 +526,6 @@ TDSSContext = class(TObject)
ActiveEarthModel :Integer;
LastFileCompiled :String;
LastCommandWasCompile :Boolean;
- SolutionAbort :Boolean;
InShowResults :Boolean;
Redirect_Abort :Boolean;
In_Redirect :Boolean;
@@ -373,27 +571,82 @@ TDSSContext = class(TObject)
// Previously C-API or COM globals
tempBuffer: AnsiString; // CAPI_Utils.pas
- ComParser: TParser; // CAPI_Parser.pas
+ ComParser: TDSSParser; // CAPI_Parser.pas
ReduceEditString: String; // CAPI_ReduceCkt.pas
EnergyMeterName: String; // CAPI_ReduceCkt.pas
FirstPDelement: String; // Full name -- CAPI_ReduceCkt.pas
FControlProxyObj: TObject; // CAPI_CtrlQueue.pas
ActiveAction: pAction; // CAPI_CtrlQueue.pas
+ Enums: TObjectList;
+ UnitsEnum, ScanTypeEnum, SequenceEnum, ConnectionEnum, LeadLagEnum, CoreTypeEnum,
+ LineTypeEnum, EarthModelEnum, DefaultLoadModelEnum, RandomModeEnum, ControlModeEnum,
+ SolveModeEnum, SolveAlgEnum, CktModelEnum, AddTypeEnum, LoadShapeClassEnum, MonPhaseEnum: TDSSENum;
+
+ // ZIP file state
+ unzipper: TObject;
+ inZipPath: String;
+
constructor Create(_Parent: TDSSContext = nil; _IsPrime: Boolean = False);
destructor Destroy; override;
function GetPrime(): TDSSContext;
function CurrentDSSDir(): String;
- procedure SetCurrentDSSDir(dir: String);
+ procedure SetCurrentDSSDir(dir: String);
+ property SolutionAbort: Boolean READ get_SolutionAbort WRITE set_SolutionAbort;
+ function GetROFileStream(fn: String): TStream;
+ procedure NewDSSClass(Value: Pointer);
End;
-
VAR
DSSPrime: TDSSContext;
implementation
-USES DSSGlobals, SysUtils, DSSObject, CktElement, DSSHelper, DSSObjectHelper, Executive, ControlProxy, Utilities, ExportCIMXML;
+USES
+ DSSGlobals,
+ SysUtils,
+ DSSObject,
+ CktElement,
+ DSSHelper,
+ DSSObjectHelper,
+ Executive,
+ ExecHelper,
+ ControlProxy,
+ Utilities,
+ ExportCIMXML,
+ TypInfo,
+ StrUtils,
+ Math,
+ Transformer,
+ LineUnits,
+ Load,
+ uCMatrix,
+ Dynamics,
+ BufStream,
+ Solution;
+
+type
+ TProp = TDSSObjectProp;
+ PLongBool = ^LongBool;
+ PPDouble = ^PDouble;
+ PPString= ^PString;
+ PPByte = ^PByte;
+ TDSSObjectPtrPtr = ^TDSSObjectPtr;
+const
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
+
+function TDSSContext.GetROFileStream(fn: String): TStream;
+begin
+ if DSSExecutive.InZip then
+ begin
+ Result := DSSExecutive.GetZipStream(fn);
+ Exit;
+ end;
+ fn := AdjustInputFilePath(self, fn);
+ Result := TBufferedFileStream.Create(fn, fmOpenRead or fmShareDenyWrite);
+end;
function TDSSContext.GetPrime(): TDSSContext;
begin
@@ -403,26 +656,159 @@ function TDSSContext.GetPrime(): TDSSContext;
Result := Parent.GetPrime();
end;
+function TDSSContext.get_SolutionAbort(): Boolean;
+begin
+ Result := FSolutionAbort <> 0;
+end;
+
+procedure TDSSContext.set_SolutionAbort(val: Boolean);
+begin
+{$IFDEF DSS_CAPI_PM}
+ if val then
+ InterlockedExchange(FSolutionAbort, 1)
+ else
+ InterlockedExchange(FSolutionAbort, 0);
+{$ELSE}
+ if val then
+ FSolutionAbort := 1
+ else
+ FSolutionAbort := 0;
+{$ENDIF}
+end;
+
constructor TDSSContext.Create(_Parent: TDSSContext; _IsPrime: Boolean);
begin
inherited Create;
+ Parent := _Parent;
+ IsPrime := _IsPrime;
+ if IsPrime and (DSSMessages = NIL) then
+ begin
+ try
+ DSSMessages := TMOFile.Create('locale/messages.mo');
+ except
+ DSSMessages := NIL;
+ end;
+
+ try
+ DSSPropertyHelp := TMOFile.Create('locale/en_US.mo');
+ except
+ DSSPropertyHelp := NIL;
+ end;
+ end;
+
+ Enums := TObjectList.Create();
+
+ // Populate enum info
+ EarthModelEnum := TDSSEnum.Create('Earth Model', True, 1, 1,
+ ['Carson', 'FullCarson', 'Deri'], [1, 2, 3]);
+ EarthModelEnum.DefaultValue := 1;
+ Enums.Add(EarthModelEnum);
+
+ LineTypeEnum := TDSSEnum.Create('Line Type', True, 2, 4,
+ ['oh', 'ug', 'ug_ts', 'ug_cn', 'swt_ldbrk', 'swt_fuse', 'swt_sect', 'swt_rec', 'swt_disc', 'swt_brk', 'swt_elbow'],
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
+ LineTypeEnum.DefaultValue := 1;
+ Enums.Add(LineTypeEnum);
+
+ UnitsEnum := TDSSEnum.Create('Dimension Units', True, 1, 2,
+ ['none', 'mi', 'kft', 'km', 'm', 'ft', 'in', 'cm', 'mm', 'meter', 'miles'],
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 4, 1]);
+ UnitsEnum.DefaultValue := 0;
+ Enums.Add(UnitsEnum);
+
+ ScanTypeEnum := TDSSEnum.Create('Scan Type', True, 1, 1, ['None', 'Zero', 'Positive'], [-1, 0, 1]);
+ Enums.Add(ScanTypeEnum);
+
+ SequenceEnum := TDSSEnum.Create('Sequence Type', True, 1, 1, ['Negative', 'Zero', 'Positive'], [-1, 0, 1]);
+ Enums.Add(SequenceEnum);
+
+ ConnectionEnum := TDSSEnum.Create('Connection', True, 1, 2,
+ ['wye', 'delta', 'y', 'ln', 'll'],
+ [0, 1, 0, 0, 1]);
+ Enums.Add(ConnectionEnum);
+
+ CoreTypeEnum := TDSSEnum.Create('Core Type', False, 1, 1,
+ ['shell', '1-phase', '3-leg', '4-leg', '5-leg', 'core-1-phase'],
+ [0, 1, 3, 4, 5, 9]);
+ Enums.Add(CoreTypeEnum);
+
+ LeadLagEnum := TDSSEnum.Create('Phase Sequence', True, 1, 1,
+ ['Lag', 'Lead', 'ANSI', 'Euro'],
+ [0, 1, 0, 1]);
+ Enums.Add(LeadLagEnum);
+
+ DefaultLoadModelEnum := TDSSEnum.Create('Load Solution Model', True, 1, 1,
+ ['PowerFlow', 'Admittance'],
+ [POWERFLOW, ADMITTANCE]);
+ DefaultLoadModelEnum.DefaultValue := ADMITTANCE;
+ Enums.Add(DefaultLoadModelEnum);
+
+ RandomModeEnum := TDSSEnum.Create('Random Type', True, 1, 1,
+ ['None', 'Gaussian', 'Uniform', 'LogNormal'],
+ [0, GAUSSIAN, UNIFORM, LOGNORMAL]);
+ RandomModeEnum.DefaultValue := 0;
+ Enums.Add(RandomModeEnum);
+
+ ControlModeEnum := TDSSEnum.Create('Control Mode', True, 1, 1,
+ ['Off', 'Static', 'Event', 'Time', 'MultiRate'],
+ [CONTROLSOFF, CTRLSTATIC, EVENTDRIVEN, TIMEDRIVEN, MULTIRATE]);
+ ControlModeEnum.DefaultValue := CTRLSTATIC;
+ Enums.Add(ControlModeEnum);
+
+ SolveModeEnum := TDSSEnum.Create('Solution Mode', True, 1, 9,
+ ['Snap', 'Daily', 'Yearly', 'M1', 'LD1', 'PeakDay', 'DutyCycle', 'Direct', 'MF', 'FaultStudy', 'M2', 'M3', 'LD2', 'AutoAdd', 'Dynamic', 'Harmonic', 'Time', 'HarmonicT', 'Snapshot'],
+ [Ord(TSolveMode.SNAPSHOT), Ord(TSolveMode.DAILYMODE), Ord(TSolveMode.YEARLYMODE), Ord(TSolveMode.MONTECARLO1), Ord(TSolveMode.LOADDURATION1), Ord(TSolveMode.PEAKDAY), Ord(TSolveMode.DUTYCYCLE), Ord(TSolveMode.DIRECT), Ord(TSolveMode.MONTEFAULT), Ord(TSolveMode.FAULTSTUDY), Ord(TSolveMode.MONTECARLO2), Ord(TSolveMode.MONTECARLO3), Ord(TSolveMode.LOADDURATION2), Ord(TSolveMode.AUTOADDFLAG), Ord(TSolveMode.DYNAMICMODE), Ord(TSolveMode.HARMONICMODE), Ord(TSolveMode.GENERALTIME), Ord(TSolveMode.HARMONICMODET), Ord(TSolveMode.SNAPSHOT)]);
+ SolveModeEnum.DefaultValue := Ord(TSolveMode.SNAPSHOT);
+ SolveModeEnum.UseFirstFound := True; // Some example/test files use just "Harm", which is ambiguous
+ Enums.Add(SolveModeEnum);
+
+ SolveAlgEnum := TDSSEnum.Create('Solution Algorithm', True, 2, 2,
+ ['Normal', 'Newton'],
+ [NORMALSOLVE, NEWTONSOLVE]);
+ SolveAlgEnum.DefaultValue := Ord(NORMALSOLVE);
+ Enums.Add(SolveAlgEnum);
+
+ CktModelEnum := TDSSEnum.Create('Circuit Model', True, 1, 1,
+ ['Multiphase', 'Positive'],
+ [Integer(False), Integer(True)]);
+ CktModelEnum.DefaultValue := Integer(False);
+ Enums.Add(CktModelEnum);
+
+ AddTypeEnum := TDSSEnum.Create('AutoAdd Device Type', True, 1, 1,
+ ['Generator', 'Capacitor'],
+ [GENADD, CAPADD]);
+ AddTypeEnum.DefaultValue := CAPADD;
+ Enums.Add(AddTypeEnum);
+
+ LoadShapeClassEnum := TDSSEnum.Create('Load Shape Class', True, 1, 2,
+ ['None', 'Daily', 'Yearly', 'Duty'],
+ [USENONE, USEDAILY, USEYEARLY, USEDUTY]);
+ LoadShapeClassEnum.DefaultValue := USENONE;
+ Enums.Add(LoadShapeClassEnum);
+
+ MonPhaseEnum := TDSSEnum.Create('Monitored Phase', True, 1, 2,
+ ['min', 'max', 'avg'], [-3, -2, -1]);
+ MonPhaseEnum.Hybrid := True;
+ Enums.Add(MonPhaseEnum);
+
// GR (global result) counters: Initialize to zero
FillByte(GR_Counts_PPAnsiChar, sizeof(TAPISize) * 2, 0);
FillByte(GR_Counts_PDouble, sizeof(TAPISize) * 2, 0);
FillByte(GR_Counts_PInteger, sizeof(TAPISize) * 2, 0);
FillByte(GR_Counts_PByte, sizeof(TAPISize) * 2, 0);
-
- IsPrime := _IsPrime;
- Parent := _Parent;
-
+
DSSPlotCallback := nil;
DSSMessageCallback := nil;
- ADiakoptics := False;
+ ClassNames := NIL;
+ DSSClassList := NIL;
+ Circuits := NIL;
+ DSSObjs := NIL;
+
{$IFDEF DSS_CAPI_PM}
ActorStatus := TActorStatus.Idle;
- ActorMA_Msg := nil;
+ ThreadStatusEvent := nil;
ActiveChildIndex := 0;
Children := nil;
@@ -430,9 +816,10 @@ constructor TDSSContext.Create(_Parent: TDSSContext; _IsPrime: Boolean);
IsSolveAll := False;
AllActors := False;
ConcatenateReports := False;
+ ConcatenateReportsLock := TCriticalSection.Create();
Parallel_enabled := False;
ActorPctProgress := 0;
-
+
if IsPrime then
begin
SetLength(Children, 1);
@@ -442,10 +829,10 @@ constructor TDSSContext.Create(_Parent: TDSSContext; _IsPrime: Boolean);
end
else
begin
- ActiveChild := nil;
+ ActiveChild := Self;
_Name := '_';
end;
- CPU := 0; //TODO: unused for now, can be useful even on single thread later
+ CPU := -1; // left at -1 = doesn't change affinity
{$ELSE}
_Name := '';
{$ENDIF} // DSS_CAPI_PM
@@ -454,18 +841,18 @@ constructor TDSSContext.Create(_Parent: TDSSContext; _IsPrime: Boolean);
LastCmdLine := '';
RedirFile := '';
-{$IFDEF DSS_CAPI}
// Use the current working directory as the initial datapath when using DSS_CAPI
SetDataPath(self, StartupDirectory);
-{$ENDIF}
-
-
ParserVars := TParserVar.Create(100); // start with space for 100 variables
- Parser := TParser.Create;
- AuxParser := TParser.Create;
+ Parser := TDSSParser.Create;
+ PropParser := TDSSParser.Create;
+ AuxParser := TDSSParser.Create;
+
+ // Share parser variables
Parser.SetVars(ParserVars);
AuxParser.SetVars(ParserVars);
+ PropParser.SetVars(ParserVars);
SeasonalRating := False;
SeasonSignal := '';
@@ -482,7 +869,7 @@ constructor TDSSContext.Create(_Parent: TDSSContext; _IsPrime: Boolean);
LastCommandWasCompile := FALSE;
LastErrorMessage := '';
MaxAllocationIterations := 2;
- SolutionAbort := FALSE;
+ FSolutionAbort := 0;
AutoShowExport := FALSE;
SolutionWasAttempted := FALSE;
@@ -508,7 +895,7 @@ constructor TDSSContext.Create(_Parent: TDSSContext; _IsPrime: Boolean);
EnergyMeterName := '';
FirstPDelement := '';
- ComParser := ParserDel.TParser.Create; // create COM Parser object
+ ComParser := ParserDel.TDSSParser.Create; // create COM Parser object
ActiveAction := NIL;
FControlProxyObj := TControlProxyObj.Create(self);
@@ -516,17 +903,27 @@ constructor TDSSContext.Create(_Parent: TDSSContext; _IsPrime: Boolean);
DSSExecutive.CreateDefaultDSSItems;
CIMExporter := TCIMExporter.Create(self);
+
+ unzipper := NIL;
end;
destructor TDSSContext.Destroy;
+var
+ i: Integer;
begin
- // DSSExecutive.Free?
+ if unzipper <> NIL then
+ unzipper.Free;
+
+ CIMExporter.Free;
+
+ DSSExecutive.Clear(False);
+ DSSExecutive.Free;
if FControlProxyObj <> nil then
TControlProxyObj(FControlProxyObj).Free;
// No need to free ActiveAction, it only points to the action
-
+ PropParser.Free;
AuxParser.Free;
EventStrings.Free;
SavedFileList.Free;
@@ -535,6 +932,16 @@ destructor TDSSContext.Destroy;
Parser.Free;
ComParser.Free;
+ Enums.Free;
+
+ if IsPrime then
+ begin
+ FreeAndNil(DSSMessages);
+ FreeAndNil(DSSPropertyHelp);
+ end;
+{$IFDEF DSS_CAPI_PM}
+ ConcatenateReportsLock.Free();
+{$ENDIF}
inherited Destroy;
end;
@@ -566,138 +973,265 @@ procedure TDSSContext.SetCurrentDSSDir(dir: String);
CurrentDSSDir_internal := dir;
end;
-{--------------------------------------------------------------}
-{ DSSClasses Implementation
-{--------------------------------------------------------------}
-Constructor TDSSClasses.Create(dssContext: TDSSContext);
-Begin
- Inherited Create;
-
- DSS := dssContext;
-End;
-
-{--------------------------------------------------------------}
-Destructor TDSSClasses.Destroy;
-Begin
- Inherited Destroy;
-End;
-
-{--------------------------------------------------------------}
-PROCEDURE TDSSClasses.New(Value:Pointer);
-
-Begin
- DSS.DSSClassList.New := Value; // Add to pointer list
- DSS.ActiveDSSClass := Value; // Declare to be active
- DSS.ClassNames.Add(DSS.ActiveDSSClass.Name); // Add to classname list
-End;
-
-{--------------------------------------------------------------}
-{ DSSClass Implementation
-{--------------------------------------------------------------}
-Constructor TDSSClass.Create(dssContext: TDSSContext);
+procedure TDSSContext.NewDSSClass(Value:Pointer);
+begin
+ DSSClassList.Add(Value); // Add to pointer list
+ ActiveDSSClass := Value; // Declare to be active
+ ClassNames.Add(ActiveDSSClass.Name); // Add to classname list
+end;
+Constructor TDSSClass.Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
BEGIN
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
+
Inherited Create;
+
+ DSSClassType := DSSClsType;
+ ClassParents := TStringList.Create(); // for easier property help with inheritance
+ Class_Name := DSSClsName;
+ ClassParents.Add('DSSClass');
DSS := dssContext;
ElementList := TDSSPointerList.Create(20); // Init size and increment
PropertyName := nil;
- PropertyHelp := Nil;
- PropertyIdxMap := Nil;
- RevPropertyIdxMap := Nil;
+ PropertyRedundantWith := nil;
+ PropertySource := nil;
+ PropertyScale := nil;
+ PropertyValueOffset := nil;
+ PropertyTrapZero := nil;
+ PropertyInverse := nil;
+ PropertyType := nil;
+ PropertyOffset := nil;
+ PropertyOffset2 := nil;
+ PropertyOffset3 := nil;
+ PropertyWriteFunction := nil;
+ PropertyReadFunction := nil;
+ // PropertyStep := nil;
+ PropertyStructArrayIndexOffset := 0;
+ PropertyStructArrayIndexOffset2 := 0;
ActiveElement := 0;
ActiveProperty := 0;
-
ElementNameList := THashListType.Create(100);
ElementNamesOutOfSynch := FALSE;
-END;
-
-{--------------------------------------------------------------}
-Destructor TDSSClass.Destroy;
+ DefineProperties();
+end;
-VAR
- i:INTEGER;
+destructor TDSSClass.Destroy;
+var
+ i: Integer;
+ obj: TDSSObject;
+begin
+ // if ElementList <> NIL then
+ // begin
+ // for i := 1 to ElementList.Count do
+ // begin
+ // obj := ElementList.At(i);
+ // obj.Free();
+ // end;
+ // ElementList.Clear();
+ // end;
-BEGIN
// Get rid of space occupied by strings
- For i := 1 to NumProperties DO PropertyName[i] := '';
- For i := 1 to NumProperties DO PropertyHelp[i] := '';
+ for i := 1 to NumProperties do
+ begin
+ PropertyName[i] := '';
+ PropertySource[i] := '';
+ end;
+
+ Reallocmem(PropertyRedundantWith, 0);
Reallocmem(PropertyName, 0);
- Reallocmem(PropertyHelp, 0);
- Reallocmem(PropertyIdxMap, 0);
- Reallocmem(RevPropertyIdxMap, 0);
+ Reallocmem(PropertySource, 0);
+ Reallocmem(PropertyScale, 0);
+ Reallocmem(PropertyValueOffset, 0);
+ Reallocmem(PropertyType, 0);
+ Reallocmem(PropertyOffset, 0);
+ Reallocmem(PropertyOffset2, 0);
+ Reallocmem(PropertyOffset3, 0);
+ Reallocmem(PropertyReadFunction, 0);
+ Reallocmem(PropertyWriteFunction, 0);
+ Reallocmem(PropertyTrapZero, 0);
+ Reallocmem(PropertyInverse, 0);
+ SetLength(PropertyFlags, 0);
+
ElementList.Free;
ElementNameList.Free;
CommandList.Free;
+ ClassParents.Free;
Inherited Destroy;
-END;
-
+end;
-{--------------------------------------------------------------}
-Function TDSSClass.NewObject(const ObjName:String):Integer;
-BEGIN
- Result := 0;
- DoErrorMsg('Reached base class of TDSSClass for device "' + ObjName + '"',
+function TDSSClass.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+begin
+ Result := NIL;
+ DoErrorMsg(Format('Reached base class of TDSSClass for device "%s"', [ObjName]),
'N/A',
'Should be overridden.', 780);
-END;
+end;
+
+function TDSSClass.NewObject(const ObjName: String; Activate: Boolean; out Idx: Integer): Pointer;
+begin
+ Result := NewObject(ObjName, Activate);
+ Idx := ElementList.Count;
+end;
Procedure TDSSClass.Set_Active(value:Integer);
BEGIN
- If (Value > 0) and (Value<= ElementList.Count)
- THEN
- Begin
+ If (Value > 0) and (Value<= ElementList.Count) THEN
+ Begin
ActiveElement := Value;
DSS.ActiveDSSObject := ElementList.Get(ActiveElement);
- // Make sure Active Ckt Element agrees if is a ckt element
- // So COM interface will work
- if ActiveDSSObject is TDSSCktElement then
- ActiveCircuit.ActiveCktElement := TDSSCktElement(ActiveDSSObject);
- End;
+ // Make sure Active Ckt Element agrees if is a ckt element
+ // So COM interface will work
+ if DSS.ActiveDSSObject is TDSSCktElement then
+ ActiveCircuit.ActiveCktElement := TDSSCktElement(DSS.ActiveDSSObject);
+ End;
END;
-Function TDSSClass.Edit:Integer;
-BEGIN
+function TDSSClass.BeginEdit(ptr: Pointer; SetActive: Boolean): Pointer;
+type
+ TObj = TDSSObject;
+var
+ Obj: TObj;
+begin
+ Result := NIL;
+ if ptr <> NIL then
+ Obj := TObj(ptr)
+ else
+ Obj := ElementList.Active;
+
+ if (Obj <> NIL) and (Flg.EditionActive in Obj.Flags) then
+ begin
+ DosimpleMsg('%s: Object already being edited!', [Obj.FullName], 37737);
+ Exit;
+ end;
+
+ if SetActive then
+ begin
+ //TODO: e.g. DSS.ActiveConductorDataObj := Obj; -- if ever required later
+ DSS.ActiveDSSObject := Obj;
+ end;
+
+ Include(Obj.Flags, Flg.EditionActive);
+ Result := Obj;
+end;
+
+function TDSSClass.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ Exclude(TDSSObject(ptr).Flags, Flg.EditionActive);
+ Result := True;
+end;
+
+Function TDSSClass.Edit(Parser: TDSSParser): Integer;
+var
+ ParamPointer: Integer;
+ ParamName, Param: String;
+ Obj: TDSSObject;
+ prevInt: Integer;
+begin
Result := 0;
- DoSimpleMsg('virtual function TDSSClass.Edit called. Should be overriden.', 781);
-END;
-Function TDSSClass.AddObjectToList(Obj:Pointer):Integer;
-BEGIN
- ElementList.New := Obj; // Stuff it in this collection's element list
+ // Get the target object and initialize the edition
+ Obj := TDSSObject(BeginEdit(NIL, True));
+
+ //TODO: error handling? if Obj = NIL then...
+
+ // Previous Edit loop
+ ParamPointer := 0;
+ ParamName := Parser.NextParam;
+ Param := Parser.StrValue;
+ while Length(Param) > 0 do
+ begin
+ if Length(ParamName) = 0 then
+ Inc(ParamPointer)
+ else
+ ParamPointer := CommandList.GetCommand(ParamName);
+
+ if (ParamPointer <= 0) or (ParamPointer > NumProperties) then
+ begin
+ if Length(ParamName) > 0 then
+ DoSimpleMsg('Unknown parameter "%s" (value "%s") for object "%s"', [ParamName, Param, TDSSObject(Obj).FullName], 110)
+ else
+ DoSimpleMsg('Unknown parameter for value "%s" in object "%s"', [Param, TDSSObject(Obj).FullName], 110);
+
+ if DSS_CAPI_EARLY_ABORT then
+ begin
+ Result := -1;
+ Exit;
+ end;
+
+ ParamName := Parser.NextParam;
+ Param := Parser.StrValue;
+ continue;
+ end;
+
+ Inc(Result);
+
+ if not ParseObjPropertyValue(Obj, ParamPointer, Param, prevInt) then
+ begin
+ if DSS_CAPI_EARLY_ABORT then
+ begin
+ Result := -1;
+ Exit;
+ end;
+
+ ParamName := Parser.NextParam;
+ Param := Parser.StrValue;
+ continue;
+ end;
+
+ Obj.SetAsNextSeq(ParamPointer);
+ Obj.PropertySideEffects(ParamPointer, prevInt);
+
+// GetObjPropertyValue(Obj, ParamPointer, tmp);
+// WriteLn(TDSSObject(Obj).FullName, '.', PropertyName[ParamPointer], ' = ', tmp);
+
+ ParamName := Parser.NextParam;
+ Param := Parser.StrValue;
+ end;
+
+ // Finalize it
+ EndEdit(Obj, Result);
+end;
+
+function TDSSClass.AddObjectToList(Obj:Pointer; Activate: Boolean): Integer;
+begin
+ ElementList.Add(Obj); // Stuff it in this collection's element list
ElementNameList.Add(TDSSObject(Obj).Name);
{$IFNDEF DSS_CAPI_HASHLIST}
If Cardinal(ElementList.Count) > 2 * ElementNameList.InitialAllocation Then ReallocateElementNameList;
{$ENDIF}
- ActiveElement := ElementList.Count;
- Result := ActiveElement; // Return index of object in list
-END;
+ if Activate then
+ begin
+ ActiveElement := ElementList.Count;
+ Result := ActiveElement; // Return index of object in list
+ end
+ else
+ Result := ElementList.Count;
+end;
-Function TDSSClass.SetActive(const ObjName:String):Boolean;
-VAR
+Function TDSSClass.SetActive(const ObjName:String): Boolean;
+var
idx: Integer;
-
-BEGIN
+begin
Result := False;
// Faster to look in hash list 7/7/03
If ElementNamesOutOfSynch Then ResynchElementNameList;
idx := ElementNameList.Find(ObjName);
- If idx>0 Then
- Begin
+ if idx>0 then
+ begin
ActiveElement := idx;
DSS.ActiveDSSObject := ElementList.get(idx);
Result := TRUE;
End;
+end;
-END;
-
-Function TDSSClass.Find(const ObjName:String; const ChangeActive: Boolean):Pointer;
+Function TDSSClass.Find(const ObjName: String; const ChangeActive: Boolean): Pointer;
VAR
idx: Integer;
-
BEGIN
Result := Nil;
If ElementNamesOutOfSynch Then ResynchElementNameList;
@@ -729,54 +1263,79 @@ procedure TDSSContext.SetCurrentDSSDir(dir: String);
Inc(ActiveProperty);
IF ActiveProperty<=NumProperties THEN
Result := PropertyName^[ActiveProperty]
- ELSE Result := '';
+ ELSE
+ Result := '';
END;
Function TDSSClass.PropertyIndex(Const Prop:String):Integer;
// find property value by string
-
-VAR
+var
i: Integer;
-BEGIN
-
+begin
Result := 0; // Default result if not found
- For i := 1 to NumProperties DO BEGIN
- IF CompareText(Prop, PropertyName[i])=0 THEN BEGIN
- Result := PropertyIdxMap[i];
+ For i := 1 to NumProperties DO
+ BEGIN
+ IF CompareText(Prop, PropertyName[i])=0 THEN
+ BEGIN
+ Result := i;
Break;
END;
- END;
+ END;
END;
-Procedure TDSSClass.CountProperties;
-Begin
+Procedure TDSSClass.CountPropertiesAndAllocate;
+var
+ i: Integer;
+begin
NumProperties := NumProperties + 1;
+
+ PropertyName := Allocmem(SizeOf(String) * NumProperties);
+ PropertyRedundantWith := Allocmem(SizeOf(Integer) * NumProperties);
+ PropertySource := Allocmem(SizeOf(String) * NumProperties);
+ PropertyScale := Allocmem(SizeOf(Double) * NumProperties);
+ PropertyValueOffset := Allocmem(SizeOf(Double) * NumProperties);
+ PropertyTrapZero := Allocmem(SizeOf(Double) * NumProperties);
+ PropertyInverse := Allocmem(SizeOf(Boolean) * NumProperties);
+ PropertyType := Allocmem(SizeOf(TPropertyType) * NumProperties);
+ PropertyOffset := Allocmem(SizeOf(PtrInt) * NumProperties);
+ PropertyOffset2 := Allocmem(SizeOf(PtrInt) * NumProperties);
+ PropertyOffset3 := Allocmem(SizeOf(PtrInt) * NumProperties);
+ PropertyReadFunction := Allocmem(SizeOf(Pointer) * NumProperties);
+ PropertyWriteFunction := Allocmem(SizeOf(Pointer) * NumProperties);
+
+ SetLength(PropertyFlags, NumProperties + 1);
+
+ for i := 1 to NumProperties do
+ begin
+ // This defaults all properties to simple doubles,
+ // but offset still needs to be set later
+ PropertyType[i] := TPropertyType.DoubleProperty;
+ PropertyScale[i] := 1;
+ PropertyValueOffset[i] := 0;
+ PropertyTrapZero[i] := 0;
+ PropertyInverse[i] := False;
+ PropertyOffset[i] := -1;
+ PropertyOffset2[i] := 0;
+ PropertyOffset3[i] := 0;
+ PropertyFlags[i] := [];
+ // PropertyStep[i] := -1;
+ PropertyReadFunction[i] := NIL;
+ PropertyWriteFunction[i] := NIL;
+ end;
+
+ ActiveProperty := 0; // initialize for AddPropert
End;
Procedure TDSSClass.DefineProperties;
Begin
- ActiveProperty := ActiveProperty + 1;
- PropertyName^[ActiveProperty] := 'like';
- PropertyHelp^[ActiveProperty] := 'Make like another object, e.g.:' + CRLF + CRLF +
- 'New Capacitor.C2 like=c1 ...';
-End;
+ PopulatePropertyNames(ActiveProperty, NumPropsThisClass, PropInfo, False, 'DSSClass');
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TDSSClass.ClassEdit(Const ActiveObj:Pointer; Const ParamPointer:Integer);
-BEGIN
- // continue parsing with contents of Parser
- If ParamPointer > 0 Then
- WITH TDSSObject(ActiveObj) DO BEGIN
- CASE ParamPointer OF
- 1: MakeLike(Parser.StrValue); // Like command (virtual)
- END;
- End;
-End;
+ PropertyType[ActiveProperty + ord(TProp.Like)] := TPropertyType.MakeLikeProperty;
+ PropertyOffset[ActiveProperty + ord(TProp.Like)] := 1; // dummy value
-Function TDSSClass.MakeLike(Const ObjName:String):Integer;
-Begin
- Result := 0;
- DoSimpleMsg('virtual function TDSSClass.MakeLike called. Should be overriden.', 784);
+ ActiveProperty := ActiveProperty + NumPropsThisClass;
+
+ CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties), True);
End;
function TDSSClass.Get_ElementCount: Integer;
@@ -791,10 +1350,10 @@ function TDSSClass.Get_First: Integer;
ELSE Begin
ActiveElement := 1;
DSS.ActiveDSSObject := ElementList.First;
- // Make sure Active Ckt Element agrees if is a ckt element
- // So COM interface will work
- if ActiveDSSObject is TDSSCktElement then
- ActiveCircuit.ActiveCktElement := TDSSCktElement(ActiveDSSObject);
+ // Make sure Active Ckt Element agrees if is a ckt element
+ // So COM interface will work
+ if DSS.ActiveDSSObject is TDSSCktElement then
+ ActiveCircuit.ActiveCktElement := TDSSCktElement(DSS.ActiveDSSObject);
Result := ActiveElement;
End;
end;
@@ -802,48 +1361,24 @@ function TDSSClass.Get_First: Integer;
function TDSSClass.Get_Next: Integer;
begin
Inc(ActiveElement);
- IF ActiveElement > ElementList.Count
- THEN Result := 0
- ELSE Begin
+ IF ActiveElement > ElementList.Count THEN
+ Result := 0
+ ELSE
+ Begin
DSS.ActiveDSSObject := ElementList.Next;
- // Make sure Active Ckt Element agrees if is a ckt element
- // So COM interface will work
- if ActiveDSSObject is TDSSCktElement then
- ActiveCircuit.ActiveCktElement := TDSSCktElement(ActiveDSSObject);
+ // Make sure Active Ckt Element agrees if is a ckt element
+ // So COM interface will work
+ if DSS.ActiveDSSObject is TDSSCktElement then
+ ActiveCircuit.ActiveCktElement := TDSSCktElement(DSS.ActiveDSSObject);
Result := ActiveElement;
End;
-
-end;
-
-procedure TDSSClass.AddProperty(const PropName: String; CmdMapIndex: Integer; const HelpString: String);
-
-begin
- Inc(ActiveProperty);
- PropertyName[ActiveProperty] := PropName;
- PropertyHelp[ActiveProperty] := HelpString;
- PropertyIdxMap[ActiveProperty] := CmdMapIndex; // Maps to internal object property index
- RevPropertyIdxMap[CmdMapIndex] := ActiveProperty;
-end;
-
-procedure TDSSClass.AllocatePropertyArrays;
-Var
- i:Integer;
-begin
- PropertyName := Allocmem(SizeOf(PropertyName^[1]) * NumProperties);
- PropertyHelp := Allocmem(SizeOf(PropertyHelp^[1]) * NumProperties);
- PropertyIdxMap := Allocmem(SizeOf(PropertyIdxMap^[1]) * NumProperties);
- RevPropertyIdxMap := Allocmem(SizeOf(RevPropertyIdxMap^[1]) * NumProperties);
- ActiveProperty := 0; // initialize for AddPropert
- {initialize PropertyIdxMap to take care of legacy items}
- For i := 1 to NumProperties Do PropertyIDXMap^[i] := i;
- For i := 1 to NumProperties Do RevPropertyIDXMap^[i] := i;
end;
procedure TDSSClass.ReallocateElementNameList;
Var
i: Integer;
begin
- {Reallocate the device name list to improve the performance of searches}
+ // Reallocate the device name list to improve the performance of searches
ElementNameList.Free; // Throw away the old one.
ElementNameList := THashListType.Create(2*ElementList.Count); // make a new one
@@ -859,6 +1394,33 @@ procedure TDSSClass.ResynchElementNameList;
ElementNamesOutOfSynch := False;
end;
+procedure TDSSClass.PopulatePropertyNames(PropOffset: Integer; NumProps: Integer; EnumInfo: Pointer; ReplacePct: Boolean = True; PropSource: String = '');
+var
+ i: Integer;
+ propName: String;
+begin
+ if Length(PropSource) = 0 then
+ PropSource := Class_Name;
+ for i := 1 to NumProps do
+ begin
+ propName := GetEnumName(EnumInfo, i);
+ if LeftStr(propName, 2) = '__' then
+ propName := Copy(propName, 3, Length(propName));
+
+ if ReplacePct then
+ propName := ReplaceStr(propName, 'pct', '%');
+
+ propName := ReplaceStr(propName, '__', '-');
+
+ if propName = 'cls' then
+ propName := 'class'
+ else if AnsiLowerCase(propName) = 'typ' then
+ propName := propName + 'e';
+
+ PropertyName[PropOffset + i] := propName;
+ PropertySource[PropOffset + i] := PropSource;
+ end;
+end;
procedure TDSSClass.DoErrorMsg(Const S, Emsg, ProbCause: String; ErrNum: Integer);inline;
begin
@@ -870,50 +1432,284 @@ procedure TDSSClass.DoSimpleMsg(Const S: String; ErrNum:Integer);inline;
DSSGlobals.DoSimpleMsg(DSS, S, ErrNum)
end;
-function TDSSClass.InterpretDblArray(const s: String; MaxValues: Integer; ResultArray: pDoubleArray): Integer;inline;
+procedure TDSSClass.DoSimpleMsg(Const S: String; fmtArgs: Array of Const; ErrNum:Integer);inline;
begin
- Result := Utilities.InterpretDblArray(DSS, s, MaxValues, ResultArray)
+ DSSGlobals.DoSimpleMsg(DSS, DSSTranslate(S), fmtArgs, ErrNum)
end;
-function TDSSClass.InterpretIntArray(const s: String; MaxValues: Integer; ResultArray: pIntegerArray): Integer;inline;
+function TDSSClass.GetPropertyHelp(idx: Integer): String;
+var
+ altkey, key: String;
+ i: Integer;
begin
- Result := Utilities.InterpretIntArray(DSS, s, MaxValues, ResultArray)
+ if (idx <= 0) or (idx > NumProperties) then
+ begin
+ Result := 'INVALID_PROPERTY';
+ Exit;
+ end;
+
+ key := Class_Name + '.' + PropertyName[idx];
+
+ if DSSPropertyHelp = NIL then
+ begin
+ // Catalog is not loaded
+ Result := key;
+ Exit;
+ end;
+
+ Result := DSSHelp(key);
+ if Result <> key then
+ Exit; // Found a string
+
+ // Try parents
+ for i := ClassParents.Count downto 1 do
+ begin
+ altkey := ClassParents.Strings[i - 1] + '.' + PropertyName[idx];
+ Result := DSSHelp(altkey);
+ if Result <> altkey then
+ Exit; // Found a string
+ end;
+
+ // Nothing found
+ Result := key;
end;
-procedure TDSSClass.InterpretAndAllocStrArray(const s: String; var Size: Integer; var ResultArray: pStringArray);inline;
+constructor TDSSEnum.Create(EnumName: String; IsSequential: Boolean; MinCh, MaxCh: Integer; EnumNames: Array of String; EnumOrds: Array of Integer);
+var
+ i: Integer;
+ n: Integer;
begin
- Utilities.InterpretAndAllocStrArray(DSS, s, Size, ResultArray)
+ inherited Create;
+
+ n := Length(EnumNames);
+ Name := EnumName;
+ Names := NIL;
+ LowerNames := NIL;
+ Ordinals := NIL;
+
+ SetLength(Names, n);
+ SetLength(LowerNames, n);
+ for i := 0 to n - 1 do
+ begin
+ Names[i] := EnumNames[i];
+ LowerNames[i] := AnsiLowerCase(EnumNames[i]);
+ end;
+
+ if High(EnumNames) <> High(EnumOrds) then
+ raise Exception.Create(Format('Could not initialize enum ("%s").', [EnumName]));
+
+ SetLength(Ordinals, n);
+ for i := 0 to n - 1 do
+ Ordinals[i] := EnumOrds[i];
+
+ Sequential := IsSequential;
+
+ //TODO: fill these automatically
+ MinChars := MinCh;
+ MaxChars := MaxCh;
+
+ DefaultValue := -9999999;
+ AllowLonger := False;
+ UseFirstFound := False;
+ Hybrid := False;
+
+ MinOrdinal := 9999999;
+ MaxOrdinal := -9999999;
+ for i := 0 to High(Ordinals) do
+ begin
+ MinOrdinal := Min(MinOrdinal, Ordinals[i]);
+ MaxOrdinal := Max(MaxOrdinal, Ordinals[i]);
+ end;
end;
-procedure TDSSClass.InterpretTStringListArray(const s: String; var ResultList: TStringList);inline;
+destructor TDSSEnum.Destroy;
begin
- Utilities.InterpretTStringListArray(DSS, s, ResultList)
+ SetLength(Names, 0);
+ SetLength(LowerNames, 0);
+ SetLength(Ordinals, 0);
+ inherited Destroy;
end;
-function TDSSClass.InterpretTimeStepSize(const s: String): Double;inline;
+function TDSSEnum.OrdinalToString(Value: Integer): String;
+var
+ i: Integer;
begin
- Result := Utilities.InterpretTimeStepSize(DSS, s)
+ if (Value < MinOrdinal) or (Value > MaxOrdinal) then
+ begin
+ Result := ''; //TODO: error? Usually on purpose though, may need a flag
+ Exit;
+ end;
+
+ if Sequential then
+ begin
+ Result := Names[Value - MinOrdinal];
+ Exit;
+ end;
+
+ for i := 0 to High(Ordinals) do
+ if Ordinals[i] = Value then
+ begin
+ Result := Names[i];
+ Exit;
+ end;
+
+ if not Hybrid then
+ begin
+ Result := ''; //TODO: error?
+ Exit;
+ end;
+
+ Result := IntToStr(Value);
end;
-function TDSSClass.InterpretColorName(const s: String): Integer;inline;
+function TDSSEnum.IsOrdinalValid(Value: Integer): Boolean;
+var
+ i: Integer;
begin
- Result := Utilities.InterpretColorName(DSS, s)
+ if Hybrid and (Value > 0) then
+ begin
+ Result := True;
+ Exit;
+ end;
+
+ for i := 0 to High(Ordinals) do
+ if Ordinals[i] = Value then
+ begin
+ Result := True;
+ Exit;
+ end;
+ Result := False;
end;
-function TDSSClass.InterpretComplex(const s: String): Complex;inline;
+function TDSSEnum.StringToOrdinal(Value: String): Integer; // Naive version for testing
+var
+ i: Integer;
+ minch, nch: Integer;
+ found: Integer;
+ s: String;
+ errCode: Word;
begin
- Result := Utilities.InterpretComplex(DSS, s)
+ if (MinChars <> 0) and (MinChars > Length(Value)) then
+ begin
+ if Hybrid then
+ begin
+ Val(Value, Result, errCode);
+ if errCode <> 0 then
+ raise EParserProblem.Create(Format('Integer number conversion error for string: "%s"', [Value]));
+
+ Result := Max(1, Result);
+ Exit;
+ end;
+
+ Result := DefaultValue;
+ if DefaultValue = -9999999 then
+ raise Exception.Create(Format('Could not match enum ("%s") value "%s"', [Name, Value]));
+ Exit;
+ //TODO: error
+ end;
+
+ minch := Max(1, MinChars);
+ for nch := minch to Min(Length(Value), MaxChars) do
+ begin
+ found := 0;
+ s := Copy(Value, 1, nch);
+ for i := 0 to High(LowerNames) do
+ begin
+ if (not AllowLonger) and (Length(LowerNames[i]) < length(Value)) then
+ continue;
+
+ if (nch = minch) and (Value = LowerNames[i]) then
+ begin
+ Result := Ordinals[i];
+ Exit;
+ end;
+
+ if CompareTextShortest(s, LowerNames[i]) = 0 then
+ begin
+ Result := Ordinals[i];
+
+ if (nch = Length(Value)) and (UseFirstFound) then
+ Exit;
+
+ Inc(found);
+ if found > 1 then
+ break;
+ end;
+ end;
+
+ if found = 1 then
+ exit; // Found the match, can exit safely
+ end;
+
+ if Hybrid then
+ begin
+ Val(Value, Result, errCode);
+ if errCode <> 0 then
+ raise EParserProblem.Create(Format('Integer number conversion error for string: "%s"', [Value]));
+
+ Result := Max(1, Result);
+ Exit;
+ end;
+
+ // TODO: Error handling or do nothing
+ if DefaultValue = -9999999 then
+ raise Exception.Create(Format('Could not match enum ("%s") value "%s"', [Name, Value]));
+ Result := DefaultValue;
end;
-function TDSSClass.GetCktElementIndex(const FullObjName: String): Integer;inline;
+constructor TProxyClass.Create(dssContext: TDSSContext; Targets: Array Of String);
+var
+ s: String;
+ i: Integer;
begin
- Result := Utilities.GetCktElementIndex(DSS, FullObjName)
+ TargetClasses := NIL;
+ s := '(';
+
+ // To avoid missing references, copy the names here and find the classes later
+ SetLength(TargetClassNames, Length(Targets));
+ for i := 0 to High(Targets) do
+ begin
+ if i <> 0 then
+ s := s + '|';
+
+ s := s + Targets[i];
+ TargetClassNames[i] := Targets[i];
+ end;
+ s := s + ')';
+
+ inherited Create(dssContext, 0, s);
+end;
+
+function TProxyClass.Find(const ObjName: String; const ChangeActive: Boolean): Pointer;
+var
+ i: Integer;
+begin
+ Result := Nil;
+
+ if Length(TargetClasses) = 0 then
+ begin
+ SetLength(TargetClasses, Length(TargetClassNames));
+ with DSS do
+ for i := 0 to High(TargetClassNames) do
+ TargetClasses[i] := DSSClassList.Get(ClassNames.Find(TargetClassNames[i]));
+ end;
+
+ for i := 0 to High(TargetClasses) do
+ begin
+ Result := TargetClasses[i].Find(ObjName, ChangeActive);
+ if Result <> NIL then
+ Exit;
+ end;
end;
-function TDSSClass.AdjustInputFilePath(const param: String): String;
+procedure TProxyClass.DefineProperties;
begin
- Result := Utilities.AdjustInputFilePath(DSS, param)
+ // Empty
end;
+destructor TProxyClass.Destroy;
+begin
+ inherited Destroy;
+end;
-end.
+end.
\ No newline at end of file
diff --git a/src/Common/DSSClassDefs.pas b/src/Common/DSSClassDefs.pas
index 12a8dfc1f..d472ff864 100644
--- a/src/Common/DSSClassDefs.pas
+++ b/src/Common/DSSClassDefs.pas
@@ -136,258 +136,266 @@ implementation
DSSHelper;
-
-{--------------------------------------------------------------}
procedure CreateDSSClasses(DSS: TDSSContext);
begin
DSS_CAPI_LEGACY_MODELS_PREV := DSS_CAPI_LEGACY_MODELS;
- DSS.Classnames := TClassNamesHashListType.Create(25); // Makes 5 sub lists
- DSS.DSSClassList := TDSSPointerList.Create(10); // 10 is initial size and increment
- DSS.DSSClasses := TDSSClasses.Create(DSS); // class to handle junk for defining DSS classes
+ DSS.Classnames := TClassNamesHashListType.Create(40); // Makes 5 sub lists
+ DSS.DSSClassList := TDSSPointerList.Create(40); // 40 is initial size and increment
- {General DSS objects, not circuit elements}
+ // General DSS objects, not circuit elements
DSS.DSSObjs := TDSSPointerList.Create(1024);
- {instantiate all Intrinsic Object Classes}
+ // instantiate all Intrinsic Object Classes
- {Generic Object classes first in case others refer to them}
- DSS.SolutionClass := TDSSSolution.Create(DSS);
- DSS.DSSClasses.New(DSS.SolutionClass); // this is a special class
+ // Generic Object classes first in case others refer to them
DSS.LineCodeClass := TLineCode.Create(DSS);
- DSS.DSSClasses.New(DSS.LineCodeClass);
+ DSS.NewDSSClass(DSS.LineCodeClass);
DSS.LoadShapeClass := TLoadShape.Create(DSS);
- DSS.DSSClasses.New(DSS.LoadShapeClass);
+ DSS.NewDSSClass(DSS.LoadShapeClass);
DSS.TShapeClass := TTShape.Create(DSS);
- DSS.DSSClasses.New(DSS.TShapeClass);
+ DSS.NewDSSClass(DSS.TShapeClass);
DSS.PriceShapeClass := TPriceShape.Create(DSS);
- DSS.DSSClasses.New(DSS.PriceShapeClass);
+ DSS.NewDSSClass(DSS.PriceShapeClass);
DSS.XYCurveClass := TXYCurve.Create(DSS);
- DSS.DSSClasses.New(DSS.XYCurveClass);
+ DSS.NewDSSClass(DSS.XYCurveClass);
DSS.GrowthShapeClass := TGrowthShape.Create(DSS);
- DSS.DSSClasses.New(DSS.GrowthShapeClass);
+ DSS.NewDSSClass(DSS.GrowthShapeClass);
DSS.TCC_CurveClass := TTCC_Curve.Create(DSS);
- DSS.DSSClasses.New(DSS.TCC_CurveClass);
+ DSS.NewDSSClass(DSS.TCC_CurveClass);
DSS.SpectrumClass := TSpectrum.Create(DSS);
- DSS.DSSClasses.New(DSS.SpectrumClass);
+ DSS.NewDSSClass(DSS.SpectrumClass);
DSS.WireDataClass := TWireData.Create(DSS);
- DSS.DSSClasses.New(DSS.WireDataClass);
+ DSS.NewDSSClass(DSS.WireDataClass);
DSS.CNDataClass := TCNData.Create(DSS);
- DSS.DSSClasses.New(DSS.CNDataClass);
+ DSS.NewDSSClass(DSS.CNDataClass);
DSS.TSDataClass := TTSData.Create(DSS);
- DSS.DSSClasses.New(DSS.TSDataClass);
+ DSS.NewDSSClass(DSS.TSDataClass);
- DSS.LineGeometryClass := TLineGeometry.Create(DSS);
- DSS.DSSClasses.New(DSS.LineGeometryClass);
-
DSS.LineSpacingClass := TLineSpacing.Create(DSS);
- DSS.DSSClasses.New(DSS.LineSpacingClass);
+ DSS.NewDSSClass(DSS.LineSpacingClass);
+ DSS.LineGeometryClass := TLineGeometry.Create(DSS);
+ DSS.NewDSSClass(DSS.LineGeometryClass);
+
DSS.XfmrCodeClass := TXfmrCode.Create(DSS);
- DSS.DSSClasses.New(DSS.XfmrCodeClass);
+ DSS.NewDSSClass(DSS.XfmrCodeClass);
- {Circuit Element Classes}
+ // Circuit Element Classes
DSS.LineClass := TLine.Create(DSS);
- DSS.DSSClasses.New(DSS.LineClass);
+ DSS.NewDSSClass(DSS.LineClass);
DSS.VSourceClass := TVSource.Create(DSS); // 2-terminal Vsource
- DSS.DSSClasses.New(DSS.VSourceClass);
+ DSS.NewDSSClass(DSS.VSourceClass);
+
+
+
DSS.ISourceClass := TISource.Create(DSS); // 2-terminal Isource
- DSS.DSSClasses.New(DSS.ISourceClass);
+ DSS.NewDSSClass(DSS.ISourceClass);
DSS.VCSSClass := TVCCS.Create(DSS);
- DSS.DSSClasses.New(DSS.VCSSClass);
+ DSS.NewDSSClass(DSS.VCSSClass);
+
+
+
DSS.LoadClass := TLoad.Create(DSS);
- DSS.DSSClasses.New(DSS.LoadClass);
+ DSS.NewDSSClass(DSS.LoadClass);
DSS.TransformerClass := TTransf.Create(DSS);
- DSS.DSSClasses.New(DSS.TransformerClass);
+ DSS.NewDSSClass(DSS.TransformerClass);
+
+
+
DSS.RegControlClass := TRegControl.Create(DSS);
- DSS.DSSClasses.New(DSS.RegControlClass);
+ DSS.NewDSSClass(DSS.RegControlClass);
DSS.CapacitorClass := TCapacitor.Create(DSS);
- DSS.DSSClasses.New(DSS.CapacitorClass);
+ DSS.NewDSSClass(DSS.CapacitorClass);
DSS.ReactorClass := TReactor.Create(DSS);
- DSS.DSSClasses.New(DSS.ReactorClass);
+ DSS.NewDSSClass(DSS.ReactorClass);
DSS.CapControlClass := TCapControl.Create(DSS);
- DSS.DSSClasses.New(DSS.CapControlClass);
+ DSS.NewDSSClass(DSS.CapControlClass);
DSS.FaultClass := TFault.Create(DSS);
- DSS.DSSClasses.New(DSS.FaultClass);
+ DSS.NewDSSClass(DSS.FaultClass);
DSS.GeneratorClass := TGenerator.Create(DSS);
- DSS.DSSClasses.New(DSS.GeneratorClass);
+ DSS.NewDSSClass(DSS.GeneratorClass);
DSS.GenDispatcherClass := TGenDispatcher.Create(DSS);
- DSS.DSSClasses.New(DSS.GenDispatcherClass);
+ DSS.NewDSSClass(DSS.GenDispatcherClass);
if DSS_CAPI_LEGACY_MODELS then
begin
DSS.StorageClass := TStorage.Create(DSS);
DSS.Storage2Class := NIL;
- DSS.DSSClasses.New(DSS.StorageClass);
+ DSS.NewDSSClass(DSS.StorageClass);
end
else
begin
DSS.StorageClass := NIL;
DSS.Storage2Class := TStorage2.Create(DSS);
- DSS.DSSClasses.New(DSS.Storage2Class);
+ DSS.NewDSSClass(DSS.Storage2Class);
end;
if DSS_CAPI_LEGACY_MODELS then
begin
DSS.StorageControllerClass := TStorageController.Create(DSS);
DSS.StorageController2Class := NIL;
- DSS.DSSClasses.New(DSS.StorageControllerClass);
+ DSS.NewDSSClass(DSS.StorageControllerClass);
end
else
begin
DSS.StorageControllerClass := NIL;
DSS.StorageController2Class := TStorageController2.Create(DSS);
- DSS.DSSClasses.New(DSS.StorageController2Class);
+ DSS.NewDSSClass(DSS.StorageController2Class);
end;
DSS.RelayClass := TRelay.Create(DSS);
- DSS.DSSClasses.New(DSS.RelayClass);
+ DSS.NewDSSClass(DSS.RelayClass);
DSS.RecloserClass := TRecloser.Create(DSS);
- DSS.DSSClasses.New(DSS.RecloserClass);
+ DSS.NewDSSClass(DSS.RecloserClass);
DSS.FuseClass := TFuse.Create(DSS);
- DSS.DSSClasses.New(DSS.FuseClass);
+ DSS.NewDSSClass(DSS.FuseClass);
DSS.SwtControlClass := TSwtControl.Create(DSS);
- DSS.DSSClasses.New(DSS.SwtControlClass);
+ DSS.NewDSSClass(DSS.SwtControlClass);
if DSS_CAPI_LEGACY_MODELS then
begin
DSS.PVSystemClass := TPVSystem.Create(DSS);
DSS.PVSystem2Class := NIL;
- DSS.DSSClasses.New(DSS.PVSystemClass);
+ DSS.NewDSSClass(DSS.PVSystemClass);
end
else
begin
DSS.PVSystemClass := NIL;
DSS.PVSystem2Class := TPVSystem2.Create(DSS);
- DSS.DSSClasses.New(DSS.PVSystem2Class);
+ DSS.NewDSSClass(DSS.PVSystem2Class);
end;
DSS.UPFCClass := TUPFC.Create(DSS);
- DSS.DSSClasses.New(DSS.UPFCClass);
+ DSS.NewDSSClass(DSS.UPFCClass);
DSS.UPFCControlClass := TUPFCControl.Create(DSS);
- DSS.DSSClasses.New(DSS.UPFCControlClass);
+ DSS.NewDSSClass(DSS.UPFCControlClass);
DSS.ESPVLControlClass := TESPVLControl.Create(DSS);
- DSS.DSSClasses.New(DSS.ESPVLControlClass);
+ DSS.NewDSSClass(DSS.ESPVLControlClass);
DSS.IndMach012Class := TIndMach012.Create(DSS);
- DSS.DSSClasses.New(DSS.IndMach012Class);
+ DSS.NewDSSClass(DSS.IndMach012Class);
DSS.GICsourceClass := TGICsource.Create(DSS); // GIC source
- DSS.DSSClasses.New(DSS.GICsourceClass);
+ DSS.NewDSSClass(DSS.GICsourceClass);
DSS.AutoTransClass := TAutoTrans.Create(DSS); // Auto Transformer
- DSS.DSSClasses.New(DSS.AutoTransClass);
+ DSS.NewDSSClass(DSS.AutoTransClass);
if DSS_CAPI_LEGACY_MODELS then
begin
DSS.InvControlClass := TInvControl.Create(DSS);
DSS.InvControl2Class := NIL;
- DSS.DSSClasses.New(DSS.InvControlClass);
+ DSS.NewDSSClass(DSS.InvControlClass);
end
else
begin
DSS.InvControlClass := NIL;
DSS.InvControl2Class := TInvControl2.Create(DSS);
- DSS.DSSClasses.New(DSS.InvControl2Class);
+ DSS.NewDSSClass(DSS.InvControl2Class);
end;
DSS.ExpControlClass := TExpControl.Create(DSS);
- DSS.DSSClasses.New(DSS.ExpControlClass);
+ DSS.NewDSSClass(DSS.ExpControlClass);
DSS.GICLineClass := TGICLine.Create(DSS);
- DSS.DSSClasses.New(DSS.GICLineClass);
+ DSS.NewDSSClass(DSS.GICLineClass);
DSS.GICTransformerClass := TGICTransformer.Create(DSS);
- DSS.DSSClasses.New(DSS.GICTransformerClass);
+ DSS.NewDSSClass(DSS.GICTransformerClass);
DSS.VSConverterClass := TVSConverter.Create(DSS);
- DSS.DSSClasses.New(DSS.VSConverterClass);
+ DSS.NewDSSClass(DSS.VSConverterClass);
DSS.MonitorClass := TDSSMonitor.Create(DSS); // Have to do this AFTER Generator
- DSS.DSSClasses.New(DSS.MonitorClass);
+ DSS.NewDSSClass(DSS.MonitorClass);
DSS.EnergyMeterClass := TEnergyMeter.Create(DSS); // Have to do this AFTER Generator
- DSS.DSSClasses.New(DSS.EnergyMeterClass);
+ DSS.NewDSSClass(DSS.EnergyMeterClass);
DSS.SensorClass := TSensor.Create(DSS); // Create state estimation sensors
- DSS.DSSClasses.New(DSS.SensorClass);
+ DSS.NewDSSClass(DSS.SensorClass);
DSS.NumIntrinsicClasses := DSS.DSSClassList.Count;
end;
-//----------------------------------------------------------------------------
procedure DisposeDSSClasses(DSS: TDSSContext);
-
var
i: Integer;
DSSObj: TDSSObject;
TraceName: String;
SuccessFree: String;
-
begin
try
- SuccessFree := 'First Object';
- for i := 1 to DSS.DSSObjs.Count do
+ if DSS.DSSObjs <> NIL then
begin
- DSSObj := DSS.DSSObjs.Get(i);
- TraceName := DSSObj.ParentClass.Name + '.' + DSSObj.Name;
- DSSObj.Free;
- SuccessFree := TraceName;
+ SuccessFree := 'First Object';
+ for i := 1 to DSS.DSSObjs.Count do
+ begin
+ DSSObj := DSS.DSSObjs.At(i);
+ TraceName := DSSObj.FullName;
+ DSSObj.Free;
+ SuccessFree := TraceName;
+ end;
+ TraceName := '(DSSObjs Class)';
+ FreeAndNil(DSS.DSSObjs);
end;
- TraceName := '(DSSObjs Class)';
- FreeAndNil(DSS.DSSObjs);
except
On E: Exception do
- DoSimpleMsg(DSS, 'Exception disposing of DSS Obj "' + TraceName + '". ' + CRLF +
- 'Last Successful dispose was for object "' + SuccessFree + '" ' + CRLF +
- E.Message, 901);
+ DoSimpleMsg(DSS, 'Exception disposing of DSS Obj "%s". Last Successful dispose was for object "%s". %s',
+ [TraceName, SuccessFree, CRLF + E.Message],
+ 901);
end;
try
- for i := 1 to DSS.DSSClassList.Count do
- TDSSClass(DSS.DSSClassList.Get(i)).Free;
- TraceName := '(DSS Class List)';
- FreeAndNil(DSS.DSSClassList);
- TraceName := '(DSS Classes)';
- FreeAndNil(DSS.DSSClasses);
- TraceName := '(ClassNames)';
- FreeAndNil(DSS.ClassNames);
+ if DSS.DSSClassList <> NIL then
+ begin
+ for i := 1 to DSS.DSSClassList.Count do
+ begin
+ TDSSClass(DSS.DSSClassList.Get(i)).Free;
+ end;
+ TraceName := '(DSS Class List)';
+ FreeAndNil(DSS.DSSClassList);
+ end;
+ if DSS.ClassNames <> NIL then
+ begin
+ TraceName := '(ClassNames)';
+ FreeAndNil(DSS.ClassNames);
+ end;
except
On E: Exception do
- DoSimpleMsg(DSS, 'Exception disposing of DSS Class"' + TraceName + '". ' + CRLF + E.Message, 902);
+ DoSimpleMsg(DSS, Format(_('Exception disposing of DSS Class "%s".'), [TraceName]) + CRLF + E.Message, 902);
end;
-
end;
-//----------------------------------------------------------------------------
function SetObjectClass(DSS: TDSSContext; const ObjType: String): Boolean;
// set LastClassReferenced variable by class name
@@ -396,13 +404,12 @@ function SetObjectClass(DSS: TDSSContext; const ObjType: String): Boolean;
Classref: Integer;
begin
-
Classref := DSS.ClassNames.Find(ObjType);
case Classref of
0:
begin
- DoSimpleMsg(DSS, 'Error! Object Class "' + ObjType + '" not found.' + CRLF + DSS.Parser.CmdString, 903);
+ DoSimpleMsg(DSS, Format(_('Error! Object Class "%s" not found.'), [ObjType]) + CRLF + DSS.Parser.CmdString, 903);
Result := FALSE;
Exit;
end;{Error}
@@ -411,13 +418,11 @@ function SetObjectClass(DSS: TDSSContext; const ObjType: String): Boolean;
end;
Result := TRUE;
-
end;
-//----------------------------------------------------------------------------
function GetDSSClassPtr(DSS: TDSSContext; const ClassName: String): TDSSClass;
begin
- Result := TDSSClass(DSS.DSSClassList.Get(DSS.ClassNames.Find(lowercase(ClassName))));
+ Result := TDSSClass(DSS.DSSClassList.Get(DSS.ClassNames.Find(AnsiLowerCase(ClassName))));
end;
diff --git a/src/Common/DSSGlobals.pas b/src/Common/DSSGlobals.pas
index 6c640af7b..1e6ff8130 100644
--- a/src/Common/DSSGlobals.pas
+++ b/src/Common/DSSGlobals.pas
@@ -6,16 +6,17 @@
----------------------------------------------------------
}
-{$WARN UNIT_PLATFORM OFF}
-
interface
Uses Classes, DSSClassDefs, DSSObject, DSSClass, ParserDel, Hashlist, DSSPointerList,
- UComplex, Arraydef, CktElement, Circuit,
+ UComplex, DSSUcomplex, Arraydef, CktElement, Circuit,
+
+ {$IFDEF UNIX}BaseUnix, {$ENDIF}
- {$IFDEF UNIX}BaseUnix,{$ENDIF}
+ gettext,
+ CpuCount,
- {Some units which have global vars defined here}
+ // Some units which have global vars defined here
Spectrum,
LoadShape,
TempShape,
@@ -75,6 +76,9 @@ interface
CRLF = sLineBreak;
IsDLL = True;
+ // TODO: CALPHA has exceptionally bad precision here... change for v0.13
+ CALPHA: Complex = (re:-0.5; im: -0.866025); // -120 degrees phase shift
+
// TODO: toggle for v0.13
// SQRT2 = 1.4142135623730950488;
// SQRT3 = 1.7320508075688772935;
@@ -138,10 +142,12 @@ interface
PROFILE120KFT = 9992; // not mutually exclusive to the other choices 9999..9994
ProgramName = 'dss-extensions';
- MaxCircuits = 2; //TODO: remove limit?
+ MaxCircuits = 2; //TODO: remove limit? or completely remove the concept of a separate circuit, i.e., make it so a DSSContext always contains one circuit
VAR
+ DSSMessages: TMOFile = NIL;
+ DSSPropertyHelp: TMOFile = NIL;
DSS_CAPI_INFO_SPARSE_COND: Boolean;
DSS_CAPI_EARLY_ABORT: Boolean;
DSS_CAPI_ITERATE_DISABLED: Integer = 0; // default to 0 for compatibility
@@ -158,8 +164,8 @@ interface
DSS_CAPI_LOADS_TERMINAL_CHECK: Boolean = True; //TODO: one per context?
DSS_CAPI_LEGACY_MODELS: Boolean = False; //TODO: one per context?
NoFormsAllowed: Boolean = True; //TODO: one per context?
- QueryLogFile: TFileStream = nil; //TODO: one per context?
- CALPHA: Complex; {120-degree shift constant}
+ DSS_CAPI_ALLOW_DOSCMD: Boolean = False; //TODO: one per context?
+
SQRT2: Double;
SQRT3: Double;
InvSQRT3: Double;
@@ -173,9 +179,13 @@ interface
function VersionString: String;
procedure DoErrorMsg(DSS: TDSSContext; Const S, Emsg, ProbCause :String; ErrNum:Integer);
-procedure DoSimpleMsg(DSS: TDSSContext; Const S :String; ErrNum:Integer);
+procedure DoSimpleMsg(DSS: TDSSContext; Const S :String; ErrNum:Integer);overload;
+procedure DoSimpleMsg(DSS: TDSSContext; Const S :String; fmtArgs: Array of Const; ErrNum:Integer);overload;
-procedure ClearAllCircuits(DSS: TDSSContext);
+procedure ClearAllCircuits_SingleContext(DSS: TDSSContext);
+{$IFDEF DSS_CAPI_PM}
+procedure ClearAllCircuits_AllContexts(DSS: TDSSContext);
+{$ENDIF}
procedure SetObject(DSS: TDSSContext; const param :string);
function SetActiveBus(DSS: TDSSContext; const BusName:String):Integer;
@@ -195,17 +205,18 @@ procedure ResetQueryLogFile(DSS: TDSSContext);
procedure WriteQueryLogFile(DSS: TDSSContext; Const Prop, S:String);
{$IFDEF DSS_CAPI_PM}
-procedure Wait4Actors(DSS: TDSSContext; ActorOffset: Integer);
-procedure DoClone(DSS: TDSSContext);
-procedure New_Actor_Slot(DSS: TDSSContext);
-procedure New_Actor(DSS: TDSSContext);
+procedure Wait4Actors(MainDSS: TDSSContext; ActorOffset: Integer);
+procedure DoClone(MainDSS: TDSSContext);
+procedure New_Actor_Slot(MainDSS: TDSSContext);
{$ENDIF}
-implementation
-
+function DSSTranslate(const s: String): String;
+function DSSHelp(const s: String): String;
+implementation
-USES {Forms, Controls,}
+USES
+ BufStream,
{$IFDEF MSWINDOWS}
Windows,
// SHFolder,
@@ -223,11 +234,6 @@ implementation
ExecOptions,
DSSHelper;
-TYPE
-
- THandle = NativeUint;
-
-{$IFDEF FPC}
FUNCTION GetDefaultDataDirectory: String;
Begin
{$IFDEF UNIX}
@@ -247,56 +253,28 @@ implementation
Result := SysUtils.GetEnvironmentVariable('LOCALAPPDATA');
{$ENDIF}
End;
-{$ELSE}
-FUNCTION GetDefaultDataDirectory: String;
-Var
- ThePath:Array[0..MAX_PATH] of char;
-Begin
- FillChar(ThePath, SizeOF(ThePath), #0);
- SHGetFolderPath (0, CSIDL_PERSONAL, 0, 0, ThePath);
- Result := ThePath;
-End;
-FUNCTION GetDefaultScratchDirectory: String;
-Var
- ThePath:Array[0..MAX_PATH] of char;
-Begin
- FillChar(ThePath, SizeOF(ThePath), #0);
- SHGetFolderPath (0, CSIDL_LOCAL_APPDATA, 0, 0, ThePath);
- Result := ThePath;
-End;
-{$ENDIF}
-
-//----------------------------------------------------------------------------
PROCEDURE DoErrorMsg(DSS: TDSSContext; Const S, Emsg, ProbCause:String; ErrNum:Integer);
VAR
Msg:String;
Retval:Integer;
-Begin
-
- Msg := Format('Error %d Reported From OpenDSS Intrinsic Function: ', [Errnum])+ CRLF + S
- + CRLF + CRLF + 'Error Description: ' + CRLF + Emsg
- + CRLF + CRLF + 'Probable Cause: ' + CRLF+ ProbCause;
-
- If Not NoFormsAllowed Then Begin
-
- If DSS.In_Redirect Then
- Begin
- RetVal := DSSMessageDlg(Msg, FALSE);
- If RetVal = -1 Then DSS.Redirect_Abort := True;
- End
- Else
- DSSMessageDlg(Msg, TRUE);
+begin
+ Msg := Format(_('Error %d Reported From OpenDSS Intrinsic Function: '), [Errnum])+ CRLF + S
+ + CRLF + CRLF + _('Error Description: ') + CRLF + Emsg
+ + CRLF + CRLF + _('Probable Cause: ') + CRLF+ ProbCause;
- End
- Else
- Begin
- {$IFDEF DSS_CAPI}
- if DSS_CAPI_EARLY_ABORT then
- DSS.Redirect_Abort := True;
- {$ENDIF}
- End;
+ if not NoFormsAllowed then
+ begin
+ if DSS.In_Redirect then
+ begin
+ RetVal := DSSMessageDlg(Msg, FALSE);
+ end
+ else
+ DSSMessageDlg(Msg, TRUE);
+ end;
+ if DSS_CAPI_EARLY_ABORT then
+ DSS.Redirect_Abort := True;
DSS.LastErrorMessage := Msg;
DSS.ErrorNumber := ErrNum;
@@ -304,52 +282,42 @@ implementation
DSS.SolutionAbort := True;
End;
-//----------------------------------------------------------------------------
-PROCEDURE AppendGlobalResultCRLF(DSS: TDSSContext; const S:String);
-
-Begin
- If Length(DSS.GlobalResult) > 0
- THEN DSS.GlobalResult := DSS.GlobalResult + CRLF + S
- ELSE DSS.GlobalResult := S;
+PROCEDURE AppendGlobalResultCRLF(DSS: TDSSContext; const S: String);
+begin
+ if Length(DSS.GlobalResult) > 0 then
+ DSS.GlobalResult := DSS.GlobalResult + CRLF + S
+ ELSE
+ DSS.GlobalResult := S;
DSS.ErrorStrings.Add(Format('(%d) %s' ,[DSS.ErrorNumber, S])); // Add to Error log
-End;
+end;
-//----------------------------------------------------------------------------
PROCEDURE DoSimpleMsg(DSS: TDSSContext; Const S:String; ErrNum:Integer);
-
-VAR
+var
Retval:Integer;
Begin
- IF Not NoFormsAllowed Then Begin
- IF DSS.In_Redirect THEN
- Begin
+ if not NoFormsAllowed then
+ begin
+ if DSS.In_Redirect then
+ begin
RetVal := DSSMessageDlg(Format('(%d) OpenDSS %s%s', [Errnum, CRLF, S]), FALSE);
- {$IFDEF DSS_CAPI}
- if DSS_CAPI_EARLY_ABORT then
- DSS.Redirect_Abort := True;
- {$ENDIF}
- IF RetVal = -1 THEN
- DSS.Redirect_Abort := True;
- End
- ELSE
+ end
+ else
DSSInfoMessageDlg(Format('(%d) OpenDSS %s%s', [Errnum, CRLF, S]));
- End
- Else
- Begin
- {$IFDEF DSS_CAPI}
- if DSS_CAPI_EARLY_ABORT then
- DSS.Redirect_Abort := True;
- {$ENDIF}
- End;
+ end;
+ if DSS_CAPI_EARLY_ABORT then
+ DSS.Redirect_Abort := True;
DSS.LastErrorMessage := S;
DSS.ErrorNumber := ErrNum;
AppendGlobalResultCRLF(DSS, S);
End;
+procedure DoSimpleMsg(DSS: TDSSContext; Const S: String; fmtArgs: Array of Const; ErrNum:Integer);
+begin
+ DoSimpleMsg(DSS, Format(_(S), fmtArgs), ErrNum)
+end;
-//----------------------------------------------------------------------------
PROCEDURE SetObject(DSS: TDSSContext; const param :string);
{Set object active by name}
@@ -377,7 +345,7 @@ implementation
Begin
IF Not DSS.ActiveDSSClass.SetActive(Objname) THEN
Begin // scroll through list of objects untill a match
- DoSimpleMsg(DSS, 'Error! Object "' + ObjName + '" not found.'+ CRLF + DSS.Parser.CmdString, 904);
+ DoSimpleMsg(DSS, Format(_('Error! Object "%s" not found.'), [ObjName]) + CRLF + DSS.Parser.CmdString, 904);
End
ELSE
With DSS.ActiveCircuit Do
@@ -392,14 +360,11 @@ implementation
End;
End
ELSE
- DoSimpleMsg(DSS, 'Error! Active object type/class is not set.', 905);
-
-End;
+ DoSimpleMsg(DSS, _('Error! Active object type/class is not set.'), 905);
+end;
-//----------------------------------------------------------------------------
FUNCTION SetActiveBus(DSS: TDSSContext; const BusName:String):Integer;
-Begin
-
+begin
// Now find the bus and set active
Result := 0;
@@ -410,14 +375,12 @@ implementation
IF ActiveBusIndex=0 Then
Begin
Result := 1;
- AppendGlobalResult(DSS, 'SetActiveBus: Bus ' + BusName + ' Not Found.');
+ AppendGlobalResult(DSS, Format(_('SetActiveBus: Bus "%s" notfound'), [BusName]));
End;
End;
+end;
-End;
-
-{$IFNDEF DSS_CAPI_PM}
-procedure ClearAllCircuits(DSS: TDSSContext);
+procedure ClearAllCircuits_SingleContext(DSS: TDSSContext);
Begin
DSS.ActiveCircuit := DSS.Circuits.First;
while DSS.ActiveCircuit <> nil do
@@ -434,8 +397,8 @@ procedure ClearAllCircuits(DSS: TDSSContext);
DSS.LogQueries := FALSE;
DSS.MaxAllocationIterations := 2;
End;
-{$ELSE}
-procedure ClearAllCircuits(DSS: TDSSContext);
+{$IFDEF DSS_CAPI_PM}
+procedure ClearAllCircuits_AllContexts(DSS: TDSSContext);
var
i : integer;
PMParent: TDSSContext;
@@ -445,29 +408,29 @@ procedure ClearAllCircuits(DSS: TDSSContext);
for i := 0 to High(PMParent.Children) do
with PMParent.Children[i] do
begin
+ // In case the actor hasn't been destroyed
+ if ActorThread <> nil then
+ begin
+ SolutionAbort := True;
+ ActorThread.Send_Message(TActorMessage.EXIT_ACTOR);
+ ActorThread.WaitFor();
+ ActorThread.Free;
+ ActorThread := nil;
+ end;
+
ActiveCircuit := Circuits.First;
while ActiveCircuit <> nil do
begin
ActiveCircuit.Free;
ActiveCircuit := Circuits.Next;
end;
- ActiveCircuit.NumCircuits := 0;
+ ActiveCircuit := Circuits.First;
+ NumCircuits := 0;
Circuits.Free;
Circuits := TDSSPointerList.Create(2); // Make a new list of circuits
//TODO: check why v8 does this:
- //Parser.Free;
- //Parser := nil;
-
- // In case the actor hasn't been destroyed
- if ActorThread <> nil then
- begin
- //TODO: set SolutionAbort?
- ActorThread.Send_Message(EXIT_ACTOR);
- ActorThread.WaitFor;
- ActorThread.Free;
- ActorThread := nil;
- end;
+ // FreeAndNil(Parser);
// Revert on key global flags to Original States
DefaultEarthModel := DERI;
@@ -480,30 +443,29 @@ procedure ClearAllCircuits(DSS: TDSSContext);
End;
{$ENDIF}// DSS_CAPI_PM
-
PROCEDURE MakeNewCircuit(DSS: TDSSContext; Const Name:String);
Var
S: String;
Begin
- If DSS.NumCircuits <= MaxCircuits - 1 Then
- Begin
- DSS.ActiveCircuit := TDSSCircuit.Create(DSS, Name);
- DSS.ActiveDSSObject := DSS.ActiveSolutionObj;
- {*Handle := *} DSS.Circuits.Add(DSS.ActiveCircuit);
- Inc(DSS.NumCircuits);
- S := DSS.Parser.Remainder; // Pass remainder of string on to vsource.
- {Create a default Circuit}
- DSS.SolutionAbort := False;
- {Voltage source named "source" connected to SourceBus}
- DSS.DSSExecutive.Command := 'New object=vsource.source Bus1=SourceBus ' + S; // Load up the parser as if it were read in
- End
- Else
- Begin
- DoErrorMsg(DSS, 'MakeNewCircuit',
- 'Cannot create new circuit.',
- 'Max. Circuits Exceeded.'+CRLF+
- '(Max no. of circuits='+inttostr(Maxcircuits)+')', 906);
- End;
+ If DSS.NumCircuits <= MaxCircuits - 1 Then
+ Begin
+ DSS.ActiveCircuit := TDSSCircuit.Create(DSS, Name);
+ // DSS.ActiveDSSObject := DSS.ActiveCircuit.Solution;
+ DSS.Circuits.Add(DSS.ActiveCircuit);
+ Inc(DSS.NumCircuits);
+ S := DSS.Parser.Remainder; // Pass remainder of string on to vsource.
+ // Create a default Circuit
+ DSS.SolutionAbort := False;
+ // Voltage source named "source" connected to SourceBus
+ DSS.DSSExecutive.Command := 'New object=vsource.source Bus1=SourceBus ' + S; // Load up the parser as if it were read in
+ End
+ Else
+ Begin
+ DoErrorMsg(DSS, 'MakeNewCircuit',
+ _('Cannot create new circuit.'),
+ Format(_('Max. Circuits Exceeded. (Max no. of circuits=%d)'),
+ [Maxcircuits]), 906);
+ End;
End;
@@ -551,22 +513,29 @@ function VersionString: String;
Result := 'DSS C-API Library version ' + DSS_CAPI_VERSION +
' revision ' + DSS_CAPI_REV +
' based on OpenDSS SVN ' + DSS_CAPI_SVN_REV
+ + ' [FPC ' + {$include %FPCVersion%} + ']'
+ {$IFDEF CPU64}
+ + ' (64-bit build)'
+ {$ENDIF}
+ {$IFDEF CPU32}
+ + ' (32-bit build)'
+ {$ENDIF}
{$IFDEF DSS_CAPI_MVMULT}
+ ' MVMULT'
{$ENDIF}
- + ' [FPC ' + {$include %FPCVersion%} + ']'
+ {$IFDEF DSS_CAPI_INCREMENTAL_Y}
+ + ' INCREMENTAL_Y'
+ {$ENDIF}
{$IFDEF DSS_CAPI_CONTEXT}
+ ' CONTEXT_API'
{$ENDIF}
+ {$IFDEF DSS_CAPI_PM}
+ + ' PM'
+ {$ENDIF}
{$IFDEF DSS_CAPI_DEBUG_BUILD}
+ ' DEBUG'
{$ENDIF}
+ ' ' + timestamp
- {$IFDEF CPUX64}
- + ' (64-bit build)'
- {$ELSE ! CPUX86}
- + ' (32-bit build)'
- {$ENDIF}
;
END;
@@ -596,7 +565,7 @@ function IsDirectoryWritable(const Dir: String): Boolean;
if (Length(PathName) > 0) and not DirectoryExists(PathName) then Begin
// Try to create the directory
if not CreateDir(PathName) then Begin
- DoSimpleMsg(DSS, 'Cannot create ' + PathName + ' directory.', 907);
+ DoSimpleMsg(DSS, 'Cannot create directory: "%s"', [PathName], 907);
Exit;
End;
End;
@@ -629,23 +598,23 @@ function IsDirectoryWritable(const Dir: String): Boolean;
{Log file is written after a query command if LogQueries is true.}
Begin
TRY
- DSS.QueryLogFileName := DSS.OutputDirectory + 'QueryLog.CSV';
+ DSS.QueryLogFileName := DSS.OutputDirectory + 'QueryLog.csv';
If DSS.QueryFirstTime then
Begin
- DSS.QueryLogFile := TFileStream.Create(DSS.QueryLogFileName, fmCreate);
+ DSS.QueryLogFile := TBufferedFileStream.Create(DSS.QueryLogFileName, fmCreate);
FSWriteln(DSS.QueryLogFile, 'Time(h), Property, Result');
DSS.QueryFirstTime := False;
end
Else
begin
- DSS.QueryLogFile := TFileStream.Create(DSS.QueryLogFileName, fmOpenReadWrite);
+ DSS.QueryLogFile := TBufferedFileStream.Create(DSS.QueryLogFileName, fmOpenReadWrite);
DSS.QueryLogFile.Seek(0, soEnd);
end;
FSWriteln(DSS.QueryLogFile,Format('%.10g, %s, %s',[DSS.ActiveCircuit.Solution.DynaVars.dblHour, Prop, S]));
FreeAndNil(DSS.QueryLogFile);
EXCEPT
- On E:Exception Do DoSimpleMsg(DSS, 'Error writing Query Log file: ' + E.Message, 908);
+ On E:Exception Do DoSimpleMsg(DSS, 'Error writing Query Log file: %s', [E.Message], 908);
END;
End;
@@ -657,45 +626,45 @@ function IsDirectoryWritable(const Dir: String): Boolean;
{$IFDEF DSS_CAPI_PM}
// Waits for all the actors running tasks
-procedure Wait4Actors(DSS: TDSSContext; ActorOffset: Integer);
+procedure Wait4Actors(MainDSS: TDSSContext; ActorOffset: Integer);
var
i: Integer;
- Flag: Boolean;
- PMParent: TDSSContext;
- Child: TDSSContext;
+ PMParent, Child, DSS: TDSSContext;
begin
- PMParent := DSS.GetPrime();
- // ActorOffset defines the starting point in which the actors will be evaluated,
- // modification introduced in 01-10-2019 to facilitate the coordination
- // between actors when a simulation is performed using A-Diakoptics
+ PMParent := MainDSS.GetPrime();
+ DSS := MainDSS.ActiveChild;
for i := ActorOffset to High(PMParent.Children) do
begin
try
Child := PMParent.Children[i];
- if Child.ActorStatus = TActorStatus.Busy then
+ if Child.ActorStatus = TActorStatus.Idle then
+ continue;
+
+ Child.ThreadStatusEvent.ResetEvent();
+ while (Child.ActorStatus <> TActorStatus.Idle) do
begin
- Flag := True;
- while Flag do
- Flag := (Child.ActorMA_Msg.WaitFor(10) = TWaitResult.wrTimeout);
+ if Child.ThreadStatusEvent.WaitFor(10) = TWaitResult.wrTimeout then
+ continue;
end;
except
on EOutOfMemory do
- Dosimplemsg(DSS, 'Exception Waiting for the parallel thread to finish a job"', 7006);
+ Dosimplemsg(DSS, _('Exception Waiting for the parallel thread to finish a job'), 7006);
end;
end;
end;
// Clones the active Circuit as many times as requested if possible
-procedure DoClone(DSS: TDSSContext);
+procedure DoClone(MainDSS: TDSSContext);
var
- PMParent: TDSSContext;
i,
NumClones: Integer;
Ref_Ckt: String;
-Begin
- //TODO: DSS must be DSSPrime here?
- PMParent := DSS.GetPrime();
- Ref_Ckt := DSS.LastFileCompiled;
+ PMParent, DSS, ChDSS: TDSSContext;
+begin
+ PMParent := MainDSS.GetPrime();
+ DSS := MainDSS.ActiveChild;
+
+ Ref_Ckt := MainDSS.LastFileCompiled;
DSS.Parser.NextParam;
NumClones := DSS.Parser.IntValue;
PMParent.Parallel_enabled := False;
@@ -704,29 +673,38 @@ procedure DoClone(DSS: TDSSContext);
for i := 1 to NumClones do
begin
New_Actor_Slot(PMParent);
- PMParent.ActiveChild.DSSExecutive.Command := 'compile "' + Ref_Ckt + '"';
+ ChDSS := PMParent.ActiveChild;
+ ChDSS.DSSExecutive.Command := 'compile "' + Ref_Ckt + '"';
+ if ChDSS.ActiveCircuit = NIL then
+ begin
+ DoSimpleMsg(DSS, 'Could not compile the script "%s"', [Ref_Ckt], 7008);
+ Exit;
+ end;
+
// sets the previous maxiterations and controliterations
- PMParent.ActiveChild.ActiveCircuit.Solution.MaxIterations := DSS.ActiveCircuit.Solution.MaxIterations;
- PMParent.ActiveChild.ActiveCircuit.Solution.MaxControlIterations := DSS.ActiveCircuit.Solution.MaxControlIterations;
+ ChDSS.ActiveCircuit.Solution.MaxIterations := DSS.ActiveCircuit.Solution.MaxIterations;
+ ChDSS.ActiveCircuit.Solution.MaxControlIterations := DSS.ActiveCircuit.Solution.MaxControlIterations;
+
// Solves the circuit
- DSS.CmdResult := ExecOptions.DoSetCmd(PMParent.ActiveChild, 1);
+ DSS.CmdResult := ExecOptions.DoSetCmd(ChDSS, 1);
end;
end
else
begin
if NumClones > 0 then
- DoSimpleMsg(DSS, 'There are no more CPUs available', 7001)
+ DoSimpleMsg(DSS, _('There are no more CPUs available'), 7001)
else
- DoSimpleMsg(DSS, 'The number of clones requested is invalid', 7004)
+ DoSimpleMsg(DSS, _('The number of clones requested is invalid'), 7004)
end;
end;
// Prepares memory to host a new actor
-procedure New_Actor_Slot(DSS: TDSSContext);
+procedure New_Actor_Slot(MainDSS: TDSSContext);
var
- PMParent: TDSSContext;
+ PMParent, DSS: TDSSContext;
begin
- PMParent := DSS.GetPrime();
+ PMParent := MainDSS.GetPrime();
+ DSS := MainDSS.ActiveChild;
if (High(PMParent.Children) + 1) < CPU_Cores then
begin
@@ -735,36 +713,50 @@ procedure New_Actor_Slot(DSS: TDSSContext);
PMParent.ActiveChild := TDSSContext.Create(PMParent);
PMParent.Children[PMParent.ActiveChildIndex] := PMParent.ActiveChild;
PMParent.ActiveChild._Name := '_' + inttostr(PMParent.ActiveChildIndex + 1);
- PMParent.ActiveChild.CPU := PMParent.ActiveChildIndex;
+ // PMParent.ActiveChild.CPU := PMParent.ActiveChildIndex;
DSS.GlobalResult := inttostr(PMParent.ActiveChildIndex + 1);
end
else
- DoSimpleMsg(DSS, 'There are no more CPUs available', 7001)
+ DoSimpleMsg(DSS, _('There are no more CPUs available'), 7001)
End;
+{$ENDIF}
+
+
+function DSSTranslate(const s: String): String;
+begin
+ if DSSMessages = NIL then
+ begin
+ Result := s;
+ Exit;
+ end;
+ Result := DSSMessages.Translate(s);
+ if Length(Result) = 0 then
+ Result := s;
+end;
-// Creates a new actor
-procedure New_Actor(DSS: TDSSContext);
+function DSSHelp(const s: String): String;
begin
- DSS.ActorThread := TSolver.Create(DSS, True, DSS.CPU, nil, DSS.ActorMA_Msg);
-// Child.ActorThread.Priority := tpTimeCritical;
- DSS.ActorThread.Start();
- DSS.ActorStatus := TActorStatus.Idle;
+ if DSSPropertyHelp = NIL then
+ begin
+ Result := 'NO HELP OR DESCRIPTION AVAILABLE.';
+ Exit;
+ end;
+ Result := DSSPropertyHelp.Translate(s);
+ if Length(Result) = 0 then
+ Result := s;
end;
-{$ENDIF}
initialization
- // TODO: CALPHA has exceptionally bad precision here... change for v0.13
- CALPHA := Cmplx(-0.5, -0.866025); // -120 degrees phase shift
SQRT2 := Sqrt(2.0);
SQRT3 := Sqrt(3.0);
InvSQRT3 := 1.0/SQRT3;
InvSQRT3x1000 := InvSQRT3 * 1000.0;
- {Initialize filenames and directories}
+ // Initialize filenames and directories
- DSSDirectory := ExtractFilePath('');
+ DSSDirectory := ExpandFileName('');
// want to know if this was built for 64-bit, not whether running on 64 bits
// (i.e. we could have a 32-bit build running on 64 bits; not interested in that
@@ -801,7 +793,7 @@ initialization
{$ELSE}
QueryPerformanceFrequency(CPU_Freq);
{$ENDIF}
- CPU_Cores := CPUCount;
+ CPU_Cores := GetLogicalCpuCount;
DSS_CAPI_INFO_SPARSE_COND := (SysUtils.GetEnvironmentVariable('DSS_CAPI_INFO_SPARSE_COND') = '1');
@@ -815,6 +807,9 @@ initialization
// Default is False, enable at initialization when DSS_CAPI_LEGACY_MODELS = 1
DSS_CAPI_LEGACY_MODELS := (SysUtils.GetEnvironmentVariable('DSS_CAPI_LEGACY_MODELS') = '1');
DSS_CAPI_LEGACY_MODELS := DSS_CAPI_LEGACY_MODELS_PREV;
+
+ // Default is False, enable at initialization when DSS_CAPI_ALLOW_DOSCMD = 1
+ DSS_CAPI_ALLOW_DOSCMD := (SysUtils.GetEnvironmentVariable('DSS_CAPI_ALLOW_DOSCMD') = '1');
// For the 0.12.x branch, default is True, disable at initialization when DSS_CAPI_COM_DEFAULTS = 0
DSS_CAPI_COM_DEFAULTS := (GetEnvironmentVariable('DSS_CAPI_COM_DEFAULTS') <> '0');;
diff --git a/src/Common/DSSHelper.pas b/src/Common/DSSHelper.pas
index ed8329808..5622b9053 100644
--- a/src/Common/DSSHelper.pas
+++ b/src/Common/DSSHelper.pas
@@ -84,7 +84,6 @@ TDSSGlobalHelper = class helper for TDSSContext
function GetXYCurveClass: TXYCurve; inline;
function GetGrowthShapeClass: TGrowthShape; inline;
function GetSpectrumClass: TSpectrum; inline;
- function GetSolutionClass: TDSSClass; inline;
function GetEnergyMeterClass: TEnergyMeter; inline;
function GetMonitorClass: TDSSMonitor; inline;
function GetSensorClass: TSensor; inline;
@@ -106,7 +105,7 @@ TDSSGlobalHelper = class helper for TDSSContext
function GetVSourceClass: TVSource; inline;
function GetISourceClass: TISource; inline;
function GetVCSSClass: TVCCS; inline;
- function GetLoadClass: TLoad; inline;
+ function GetLoadClass: TLoad; inline;
function GetTransformerClass: TTransf; inline;
function GetRegControlClass: TRegControl; inline;
function GetCapacitorClass: TCapacitor; inline;
@@ -145,7 +144,6 @@ TDSSGlobalHelper = class helper for TDSSContext
procedure SetXYCurveClass(val: TXYCurve); inline;
procedure SetGrowthShapeClass(val: TGrowthShape); inline;
procedure SetSpectrumClass(val: TSpectrum); inline;
- procedure SetSolutionClass(val: TDSSClass); inline;
procedure SetEnergyMeterClass(val: TEnergyMeter); inline;
procedure SetMonitorClass(val: TDSSMonitor); inline;
procedure SetSensorClass(val: TSensor); inline;
@@ -193,117 +191,11 @@ TDSSGlobalHelper = class helper for TDSSContext
procedure SetGICLineClass(val: TGICLine); inline;
procedure SetGICTransformerClass(val:TGICTransformer); inline;
- function GetActiveSolutionObj: TSolutionObj; inline;
- function GetActiveCapControlObj: TCapControlObj; inline;
- function GetActiveESPVLControlObj: TESPVLControlObj; inline;
- function GetActiveExpControlObj: TExpControlObj; inline;
- function GetActiveGenDispatcherObj: TGenDispatcherObj; inline;
- function GetActiveInvControlObj: TInvControlObj; inline;
- function GetActiveInvControl2Obj: TInvControl2Obj; inline;
- function GetActiveRecloserObj: TRecloserObj; inline;
- function GetActiveRegControlObj: TRegControlObj; inline;
- function GetActiveRelayObj: TRelayObj; inline;
- function GetActiveStorageControllerObj: TStorageControllerObj; inline;
- function GetActiveStorageController2Obj: TStorageController2Obj; inline;
- function GetActiveSwtControlObj: TSwtControlObj; inline;
- function GetActiveUPFCControlObj: TUPFCControlObj; inline;
- // function GetActiveVVCControlObj: TVVControlObj; inline;
- function GetActiveConductorDataObj: TConductorDataObj; inline;
- function GetActiveGrowthShapeObj: TGrowthShapeObj; inline;
- function GetActiveLineCodeObj: TLineCodeObj; inline;
- function GetActiveLineGeometryObj: TLineGeometryObj; inline;
- function GetActiveLineSpacingObj: TLineSpacingObj; inline;
- function GetActiveLoadShapeObj: TLoadShapeObj; inline;
- function GetActivePriceShapeObj: TPriceShapeObj; inline;
- function GetActiveSpectrumObj: TSpectrumObj; inline;
- function GetActiveTCC_CurveObj: TTCC_CurveObj; inline;
- function GetActiveTShapeObj: TTShapeObj; inline;
- function GetActiveXfmrCodeObj: TXfmrCodeObj; inline;
- function GetActiveXYcurveObj: TXYcurveObj; inline;
function GetActiveEnergyMeterObj: TEnergyMeterObj; inline;
- // function GetActiveFMonitorObj: TFMonitorObj; inline;
- function GetActiveMonitorObj: TMonitorObj; inline;
- function GetActiveSensorObj: TSensorObj; inline;
- // function GetActiveEquivalentObj: TEquivalentObj; inline;
- function GetActiveGeneratorObj: TGeneratorObj; inline;
- // function GetActiveGeneric5Obj: TGeneric5Obj; inline;
- function GetActiveGICLineObj: TGICLineObj; inline;
- function GetActiveGICsourceObj: TGICSourceObj; inline;
- function GetActiveIndMach012Obj: TIndMach012Obj; inline;
- function GetActiveIsourceObj: TIsourceObj; inline;
- function GetActiveLoadObj: TLoadObj; inline;
- function GetActivePVsystemObj: TPVsystemObj; inline;
- function GetActivePVsystem2Obj: TPVsystem2Obj; inline;
- function GetActiveStorageObj: TStorageObj; inline;
- function GetActiveStorage2Obj: TStorage2Obj; inline;
- function GetActiveUPFCObj: TUPFCObj; inline;
- function GetActiveVCCSObj: TVCCSObj; inline;
- function GetActiveVSConverterObj: TVSConverterObj; inline;
- function GetActiveVsourceObj: TVsourceObj; inline;
- function GetActiveAutoTransObj: TAutoTransObj; inline;
- function GetActiveCapacitorObj: TCapacitorObj; inline;
function GetActiveFaultObj: TFaultObj; inline;
- function GetActiveFuseObj: TFuseObj; inline;
- function GetActiveGICTransformerObj: TGICTransformerObj; inline;
- function GetActiveLineObj: TLineObj; inline;
- function GetActiveReactorObj: TReactorObj; inline;
- function GetActiveTransfObj: TTransfObj; inline;
- procedure SetActiveSolutionObj(val: TSolutionObj); inline;
- procedure SetActiveCapControlObj(val: TCapControlObj); inline;
- procedure SetActiveESPVLControlObj(val: TESPVLControlObj); inline;
- procedure SetActiveExpControlObj(val: TExpControlObj); inline;
- procedure SetActiveGenDispatcherObj(val: TGenDispatcherObj); inline;
- procedure SetActiveInvControlObj(val: TInvControlObj); inline;
- procedure SetActiveInvControl2Obj(val: TInvControl2Obj); inline;
- procedure SetActiveRecloserObj(val: TRecloserObj); inline;
- procedure SetActiveRegControlObj(val: TRegControlObj); inline;
- procedure SetActiveRelayObj(val: TRelayObj); inline;
- procedure SetActiveStorageControllerObj(val: TStorageControllerObj); inline;
- procedure SetActiveStorageController2Obj(val: TStorageController2Obj); inline;
- procedure SetActiveSwtControlObj(val: TSwtControlObj); inline;
- procedure SetActiveUPFCControlObj(val: TUPFCControlObj); inline;
- // procedure SetActiveVVCControlObj(val: TVVControlObj); inline;
- procedure SetActiveConductorDataObj(val: TConductorDataObj); inline;
- procedure SetActiveGrowthShapeObj(val: TGrowthShapeObj); inline;
- procedure SetActiveLineCodeObj(val: TLineCodeObj); inline;
- procedure SetActiveLineGeometryObj(val: TLineGeometryObj); inline;
- procedure SetActiveLineSpacingObj(val: TLineSpacingObj); inline;
- procedure SetActiveLoadShapeObj(val: TLoadShapeObj); inline;
- procedure SetActivePriceShapeObj(val: TPriceShapeObj); inline;
- procedure SetActiveSpectrumObj(val: TSpectrumObj); inline;
- procedure SetActiveTCC_CurveObj(val: TTCC_CurveObj); inline;
- procedure SetActiveTShapeObj(val: TTShapeObj); inline;
- procedure SetActiveXfmrCodeObj(val: TXfmrCodeObj); inline;
- procedure SetActiveXYcurveObj(val: TXYcurveObj); inline;
procedure SetActiveEnergyMeterObj(val: TEnergyMeterObj); inline;
- // procedure SetActiveFMonitorObj(val: TFMonitorObj); inline;
- procedure SetActiveMonitorObj(val: TMonitorObj); inline;
- procedure SetActiveSensorObj(val: TSensorObj); inline;
- // procedure SetActiveEquivalentObj(val: TEquivalentObj); inline;
- procedure SetActiveGeneratorObj(val: TGeneratorObj); inline;
- // procedure SetActiveGeneric5Obj(val: TGeneric5Obj); inline;
- procedure SetActiveGICLineObj(val: TGICLineObj); inline;
- procedure SetActiveGICsourceObj(val: TGICSourceObj); inline;
- procedure SetActiveIndMach012Obj(val: TIndMach012Obj); inline;
- procedure SetActiveIsourceObj(val: TIsourceObj); inline;
- procedure SetActiveLoadObj(val: TLoadObj); inline;
- procedure SetActivePVsystemObj(val: TPVsystemObj); inline;
- procedure SetActivePVsystem2Obj(val: TPVsystem2Obj); inline;
- procedure SetActiveStorageObj(val: TStorageObj); inline;
- procedure SetActiveStorage2Obj(val: TStorage2Obj); inline;
- procedure SetActiveUPFCObj(val: TUPFCObj); inline;
- procedure SetActiveVCCSObj(val: TVCCSObj); inline;
- procedure SetActiveVSConverterObj(val: TVSConverterObj); inline;
- procedure SetActiveVsourceObj(val: TVsourceObj); inline;
- procedure SetActiveAutoTransObj(val: TAutoTransObj); inline;
- procedure SetActiveCapacitorObj(val: TCapacitorObj); inline;
procedure SetActiveFaultObj(val: TFaultObj); inline;
- procedure SetActiveFuseObj(val: TFuseObj); inline;
- procedure SetActiveGICTransformerObj(val: TGICTransformerObj); inline;
- procedure SetActiveLineObj(val: TLineObj); inline;
- procedure SetActiveReactorObj(val: TReactorObj); inline;
- procedure SetActiveTransfObj(val: TTransfObj); inline;
public
{$IFDEF DSS_CAPI_PM}
@@ -313,16 +205,18 @@ TDSSGlobalHelper = class helper for TDSSContext
property ControlProxyObj: TControlProxyObj read GetControlProxyObj;
property DSSExecutive: TExecutive read GetDSSExecutive write SetDSSExecutive;
property CIMExporter: TCIMExporter read GetCIMExporter write SetCIMExporter;
+
property ActiveCircuit: TDSSCircuit read GetActiveCircuit write SetActiveCircuit;
property ActiveDSSObject: TDSSObject read GetActiveDSSObject write SetActiveDSSObject;
-
+ property ActiveFaultObj: TFaultObj read GetActiveFaultObj write SetActiveFaultObj;
+ property ActiveEnergyMeterObj: TEnergyMeterObj read GetActiveEnergyMeterObj write SetActiveEnergyMeterObj;
+
property LoadShapeClass: TLoadShape read GetLoadShapeClass write SetLoadShapeClass;
property TShapeClass: TTshape read GetTShapeClass write SetTShapeClass;
property PriceShapeClass: TPriceShape read GetPriceShapeClass write SetPriceShapeClass;
property XYCurveClass: TXYCurve read GetXYCurveClass write SetXYCurveClass;
property GrowthShapeClass: TGrowthShape read GetGrowthShapeClass write SetGrowthShapeClass;
property SpectrumClass: TSpectrum read GetSpectrumClass write SetSpectrumClass;
- property SolutionClass: TDSSClass read GetSolutionClass write SetSolutionClass;
property EnergyMeterClass: TEnergyMeter read GetEnergyMeterClass write SetEnergyMeterClass;
property MonitorClass: TDSSMonitor read GetMonitorClass write SetMonitorClass;
property SensorClass: TSensor read GetSensorClass write SetSensorClass;
@@ -369,63 +263,6 @@ TDSSGlobalHelper = class helper for TDSSContext
property XfmrCodeClass: TXfmrCode read GetXfmrCodeClass write SetXfmrCodeClass;
property GICLineClass: TGICLine read GetGICLineClass write SetGICLineClass;
property GICTransformerClass: TGICTransformer read GetGICTransformerClass write SetGICTransformerClass;
-
- property ActiveSolutionObj: TSolutionObj read GetActiveSolutionObj write SetActiveSolutionObj;
- property ActiveCapControlObj: TCapControlObj read GetActiveCapControlObj write SetActiveCapControlObj;
- property ActiveESPVLControlObj: TESPVLControlObj read GetActiveESPVLControlObj write SetActiveESPVLControlObj;
- property ActiveExpControlObj: TExpControlObj read GetActiveExpControlObj write SetActiveExpControlObj;
- property ActiveGenDispatcherObj: TGenDispatcherObj read GetActiveGenDispatcherObj write SetActiveGenDispatcherObj;
- property ActiveInvControlObj: TInvControlObj read GetActiveInvControlObj write SetActiveInvControlObj;
- property ActiveInvControl2Obj: TInvControl2Obj read GetActiveInvControl2Obj write SetActiveInvControl2Obj;
- property ActiveRecloserObj: TRecloserObj read GetActiveRecloserObj write SetActiveRecloserObj;
- property ActiveRegControlObj: TRegControlObj read GetActiveRegControlObj write SetActiveRegControlObj;
- property ActiveRelayObj: TRelayObj read GetActiveRelayObj write SetActiveRelayObj;
- property ActiveStorageControllerObj: TStorageControllerObj read GetActiveStorageControllerObj write SetActiveStorageControllerObj;
- property ActiveStorageController2Obj: TStorageController2Obj read GetActiveStorageController2Obj write SetActiveStorageController2Obj;
- property ActiveSwtControlObj: TSwtControlObj read GetActiveSwtControlObj write SetActiveSwtControlObj;
- property ActiveUPFCControlObj: TUPFCControlObj read GetActiveUPFCControlObj write SetActiveUPFCControlObj;
- // property ActiveVVCControlObj: TVVControlObj read GetActiveVVCControlObj write SetActiveVVCControlObj;
- property ActiveConductorDataObj: TConductorDataObj read GetActiveConductorDataObj write SetActiveConductorDataObj;
- property ActiveGrowthShapeObj: TGrowthShapeObj read GetActiveGrowthShapeObj write SetActiveGrowthShapeObj;
- property ActiveLineCodeObj: TLineCodeObj read GetActiveLineCodeObj write SetActiveLineCodeObj;
- property ActiveLineGeometryObj: TLineGeometryObj read GetActiveLineGeometryObj write SetActiveLineGeometryObj;
- property ActiveLineSpacingObj: TLineSpacingObj read GetActiveLineSpacingObj write SetActiveLineSpacingObj;
- property ActiveLoadShapeObj: TLoadShapeObj read GetActiveLoadShapeObj write SetActiveLoadShapeObj;
- property ActivePriceShapeObj: TPriceShapeObj read GetActivePriceShapeObj write SetActivePriceShapeObj;
- property ActiveSpectrumObj: TSpectrumObj read GetActiveSpectrumObj write SetActiveSpectrumObj;
- property ActiveTCC_CurveObj: TTCC_CurveObj read GetActiveTCC_CurveObj write SetActiveTCC_CurveObj;
- property ActiveTShapeObj: TTShapeObj read GetActiveTShapeObj write SetActiveTShapeObj;
- property ActiveXfmrCodeObj: TXfmrCodeObj read GetActiveXfmrCodeObj write SetActiveXfmrCodeObj;
- property ActiveXYcurveObj: TXYcurveObj read GetActiveXYcurveObj write SetActiveXYcurveObj;
- property ActiveEnergyMeterObj: TEnergyMeterObj read GetActiveEnergyMeterObj write SetActiveEnergyMeterObj;
- // property ActiveFMonitorObj: TFMonitorObj read GetActiveFMonitorObj write SetActiveFMonitorObj;
- property ActiveMonitorObj: TMonitorObj read GetActiveMonitorObj write SetActiveMonitorObj;
- property ActiveSensorObj: TSensorObj read GetActiveSensorObj write SetActiveSensorObj;
- // property ActiveEquivalentObj: TEquivalentObj read GetActiveEquivalentObj write SetActiveEquivalentObj;
- property ActiveGeneratorObj: TGeneratorObj read GetActiveGeneratorObj write SetActiveGeneratorObj;
- // property ActiveGeneric5Obj: TGeneric5Obj read GetActiveGeneric5Obj write SetActiveGeneric5Obj;
- property ActiveGICLineObj: TGICLineObj read GetActiveGICLineObj write SetActiveGICLineObj;
- property ActiveGICsourceObj: TGICSourceObj read GetActiveGICsourceObj write SetActiveGICsourceObj;
- property ActiveIndMach012Obj: TIndMach012Obj read GetActiveIndMach012Obj write SetActiveIndMach012Obj;
- property ActiveIsourceObj: TIsourceObj read GetActiveIsourceObj write SetActiveIsourceObj;
- property ActiveLoadObj: TLoadObj read GetActiveLoadObj write SetActiveLoadObj;
- property ActivePVsystemObj: TPVsystemObj read GetActivePVsystemObj write SetActivePVsystemObj;
- property ActivePVsystem2Obj: TPVsystem2Obj read GetActivePVsystem2Obj write SetActivePVsystem2Obj;
- property ActiveStorageObj: TStorageObj read GetActiveStorageObj write SetActiveStorageObj;
- property ActiveStorage2Obj: TStorage2Obj read GetActiveStorage2Obj write SetActiveStorage2Obj;
- property ActiveUPFCObj: TUPFCObj read GetActiveUPFCObj write SetActiveUPFCObj;
- property ActiveVCCSObj: TVCCSObj read GetActiveVCCSObj write SetActiveVCCSObj;
- property ActiveVSConverterObj: TVSConverterObj read GetActiveVSConverterObj write SetActiveVSConverterObj;
- property ActiveVsourceObj: TVsourceObj read GetActiveVsourceObj write SetActiveVsourceObj;
- property ActiveAutoTransObj: TAutoTransObj read GetActiveAutoTransObj write SetActiveAutoTransObj;
- property ActiveCapacitorObj: TCapacitorObj read GetActiveCapacitorObj write SetActiveCapacitorObj;
- property ActiveFaultObj: TFaultObj read GetActiveFaultObj write SetActiveFaultObj;
- property ActiveFuseObj: TFuseObj read GetActiveFuseObj write SetActiveFuseObj;
- property ActiveGICTransformerObj: TGICTransformerObj read GetActiveGICTransformerObj write SetActiveGICTransformerObj;
- property ActiveLineObj: TLineObj read GetActiveLineObj write SetActiveLineObj;
- property ActiveReactorObj: TReactorObj read GetActiveReactorObj write SetActiveReactorObj;
- property ActiveTransfObj: TTransfObj read GetActiveTransfObj write SetActiveTransfObj;
-
end;
implementation
@@ -445,7 +282,6 @@ function TDSSGlobalHelper.GetPriceShapeClass: TPriceShape; begin Result := TPric
function TDSSGlobalHelper.GetXYCurveClass: TXYCurve; begin Result := TXYCurve(FXYCurveClass); end;
function TDSSGlobalHelper.GetGrowthShapeClass: TGrowthShape; begin Result := TGrowthShape(FGrowthShapeClass); end;
function TDSSGlobalHelper.GetSpectrumClass: TSpectrum; begin Result := TSpectrum(FSpectrumClass); end;
-function TDSSGlobalHelper.GetSolutionClass: TDSSClass; begin Result := TDSSClass(FSolutionClass); end;
function TDSSGlobalHelper.GetEnergyMeterClass: TEnergyMeter; begin Result := TEnergyMeter(FEnergyMeterClass); end;
function TDSSGlobalHelper.GetMonitorClass: TDSSMonitor; begin Result := TDSSMonitor(FMonitorClass); end;
function TDSSGlobalHelper.GetSensorClass: TSensor; begin Result := TSensor(FSensorClass); end;
@@ -493,61 +329,8 @@ function TDSSGlobalHelper.GetXfmrCodeClass: TXfmrCode; begin Result := TXfmrCode
function TDSSGlobalHelper.GetGICLineClass: TGICLine; begin Result := TGICLine(FGICLineClass); end;
function TDSSGlobalHelper.GetGICTransformerClass: TGICTransformer; begin Result := TGICTransformer(FGICTransformerClass); end;
-function TDSSGlobalHelper.GetActiveSolutionObj: TSolutionObj; begin Result := TSolutionObj(FActiveSolutionObj); end;
-function TDSSGlobalHelper.GetActiveCapControlObj: TCapControlObj; begin Result := TCapControlObj(FActiveCapControlObj); end;
-function TDSSGlobalHelper.GetActiveESPVLControlObj: TESPVLControlObj; begin Result := TESPVLControlObj(FActiveESPVLControlObj); end;
-function TDSSGlobalHelper.GetActiveExpControlObj: TExpControlObj; begin Result := TExpControlObj(FActiveExpControlObj); end;
-function TDSSGlobalHelper.GetActiveGenDispatcherObj: TGenDispatcherObj; begin Result := TGenDispatcherObj(FActiveGenDispatcherObj); end;
-function TDSSGlobalHelper.GetActiveInvControlObj: TInvControlObj; begin Result := TInvControlObj(FActiveInvControlObj); end;
-function TDSSGlobalHelper.GetActiveInvControl2Obj: TInvControl2Obj; begin Result := TInvControl2Obj(FActiveInvControl2Obj); end;
-function TDSSGlobalHelper.GetActiveRecloserObj: TRecloserObj; begin Result := TRecloserObj(FActiveRecloserObj); end;
-function TDSSGlobalHelper.GetActiveRegControlObj: TRegControlObj; begin Result := TRegControlObj(FActiveRegControlObj); end;
-function TDSSGlobalHelper.GetActiveRelayObj: TRelayObj; begin Result := TRelayObj(FActiveRelayObj); end;
-function TDSSGlobalHelper.GetActiveStorageControllerObj: TStorageControllerObj; begin Result := TStorageControllerObj(FActiveStorageControllerObj); end;
-function TDSSGlobalHelper.GetActiveStorageController2Obj: TStorageController2Obj; begin Result := TStorageController2Obj(FActiveStorageController2Obj); end;
-function TDSSGlobalHelper.GetActiveSwtControlObj: TSwtControlObj; begin Result := TSwtControlObj(FActiveSwtControlObj); end;
-function TDSSGlobalHelper.GetActiveUPFCControlObj: TUPFCControlObj; begin Result := TUPFCControlObj(FActiveUPFCControlObj); end;
-// function TDSSGlobalHelper.ActiveGetVVCControlObj: TVVControlObj; begin Result := TVVCControlObj(FActiveVVCControlObj); end;
-function TDSSGlobalHelper.GetActiveConductorDataObj: TConductorDataObj; begin Result := TConductorDataObj(FActiveConductorDataObj); end;
-function TDSSGlobalHelper.GetActiveGrowthShapeObj: TGrowthShapeObj; begin Result := TGrowthShapeObj(FActiveGrowthShapeObj); end;
-function TDSSGlobalHelper.GetActiveLineCodeObj: TLineCodeObj; begin Result := TLineCodeObj(FActiveLineCodeObj); end;
-function TDSSGlobalHelper.GetActiveLineGeometryObj: TLineGeometryObj; begin Result := TLineGeometryObj(FActiveLineGeometryObj); end;
-function TDSSGlobalHelper.GetActiveLineSpacingObj: TLineSpacingObj; begin Result := TLineSpacingObj(FActiveLineSpacingObj); end;
-function TDSSGlobalHelper.GetActiveLoadShapeObj: TLoadShapeObj; begin Result := TLoadShapeObj(FActiveLoadShapeObj); end;
-function TDSSGlobalHelper.GetActivePriceShapeObj: TPriceShapeObj; begin Result := TPriceShapeObj(FActivePriceShapeObj); end;
-function TDSSGlobalHelper.GetActiveSpectrumObj: TSpectrumObj; begin Result := TSpectrumObj(FActiveSpectrumObj); end;
-function TDSSGlobalHelper.GetActiveTCC_CurveObj: TTCC_CurveObj; begin Result := TTCC_CurveObj(FActiveTCC_CurveObj); end;
-function TDSSGlobalHelper.GetActiveTShapeObj: TTShapeObj; begin Result := TTShapeObj(FActiveTShapeObj); end;
-function TDSSGlobalHelper.GetActiveXfmrCodeObj: TXfmrCodeObj; begin Result := TXfmrCodeObj(FActiveXfmrCodeObj); end;
-function TDSSGlobalHelper.GetActiveXYcurveObj: TXYcurveObj; begin Result := TXYcurveObj(FActiveXYcurveObj); end;
function TDSSGlobalHelper.GetActiveEnergyMeterObj: TEnergyMeterObj; begin Result := TEnergyMeterObj(FActiveEnergyMeterObj); end;
-// function TDSSGlobalHelper.GetActiveFMonitorObj: TFMonitorObj; begin Result := TFMonitorObj(FActiveFMonitorObj); end;
-function TDSSGlobalHelper.GetActiveMonitorObj: TMonitorObj; begin Result := TMonitorObj(FActiveMonitorObj); end;
-function TDSSGlobalHelper.GetActiveSensorObj: TSensorObj; begin Result := TSensorObj(FActiveSensorObj); end;
-// function TDSSGlobalHelper.ActiveGetEquivalentObj: TEquivalentObj; begin Result := TEquivalentObj(FActiveEquivalentObj); end;
-function TDSSGlobalHelper.GetActiveGeneratorObj: TGeneratorObj; begin Result := TGeneratorObj(FActiveGeneratorObj); end;
-// function TDSSGlobalHelper.ActiveGetGeneric5Obj: TGeneric5Obj; begin Result := TGeneric5Obj(FActiveGeneric5Obj); end;
-function TDSSGlobalHelper.GetActiveGICLineObj: TGICLineObj; begin Result := TGICLineObj(FActiveGICLineObj); end;
-function TDSSGlobalHelper.GetActiveGICsourceObj: TGICSourceObj; begin Result := TGICsourceObj(FActiveGICsourceObj); end;
-function TDSSGlobalHelper.GetActiveIndMach012Obj: TIndMach012Obj; begin Result := TIndMach012Obj(FActiveIndMach012Obj); end;
-function TDSSGlobalHelper.GetActiveIsourceObj: TIsourceObj; begin Result := TIsourceObj(FActiveIsourceObj); end;
-function TDSSGlobalHelper.GetActiveLoadObj: TLoadObj; begin Result := TLoadObj(FActiveLoadObj); end;
-function TDSSGlobalHelper.GetActivePVsystemObj: TPVsystemObj; begin Result := TPVsystemObj(FActivePVsystemObj); end;
-function TDSSGlobalHelper.GetActivePVsystem2Obj: TPVsystem2Obj; begin Result := TPVsystem2Obj(FActivePVsystem2Obj); end;
-function TDSSGlobalHelper.GetActiveStorageObj: TStorageObj; begin Result := TStorageObj(FActiveStorageObj); end;
-function TDSSGlobalHelper.GetActiveStorage2Obj: TStorage2Obj; begin Result := TStorage2Obj(FActiveStorage2Obj); end;
-function TDSSGlobalHelper.GetActiveUPFCObj: TUPFCObj; begin Result := TUPFCObj(FActiveUPFCObj); end;
-function TDSSGlobalHelper.GetActiveVCCSObj: TVCCSObj; begin Result := TVCCSObj(FActiveVCCSObj); end;
-function TDSSGlobalHelper.GetActiveVSConverterObj: TVSConverterObj; begin Result := TVSConverterObj(FActiveVSConverterObj); end;
-function TDSSGlobalHelper.GetActiveVsourceObj: TVsourceObj; begin Result := TVsourceObj(FActiveVsourceObj); end;
-function TDSSGlobalHelper.GetActiveAutoTransObj: TAutoTransObj; begin Result := TAutoTransObj(FActiveAutoTransObj); end;
-function TDSSGlobalHelper.GetActiveCapacitorObj: TCapacitorObj; begin Result := TCapacitorObj(FActiveCapacitorObj); end;
function TDSSGlobalHelper.GetActiveFaultObj: TFaultObj; begin Result := TFaultObj(FActiveFaultObj); end;
-function TDSSGlobalHelper.GetActiveFuseObj: TFuseObj; begin Result := TFuseObj(FActiveFuseObj); end;
-function TDSSGlobalHelper.GetActiveGICTransformerObj: TGICTransformerObj; begin Result := TGICTransformerObj(FActiveGICTransformerObj); end;
-function TDSSGlobalHelper.GetActiveLineObj: TLineObj; begin Result := TLineObj(FActiveLineObj); end;
-function TDSSGlobalHelper.GetActiveReactorObj: TReactorObj; begin Result := TReactorObj(FActiveReactorObj); end;
-function TDSSGlobalHelper.GetActiveTransfObj: TTransfObj; begin Result := TTransfObj(FActiveTransfObj); end;
{$IFDEF DSS_CAPI_PM}
@@ -563,7 +346,6 @@ procedure TDSSGlobalHelper.SetPriceShapeClass(val: TPriceShape); begin FPriceSha
procedure TDSSGlobalHelper.SetXYCurveClass(val: TXYCurve); begin FXYCurveClass := val; end;
procedure TDSSGlobalHelper.SetGrowthShapeClass(val: TGrowthShape); begin FGrowthShapeClass := val; end;
procedure TDSSGlobalHelper.SetSpectrumClass(val: TSpectrum); begin FSpectrumClass := val; end;
-procedure TDSSGlobalHelper.SetSolutionClass(val: TDSSClass); begin FSolutionClass := val; end;
procedure TDSSGlobalHelper.SetEnergyMeterClass(val: TEnergyMeter); begin FEnergyMeterClass := val; end;
procedure TDSSGlobalHelper.SetMonitorClass(val: TDSSMonitor); begin FMonitorClass := val; end;
procedure TDSSGlobalHelper.SetSensorClass(val: TSensor); begin FSensorClass := val; end;
@@ -611,61 +393,8 @@ procedure TDSSGlobalHelper.SetXfmrCodeClass(val: TXfmrCode); begin FXfmrCodeClas
procedure TDSSGlobalHelper.SetGICLineClass(val: TGICLine); begin FGICLineClass := val; end;
procedure TDSSGlobalHelper.SetGICTransformerClass(val:TGICTransformer); begin FGICTransformerClass := val; end;
-procedure TDSSGlobalHelper.SetActiveSolutionObj(val: TSolutionObj); begin FActiveSolutionObj := val; end;
-procedure TDSSGlobalHelper.SetActiveCapControlObj(val: TCapControlObj); begin FActiveCapControlObj := val; end;
-procedure TDSSGlobalHelper.SetActiveESPVLControlObj(val: TESPVLControlObj); begin FActiveESPVLControlObj := val; end;
-procedure TDSSGlobalHelper.SetActiveExpControlObj(val: TExpControlObj); begin FActiveExpControlObj := val; end;
-procedure TDSSGlobalHelper.SetActiveGenDispatcherObj(val: TGenDispatcherObj); begin FActiveGenDispatcherObj := val; end;
-procedure TDSSGlobalHelper.SetActiveInvControlObj(val: TInvControlObj); begin FActiveInvControlObj := val; end;
-procedure TDSSGlobalHelper.SetActiveInvControl2Obj(val: TInvControl2Obj); begin FActiveInvControl2Obj := val; end;
-procedure TDSSGlobalHelper.SetActiveRecloserObj(val: TRecloserObj); begin FActiveRecloserObj := val; end;
-procedure TDSSGlobalHelper.SetActiveRegControlObj(val: TRegControlObj); begin FActiveRegControlObj := val; end;
-procedure TDSSGlobalHelper.SetActiveRelayObj(val: TRelayObj); begin FActiveRelayObj := val; end;
-procedure TDSSGlobalHelper.SetActiveStorageControllerObj(val: TStorageControllerObj); begin FActiveStorageControllerObj := val; end;
-procedure TDSSGlobalHelper.SetActiveStorageController2Obj(val: TStorageController2Obj); begin FActiveStorageController2Obj := val; end;
-procedure TDSSGlobalHelper.SetActiveSwtControlObj(val: TSwtControlObj); begin FActiveSwtControlObj := val; end;
-procedure TDSSGlobalHelper.SetActiveUPFCControlObj(val: TUPFCControlObj); begin FActiveUPFCControlObj := val; end;
-// procedure TDSSGlobalHelper.SetActiveVVCControlObj(val: TVVControlObj); begin FActiveVVCControlObj := val; end;
-procedure TDSSGlobalHelper.SetActiveConductorDataObj(val: TConductorDataObj); begin FActiveConductorDataObj := val; end;
-procedure TDSSGlobalHelper.SetActiveGrowthShapeObj(val: TGrowthShapeObj); begin FActiveGrowthShapeObj := val; end;
-procedure TDSSGlobalHelper.SetActiveLineCodeObj(val: TLineCodeObj); begin FActiveLineCodeObj := val; end;
-procedure TDSSGlobalHelper.SetActiveLineGeometryObj(val: TLineGeometryObj); begin FActiveLineGeometryObj := val; end;
-procedure TDSSGlobalHelper.SetActiveLineSpacingObj(val: TLineSpacingObj); begin FActiveLineSpacingObj := val; end;
-procedure TDSSGlobalHelper.SetActiveLoadShapeObj(val: TLoadShapeObj); begin FActiveLoadShapeObj := val; end;
-procedure TDSSGlobalHelper.SetActivePriceShapeObj(val: TPriceShapeObj); begin FActivePriceShapeObj := val; end;
-procedure TDSSGlobalHelper.SetActiveSpectrumObj(val: TSpectrumObj); begin FActiveSpectrumObj := val; end;
-procedure TDSSGlobalHelper.SetActiveTCC_CurveObj(val: TTCC_CurveObj); begin FActiveTCC_CurveObj := val; end;
-procedure TDSSGlobalHelper.SetActiveTShapeObj(val: TTShapeObj); begin FActiveTShapeObj := val; end;
-procedure TDSSGlobalHelper.SetActiveXfmrCodeObj(val: TXfmrCodeObj); begin FActiveXfmrCodeObj := val; end;
-procedure TDSSGlobalHelper.SetActiveXYcurveObj(val: TXYcurveObj); begin FActiveXYcurveObj := val; end;
procedure TDSSGlobalHelper.SetActiveEnergyMeterObj(val: TEnergyMeterObj); begin FActiveEnergyMeterObj := val; end;
-// procedure TDSSGlobalHelper.SetActiveFMonitorObj(val: TFMonitorObj); begin FActiveFMonitorObj := val; end;
-procedure TDSSGlobalHelper.SetActiveMonitorObj(val: TMonitorObj); begin FActiveMonitorObj := val; end;
-procedure TDSSGlobalHelper.SetActiveSensorObj(val: TSensorObj); begin FActiveSensorObj := val; end;
-// procedure TDSSGlobalHelper.SetActiveEquivalentObj(val: TEquivalentObj); begin FActiveEquivalentObj := val; end;
-procedure TDSSGlobalHelper.SetActiveGeneratorObj(val: TGeneratorObj); begin FActiveGeneratorObj := val; end;
-// procedure TDSSGlobalHelper.SetActiveGeneric5Obj(val: TGeneric5Obj); begin FActiveGeneric5Obj := val; end;
-procedure TDSSGlobalHelper.SetActiveGICLineObj(val: TGICLineObj); begin FActiveGICLineObj := val; end;
-procedure TDSSGlobalHelper.SetActiveGICsourceObj(val: TGICSourceObj); begin FActiveGICsourceObj := val; end;
-procedure TDSSGlobalHelper.SetActiveIndMach012Obj(val: TIndMach012Obj); begin FActiveIndMach012Obj := val; end;
-procedure TDSSGlobalHelper.SetActiveIsourceObj(val: TIsourceObj); begin FActiveIsourceObj := val; end;
-procedure TDSSGlobalHelper.SetActiveLoadObj(val: TLoadObj); begin FActiveLoadObj := val; end;
-procedure TDSSGlobalHelper.SetActivePVsystemObj(val: TPVsystemObj); begin FActivePVsystemObj := val; end;
-procedure TDSSGlobalHelper.SetActivePVsystem2Obj(val: TPVsystem2Obj); begin FActivePVsystem2Obj := val; end;
-procedure TDSSGlobalHelper.SetActiveStorageObj(val: TStorageObj); begin FActiveStorageObj := val; end;
-procedure TDSSGlobalHelper.SetActiveStorage2Obj(val: TStorage2Obj); begin FActiveStorage2Obj := val; end;
-procedure TDSSGlobalHelper.SetActiveUPFCObj(val: TUPFCObj); begin FActiveUPFCObj := val; end;
-procedure TDSSGlobalHelper.SetActiveVCCSObj(val: TVCCSObj); begin FActiveVCCSObj := val; end;
-procedure TDSSGlobalHelper.SetActiveVSConverterObj(val: TVSConverterObj); begin FActiveVSConverterObj := val; end;
-procedure TDSSGlobalHelper.SetActiveVsourceObj(val: TVsourceObj); begin FActiveVsourceObj := val; end;
-procedure TDSSGlobalHelper.SetActiveAutoTransObj(val: TAutoTransObj); begin FActiveAutoTransObj := val; end;
-procedure TDSSGlobalHelper.SetActiveCapacitorObj(val: TCapacitorObj); begin FActiveCapacitorObj := val; end;
procedure TDSSGlobalHelper.SetActiveFaultObj(val: TFaultObj); begin FActiveFaultObj := val; end;
-procedure TDSSGlobalHelper.SetActiveFuseObj(val: TFuseObj); begin FActiveFuseObj := val; end;
-procedure TDSSGlobalHelper.SetActiveGICTransformerObj(val: TGICTransformerObj); begin FActiveGICTransformerObj := val; end;
-procedure TDSSGlobalHelper.SetActiveLineObj(val: TLineObj); begin FActiveLineObj := val; end;
-procedure TDSSGlobalHelper.SetActiveReactorObj(val: TReactorObj); begin FActiveReactorObj := val; end;
-procedure TDSSGlobalHelper.SetActiveTransfObj(val: TTransfObj); begin FActiveTransfObj := val; end;
end.
\ No newline at end of file
diff --git a/src/Common/Diakoptics.pas b/src/Common/Diakoptics.pas
index d963a5e5a..404aff810 100644
--- a/src/Common/Diakoptics.pas
+++ b/src/Common/Diakoptics.pas
@@ -1,736 +1,711 @@
unit Diakoptics;
-{
- ----------------------------------------------------------
- Copyright (c) 2008-2019, Electric Power Research Institute, Inc.
- All rights reserved.
- ----------------------------------------------------------
-}
-{
+// Copyright (c) 2008-2021, Electric Power Research Institute, Inc.
+// All rights reserved.
-}
interface
uses
- DSSClass, Circuit, Solution, DSSGlobals, SysUtils, DSSClassDefs, EnergyMeter,
- SolutionAlgs, Line,
- {$IFDEF FPC}CmdForms{$ELSE}DSSForms, ScriptEdit{$ENDIF};
-
-Function Solve_Diakoptics(DSS: TDSSContext):Integer;
-Function ADiakoptics_Tearing(DSS: TDSSContext): Integer;
+ DSSClass,
+ Circuit,
+ Solution,
+ SysUtils,
+ DSSClassDefs,
+ EnergyMeter,
+ SolutionAlgs,
+ Line,
+ CmdForms;
+
+function Solve_Diakoptics(DSS: TDSSContext): Integer;
+function ADiakoptics_Tearing(DSS: TDSSContext; AddISrc: Boolean): Integer;
procedure ADiakopticsInit(DSS: TDSSContext);
-function Calc_C_Matrix(DSS: TDSSContext; PLinks : PString; NLinks : Integer):Integer;
-function Calc_ZLL(DSS: TDSSContext; PLinks : PString; NLinks : Integer):Integer;
-procedure Calc_ZCC(DSS: TDSSContext; Links : Integer);
+function Calc_C_Matrix(DSS: TDSSContext; PLinks: PString; NLinks: Integer): Integer;
+function Calc_ZLL(DSS: TDSSContext; PLinks: PString; NLinks: Integer): Integer;
+procedure Calc_ZCC(DSS: TDSSContext; Links: Integer);
procedure Calc_Y4(DSS: TDSSContext);
procedure SendIdx2Actors(DSS: TDSSContext);
-Function get_Statistics(DSS: TDSSContext): String;
+function get_Statistics(DSS: TDSSContext): String;
implementation
-Uses
- DSSHelper, ExecHelper, Executive, ParserDel, YMatrix, KLUSolve, Ucomplex, Sparse_Math,
- UcMatrix, math,
- Dynamics; // For TSolveMode
-
-
-
-{*******************************************************************************
-* This is the A-Diakoptics algorithm executed by the *
-* Coordinator (Actor = 1) *
-*******************************************************************************}
-
-Function Solve_Diakoptics(DSS: TDSSContext):Integer;
+uses
+ DSSGlobals,
+ DSSHelper,
+ ExecHelper,
+ Executive,
+ YMatrix,
+ KLUSolve,
+ UComplex,
+ DSSUcomplex,
+ Sparse_Math,
+ UcMatrix,
+ math,
+ Dynamics; // For TSolveMode
+
+// This is the A-Diakoptics algorithm executed by the Coordinator (Actor = 1)
+function Solve_Diakoptics(DSS: TDSSContext): Integer;
var
- i,
- j,
- k : Integer;
- Vpartial : TSparse_Complex; //TODO: migrate to KLUSolveX
-Begin
- {Space left empty to implement the simplified Diakoptics algorithm}
- With DSS.ActiveCircuit, Solution do
- Begin
- // This is the coordinator actor in A-Diakoptics mode
- FOR i := 1 TO NumberOfTimes Do
- Begin
- if AD_Init then
- Begin
- // Loads the partial solution considering the previous iteration
- VPartial := Contours.Transpose();
- VPartial := Vpartial.multiply(V_0);
- Vpartial := Y4.multiply(VPartial);
- Ic := Contours.multiply(VPartial);
- // Ready to go
- End
- else
- Begin
- // Setups the other actors to match the options of the coordinator
- for j := 1 to DSS.NumOfActors - 1 do
- Begin
- DSS.Children[j].ActiveCircuit.Solution.Mode := Mode;
- DSS.Children[j].ActiveCircuit.solution.DynaVars.h := DynaVars.h;
- DSS.Children[j].ActiveCircuit.solution.DynaVars.intHour := DynaVars.intHour;
- DSS.Children[j].ActiveCircuit.solution.DynaVars.t := DynaVars.t;
- DSS.Children[j].ActiveCircuit.solution.MaxIterations := MaxIterations;
- DSS.Children[j].ActiveCircuit.solution.MaxControlIterations := MaxControlIterations;
- DSS.Children[j].ActiveCircuit.solution.ControlMode := ControlMode;
- DSS.Children[j].ActiveCircuit.solution.NumberOfTimes := 1;
- End;
- AD_Init := True;
- End;
- // Starts the simulation
- for j := 1 to DSS.NumOfActors - 1 do
- Begin
- DSS.CmdResult := DSS.Children[j].DSSExecutive.DoSolveCmd();
- End;
- Wait4Actors(DSS, AD_Actors);
- // The other routines
- DSS.ActorPctProgress := (i*100) div NumberofTimes;
- if DSS.SolutionAbort then
- break
- else
- DSS.ActiveCircuit.IsSolved := True;
- Increment_time;
- End;
- End;
-
- if DSS.SolutionAbort then
- DSS.ActiveCircuit.IsSolved := False;
- DSS.ActiveChild := DSS;
- DSS.ActiveChildIndex := 0;
- Result := 0;
-End;
-
-{*******************************************************************************
-* Returns a string with the partitioning statistics *
-* It only works if the partitioning was succesful *
-*******************************************************************************}
-Function get_Statistics(DSS: TDSSContext): String;
+ i, myRow: Integer;
+ Vpartial: TSparse_Complex; //TODO: migrate to KLUSolveX
+begin
+ // Space left empty to implement the simplified Diakoptics algorithm
+ with DSS.ActiveCircuit, Solution do
+ begin
+ // Solves the partial systems to find the voltages at the edges of the sub-systems
+ SendCmd2Actors(SOLVE_AD1);
+
+ Vpartial := Tsparse_Complex.Create;
+ Vpartial.sparse_matrix_Cmplx(Contours.NCols, 1);
+ // Does the voltage diff calculation using the partial results
+ myRow := 0;
+ for i := 0 to (Contours.NCols - 1) do
+ begin
+ VPartial.insert(i, 0, NodeV^[Contours.CData[myRow].Row + 1] - NodeV^[Contours.CData[myRow + 1].Row + 1]);
+ myRow := myRow + 2;
+ end;
+ // Loads the partial solution considering the previous iteration
+ Vpartial := Y4.multiply(VPartial);
+ Ic := Contours.multiply(VPartial); // Calculates the new Injecting Currents
+
+ // Commands the actors to complement the solution
+ SendCmd2Actors(SOLVE_AD2);
+ end;
+ DSS.ActiveCircuit.IsSolved := TRUE;
+ DSS.ActiveCircuit.BusNameRedefined := FALSE;
+ if DSS.SolutionAbort then
+ DSS.ActiveCircuit.IsSolved := FALSE;
+ DSS.ActiveChild := DSS;
+ DSS.ActiveChildIndex := 0;
+ Result := 0;
+ Vpartial.Free;
+end;
+
+// Returns a string with the partitioning statistics
+// It only works if the partitioning was succesful
+function get_Statistics(DSS: TDSSContext): String;
var
- unbalance,
- ASize : Array of single;
- idx : Integer;
- GReduct,
- MaxImbal,
- AvgImbal : Double;
-Begin
- setlength(ASize,0);
- for idx := 1 to DSS.NumOfActors-1 do
- Begin
- setlength(ASize,length(ASize) + 1);
- ASize[high(ASize)] := DSS.Children[idx].ActiveCircuit.NumNodes;
- End;
- GReduct := (1 - (MaxValue(ASize)/DSS.ActiveCircuit.NumNodes))*100; // The biggest actor
- setlength(unbalance,length(ASize));
- for idx := 0 to High(ASize) do
- unbalance[idx] := (1 - (ASize[idx]/MaxValue(ASize))) * 100; // All the unbalances
- MaxImbal := MaxValue(unbalance); // Max imbalance
- AvgImbal := mean(unbalance); // Average
+ unbalance,
+ ASize: array of Single;
+ idx: Integer;
+ GReduct,
+ MaxImbal,
+ AvgImbal: Double;
+begin
+ setlength(ASize, 0);
+ for idx := 1 to DSS.NumOfActors - 1 do
+ begin
+ setlength(ASize, length(ASize) + 1);
+ ASize[high(ASize)] := DSS.Children[idx].ActiveCircuit.NumNodes;
+ end;
+ GReduct := (1 - (MaxValue(ASize) / DSS.ActiveCircuit.NumNodes)) * 100; // The biggest actor
+ setlength(unbalance, length(ASize));
+ for idx := 0 to High(ASize) do
+ unbalance[idx] := (1 - (ASize[idx] / MaxValue(ASize))) * 100; // All the unbalances
+ MaxImbal := MaxValue(unbalance); // Max imbalance
+ AvgImbal := mean(unbalance); // Average
// publishes the results
- Result := CRLF +
- 'Circuit reduction (%): ' + floattostrf(GReduct, ffgeneral, 4 ,2) + CRLF +
- 'Max imbalance (%): ' + floattostrf(MaxImbal, ffgeneral, 4 ,2) + CRLF +
- 'Average imbalance(%): ' + floattostrf(AvgImbal, ffgeneral, 4 ,2) + CRLF;
-End;
+ Result := CRLF +
+ Format(_('Circuit reduction (%%): %4.2f'), [GReduct]) + CRLF +
+ Format(_('Max imbalance (%%): %4.2f'), [MaxImbal]) + CRLF +
+ Format(_('Average imbalance(%): %4.2f'), [AvgImbal]) + CRLF;
+end;
-{*******************************************************************************
-* Sets the memory index for each actor so they can write *
-* directly into the coordinator's Voltage vector *
-*******************************************************************************}
+// Sets the memory index for each actor so they can write
+// directly into the coordinator's Voltage vector
procedure SendIdx2Actors(DSS: TDSSContext);
-VAR
- i,j,k : Integer;
- BusName : String;
- AllNNames : Array of String;
-Begin
+var
+ i, j: Integer;
+ BusName: String;
+ AllNNames: array of String;
+begin
// Gets the names of the nodes in the interconnected system
- setlength(AllNNames,0);
- With DSS.ActiveCircuit do
- Begin
- FOR i := 1 to NumBuses DO
- Begin
- BusName := BusList.Get(i);
- FOR j := 1 to Buses^[i].NumNodesThisBus DO
- Begin
- setlength(AllNNames,(length(AllNNames) + 1));
+ setlength(AllNNames, 0);
+ with DSS.ActiveCircuit do
+ begin
+ for i := 1 to NumBuses do
+ begin
+ BusName := BusList.NameOfIndex(i);
+ for j := 1 to Buses^[i].NumNodesThisBus do
+ begin
+ setlength(AllNNames, (length(AllNNames) + 1));
AllNNames[high(AllNNames)] := BusName + '.' + IntToStr(Buses^[i].GetNum(j));
- End;
- End;
- End;
-// Sets the index for each actor
-// The feeder head first
- DSS.ActiveCircuit.VIndex := 0;
-// Then checks the rest of the actors
- for i := 1 to DSS.NumOfActors - 1 do
- Begin
- BusName := DSS.Children[i].ActiveCircuit.BusList.Get(1) + '.1';
- // Looks for the node within all the Node Names in the interconnected model
- for j := 0 to High(AllNNames) do
- if BusName = AllNNames[j] then
- Break;
- DSS.Children[i].ActiveCircuit.VIndex := j;
- End;
- // Initializes the Ic vector with zeros
- DSS.ActiveCircuit.Ic.sparse_matrix_Cmplx(length(AllNNames),1);
- DSS.ActiveCircuit.V_0.sparse_matrix_Cmplx(length(AllNNames),1);
- for i := 0 to High(AllNNames) do
- Begin
- DSS.ActiveCircuit.Ic.Insert(i,0,cZERO);
- DSS.ActiveCircuit.V_0.Insert(i,0,cZERO);
- End;
-End;
-
-{*******************************************************************************
-* Inverts ZCC to obtain its admittance equivalent Y4 *
-* This is the heart of ADiakoptics *
-*******************************************************************************}
+ end;
+ end;
+ end;
+ // Sets the index for each actor
+ // The feeder head first
+ DSS.Children[1].ActiveCircuit.VIndex := 0;
+ // Then checks the rest of the actors
+ for i := 2 to DSS.NumOfActors - 1 do
+ begin
+ BusName := DSS.Children[i].ActiveCircuit.BusList.NameOfIndex(1) + '.1';
+ // Looks for the node within all the Node Names in the interconnected model
+ for j := 0 to High(AllNNames) do
+ if BusName = AllNNames[j] then
+ Break;
+ DSS.Children[i].ActiveCircuit.VIndex := j;
+ end;
+ // Initializes the Ic vector with zeros
+ DSS.ActiveCircuit.Ic.sparse_matrix_Cmplx(length(AllNNames), 1);
+ // DSS.ActiveCircuit.V_0.sparse_matrix_Cmplx(length(AllNNames), 1);
+ for i := 0 to High(AllNNames) do
+ begin
+ DSS.ActiveCircuit.Ic.Insert(i, 0, cZERO);
+ // DSS.ActiveCircuit.V_0.Insert(i, 0, cZERO);
+ end;
+end;
+
+// Inverts ZCC to obtain its admittance equivalent Y4
+// This is the heart of A-Diakoptics
procedure Calc_Y4(DSS: TDSSContext);
var
- value : Complex;
- NumRows,
- NumCols,
- col,
- idx : Integer;
- TempMat : TcMatrix;
-Begin
- WITH DSS.ActiveCircuit, Solution DO
- Begin
- // Moves ZCC into an equivalent compatible with TcMatrix
- TempMat := TCmatrix.CreateMatrix(ZCC.NRows);
- for idx := 0 to High(ZCC.CData) do
- Begin
- TempMat.SetElement(ZCC.CData[idx].Row + 1,ZCC.CData[idx].Col + 1,ZCC.CData[idx].Value);
- End;
- // Inverts the ZCC equivalent
- TempMat.Invert;
- Y4.sparse_matrix_Cmplx(ZCC.NRows,ZCC.NCols);
- NumRows := ZCC.NRows - 1;
- NumCols := ZCC.NCols - 1;
+ value: Complex;
+ NumRows,
+ NumCols,
+ col,
+ idx: Integer;
+ TempMat: TcMatrix;
+begin
+ with DSS.ActiveCircuit, Solution do
+ begin
+ // Moves ZCC into an equivalent compatible with TcMatrix
+ TempMat := TCmatrix.CreateMatrix(ZCC.NRows);
+ for idx := 0 to High(ZCC.CData) do
+ begin
+ TempMat.SetElement(ZCC.CData[idx].Row + 1, ZCC.CData[idx].Col + 1, ZCC.CData[idx].Value);
+ end;
+ // Inverts the ZCC equivalent
+ TempMat.Invert;
+ Y4.sparse_matrix_Cmplx(ZCC.NRows, ZCC.NCols);
+ NumRows := ZCC.NRows - 1;
+ NumCols := ZCC.NCols - 1;
// Moves the inverse into Y4 for furhter use
- for idx := 0 to NumRows do
- Begin
- for col := 0 to NumCols do
- Begin
- value := TempMat.GetElement(idx+1,col+1);
- if (value.re <> 0) and (value.re <> 0) then
- Y4.insert(idx,col,value);
- End;
- End;
- End;
-End;
-
-{*******************************************************************************
-* Calculates the Connections matrix ZCC in the *
-* contours-contours domain *
-*******************************************************************************}
-procedure Calc_ZCC(DSS: TDSSContext; Links : Integer);
+ for idx := 0 to NumRows do
+ begin
+ for col := 0 to NumCols do
+ begin
+ value := TempMat.GetElement(idx + 1, col + 1);
+ if (value.re <> 0) and (value.re <> 0) then
+ Y4.insert(idx, col, value);
+ end;
+ end;
+ end;
+end;
+
+// Calculates the Connections matrix ZCC in the
+// contours-contours domain
+procedure Calc_ZCC(DSS: TDSSContext; Links: Integer);
var
- row,
- col,
- idx3,
- idx2,
- idx : Integer;
- NNodes : LongWord;
- CVector,
- ZVector : pComplexArray;
- Ctemp : Complex;
-Begin
- WITH DSS.ActiveCircuit, Solution DO
- Begin
- GetSize(hY, @NNodes);
- col := NNodes;
- dec(Links);
- ZCT.sparse_matrix_Cmplx(Links*3,col);
- CVector := Allocmem(SizeOf(CVector^[1]) * (col+1));
- ZVector := Allocmem(SizeOf(ZVector^[1]) * (col+1));
- idx3 := Links*3 - 1;
-
- for idx2 := 0 to idx3 do
- Begin
-
- for idx := 1 to col do CVector^[idx] := cZERO; // Makes it zero
-
- for idx := 1 to length(Contours.CData) do
- Begin
- if Contours.CData[idx - 1].col = idx2 then
- Begin
- row := Contours.CData[idx - 1].row + 1;
- CVector^[row] := Contours.CData[idx-1].Value;
- End;
- End;
- SolveSparseSet(hy,@ZVector^[1],@CVector^[1]);
-
- for idx := 1 to col do // inserts result into the ZCT matrix
- Begin
- CTemp := ZVector^[idx];
- if (CTemp.re <> 0) and (CTemp.im <> 0) then
- ZCT.insert(idx2,(idx-1),ZVector[idx]);
- End;
- idx := col;
- End;
-
- ZCC := ZCT.multiply(Contours); // Calculates ZCC with no Link impedances
- ZCC := ZCC.Add(ZLL); // Adds the link impedance
-
- FreeMem(CVector);
- FreeMem(ZVector);
- End;
-End;
-
-{*******************************************************************************
-* Calculates the contours matrix based *
-* on the location in the graph of the link branches *
-* if there is an error returns <> 0 *
-*******************************************************************************}
-function Calc_C_Matrix(DSS: TDSSContext; PLinks : PString; NLinks : Integer):Integer;
+ row,
+ col,
+ idx3,
+ idx2,
+ idx: Integer;
+ NNodes: Longword;
+ CVector,
+ ZVector: pComplexArray;
+ Ctemp: Complex;
+begin
+ with DSS.ActiveCircuit, Solution do
+ begin
+ GetSize(hY, @NNodes);
+ col := NNodes;
+ dec(Links);
+ ZCT.sparse_matrix_Cmplx(col, Links * 3);
+ CVector := Allocmem(SizeOf(CVector^[1]) * (col + 1));
+ ZVector := Allocmem(SizeOf(ZVector^[1]) * (col + 1));
+ idx3 := Links * 3 - 1;
+
+ for idx2 := 0 to idx3 do
+ begin
+ for idx := 1 to col do
+ CVector^[idx] := cZERO; // Makes it zero
+
+ for idx := 1 to length(Contours.CData) do
+ begin
+ if Contours.CData[idx - 1].col = idx2 then
+ begin
+ row := Contours.CData[idx - 1].row + 1;
+ CVector^[row] := Contours.CData[idx - 1].Value;
+ end;
+ end;
+ SolveSparseSet(hy, pComplexArray(@ZVector^[1]), pComplexArray(@CVector^[1]));
+
+ for idx := 1 to col do // inserts result into the ZCT matrix
+ begin
+ CTemp := ZVector^[idx];
+ if (CTemp.re <> 0) and (CTemp.im <> 0) then
+ ZCT.insert((idx - 1), idx2, ZVector[idx]);
+ end;
+ idx := col;
+ end;
+ // At this point we have calculated the right side of the equation
+ // ZCC = CTZ(TT)C -> Z(TT)C
+ // It is needed transpose the contours matrix and multiply it
+ ContoursT := Contours.Transpose();
+ ZCC := ContoursT.multiply(ZCT); // Calculates ZCC with no Link impedances
+ ZCC := ZCC.Add(ZLL); // Adds the link impedance
+
+ FreeMem(CVector);
+ FreeMem(ZVector);
+ end;
+end;
+
+// Calculates the contours matrix based
+// on the location in the graph of the link branches
+// if there is an error returns <> 0
+function Calc_C_Matrix(DSS: TDSSContext; PLinks: PString; NLinks: Integer): Integer;
var
- LIdx,k,l,
- j,CDirection,
- NumPhases,
- i : Integer;
- Elem_Buses,
- Node_Names : Array of String;
- temp : String;
- Go_Flag : Boolean;
-Begin
- DSS := DSS.GetPrime();
- DSS.ActiveChild := DSS;
- DSS.ActiveChildIndex := 0;
- WITH DSS.ActiveCircuit DO
- Begin
- Result := 0;
- setlength(Elem_Buses,2);
- setlength(Node_Names,0);
- FOR i := 1 to NumNodes DO
- Begin
- setlength(Node_Names,(length(Node_Names) + 1));
- With MapNodeToBus^[i] do
- Node_Names[High(Node_names)] := Format('%s.%-d',[lowercase(BusList.Get(Busref)), NodeNum]);
- End;
-
- Contours.sparse_matrix_Cmplx(length(Node_Names),(NLinks - 1)*3);
-
- for LIdx := 1 to (NLinks - 1) do
- Begin
-
- inc(PLinks); // Pointing to the Next link branch (starting in 1)
- temp := string(PLinks^);
- j := ansipos('.',temp);
- temp := lowercase(copy(temp,0,(j - 1)));
- if (temp = 'line') then
- Begin
- i := SetElementActive(string(PLinks^));
- // Gest the names of the buses fot this PDElement
- // If it is something different from a Transformer reports an error
- // Since a link branch cannot be a transformer
- For i := 1 to ActiveCktElement.Nterms Do
- Begin
- Elem_Buses[i-1] := ActiveCktElement.GetBus(i);
- j := ansipos('.',Elem_Buses[i-1]);
- if j <> 0 then
- Elem_Buses[i-1] := copy(Elem_Buses[i-1],0,j)
- else
- Elem_Buses[i-1] := Elem_Buses[i-1] + '.';
- End;
- // Marks the connection point in the contours matrix
- NumPhases := ActiveCktElement.NPhases;
- For l := 1 to NumPhases Do
- Begin
-
- for i := 0 to 1 do
- Begin
-
- temp := Elem_Buses[i] + inttostr(l);
- Go_Flag := True;
- j := 0;
- while Go_Flag and (j <= High(Node_Names)) do
- Begin
-
- k := ansipos(temp,Node_Names[j]);
- if k <> 0 then
- Begin
- if i = 0 then CDirection := 1
- else CDirection := -1;
- Contours.insert(j,((l-1) + (LIdx -1)*3),cmplx(CDirection,0));
- Go_Flag := False;
- End;
- inc(j);
-
- End;
-
- End;
- End;
- End
- else
- Begin
- Result := -1; // There was an error when selecting the link branches (MeTIS)
- break; // Abort
- End;
- End;
- // More error checking
- if Result = 0 then
- if Contours.NZero <> 0 then Result := 0
- else Result := 1;
-
- End;
-End;
-
-{*******************************************************************************
-* Calculates the Link branches matrix for further use *
-* if there is an error returns <> 0 *
-*******************************************************************************}
-Function Calc_ZLL(DSS: TDSSContext; PLinks : PString; NLinks : Integer):Integer;
+ LIdx, k, l,
+ j, CDirection,
+ NumPhases,
+ i: Integer;
+ Elem_Buses,
+ Node_Names: array of String;
+ temp: String;
+ Go_Flag: Boolean;
+begin
+ DSS := DSS.GetPrime();
+ DSS.ActiveChild := DSS;
+ DSS.ActiveChildIndex := 0;
+ with DSS.ActiveCircuit do
+ begin
+ Result := 0;
+ setlength(Elem_Buses, 2);
+ setlength(Node_Names, 0);
+ for i := 1 to NumNodes do
+ begin
+ setlength(Node_Names, (length(Node_Names) + 1));
+ with MapNodeToBus^[i] do
+ Node_Names[High(Node_names)] := Format('%s.%-d', [AnsiLowerCase(BusList.NameOfIndex(Busref)), NodeNum]);
+ end;
+
+ Contours.sparse_matrix_Cmplx(length(Node_Names), (NLinks - 1) * 3);
+
+ for LIdx := 1 to (NLinks - 1) do
+ begin
+ inc(PLinks); // Pointing to the Next link branch (starting in 1)
+ temp := String(PLinks^);
+ j := ansipos('.', temp);
+ temp := AnsiLowerCase(copy(temp, 0, (j - 1)));
+ if (temp = 'line') then
+ begin
+ i := SetElementActive(String(PLinks^));
+ // Get the names of the buses fot this PDElement
+ // If it is something different from a Transformer reports an error
+ // Since a link branch cannot be a transformer
+ for i := 1 to ActiveCktElement.Nterms do
+ begin
+ Elem_Buses[i - 1] := ActiveCktElement.GetBus(i);
+ j := ansipos('.', Elem_Buses[i - 1]);
+ if j <> 0 then
+ Elem_Buses[i - 1] := copy(Elem_Buses[i - 1], 0, j)
+ else
+ Elem_Buses[i - 1] := Elem_Buses[i - 1] + '.';
+ end;
+ // Marks the connection point in the contours matrix
+ NumPhases := ActiveCktElement.NPhases;
+ for l := 1 to NumPhases do
+ begin
+ for i := 0 to 1 do
+ begin
+ temp := Elem_Buses[i] + inttostr(l);
+ Go_Flag := TRUE;
+ j := 0;
+ while Go_Flag and (j <= High(Node_Names)) do
+ begin
+ k := ansipos(temp, Node_Names[j]);
+ if k <> 0 then
+ begin
+ if i = 0 then
+ CDirection := 1
+ else
+ CDirection := -1;
+ Contours.insert(j, ((l - 1) + (LIdx - 1) * 3), cmplx(CDirection, 0));
+ Go_Flag := FALSE;
+ end;
+ inc(j);
+
+ end;
+
+ end;
+ end;
+ end
+ else
+ begin
+ Result := -1; // There was an error when selecting the link branches (MeTIS)
+ break; // Abort
+ end;
+ end;
+ // More error checking
+ if Result = 0 then
+ if Contours.NZero <> 0 then
+ Result := 0
+ else
+ Result := 1;
+ end;
+end;
+
+// Calculates the Link branches matrix for further use
+// if there is an error returns <> 0
+function Calc_ZLL(DSS: TDSSContext; PLinks: PString; NLinks: Integer): Integer;
var
- NValues,
- idx, k, j,
- row,col,
- count,
- i : Integer;
- cValues : pComplexArray;
- ErrorFlag : Boolean;
- localMat : TSparse_Complex;
- LinkPrim : TcMatrix;
-Begin
- dec(NLinks);
- ErrorFlag := False;
- LinkPrim := TCmatrix.CreateMatrix(3);
- DSS := DSS.GetPrime();
- DSS.ActiveChild := DSS;
- DSS.ActiveChildIndex := 0;
- WITH DSS.ActiveCircuit DO
- Begin
- ZLL.Sparse_matrix_Cmplx(NLinks*3,NLinks*3);
- for i := 1 to NLinks do
- Begin
- inc(PLinks);
- idx := SetElementActive(string(PLinks^));
-
- If ActiveCktElement<>Nil THEN
- WITH ActiveCktElement Do
- Begin
- NValues := SQR(Yorder);
- cValues := GetYprimValues(ALL_YPRIM); // Get pointer to complex array of values
- If cValues <> Nil Then
- Begin
- k := 1;
- idx := (i-1)*3;
- row := 1;
- col := 1;
- count := 0;
- // Extracts the YPrim of the Link branch
- FOR j := 1 to (NValues div 4) DO
- Begin
- LinkPrim.SetElement(row,col,cValues^[k]);
- inc(count);
- if count > 2 then
- Begin
- inc(row);
- Col := 1;
- Count := 0;
- k := k + 4;
- End
- else
- Begin
- inc(Col);
- inc(k);
- End;
- End;
- // Inverts the Y primitive
- LinkPrim.Invert;
- // Inserts the Z primitive values into ZLL
- row := 0;
- col := 0;
- count := 0;
- FOR j := 1 to (NValues div 4) DO
- Begin
- ZLL.insert((row + idx),(col + idx),LinkPrim.GetElement(row+1,col+1));
- inc(count);
- if count > 2 then
- Begin
- inc(row);
- Col := 0;
- Count := 0;
- End
- else
- inc(Col);
- End;
- End
- else
- ErrorFlag := True;
-
- End
-
- End;
- if ErrorFlag then Result := 1
- else Result := 0;
-
- End;
-End;
-
-
-{*******************************************************************************
-* Tears the system using considering the number of *
-* circuits specified by the user *
-*******************************************************************************}
-Function ADiakoptics_Tearing(DSS: TDSSContext): Integer;
+ NValues,
+ idx, k, j,
+ row, col,
+ count,
+ i: Integer;
+ cValues: pComplexArray;
+ ErrorFlag: Boolean;
+ LinkPrim: TcMatrix;
+begin
+ dec(NLinks);
+ ErrorFlag := FALSE;
+ LinkPrim := TCmatrix.CreateMatrix(3);
+ DSS := DSS.GetPrime();
+ DSS.ActiveChild := DSS;
+ DSS.ActiveChildIndex := 0;
+ with DSS.ActiveCircuit do
+ begin
+ ZLL.Sparse_matrix_Cmplx(NLinks * 3, NLinks * 3);
+ for i := 1 to NLinks do
+ begin
+ inc(PLinks);
+ idx := SetElementActive(String(PLinks^));
+
+ if ActiveCktElement <> NIL then
+ with ActiveCktElement do
+ begin
+ NValues := SQR(Yorder);
+ cValues := GetYprimValues(ALL_YPRIM); // Get pointer to complex array of values
+ if cValues <> NIL then
+ begin
+ k := 1;
+ idx := (i - 1) * 3;
+ row := 1;
+ col := 1;
+ count := 0;
+ // Extracts the YPrim of the Link branch
+ for j := 1 to (NValues div 4) do
+ begin
+ LinkPrim.SetElement(row, col, cValues^[k]);
+ inc(count);
+ if count > 2 then
+ begin
+ inc(row);
+ Col := 1;
+ Count := 0;
+ k := k + 4;
+ end
+ else
+ begin
+ inc(Col);
+ inc(k);
+ end;
+ end;
+ // Inverts the Y primitive
+ LinkPrim.Invert;
+ // Inserts the Z primitive values into ZLL
+ row := 0;
+ col := 0;
+ count := 0;
+ for j := 1 to (NValues div 4) do
+ begin
+ ZLL.insert((row + idx), (col + idx), LinkPrim.GetElement(row + 1, col + 1));
+ inc(count);
+ if count > 2 then
+ begin
+ inc(row);
+ Col := 0;
+ Count := 0;
+ end
+ else
+ inc(Col);
+ end;
+ end
+ else
+ ErrorFlag := TRUE;
+
+ end
+
+ end;
+ if ErrorFlag then
+ Result := 1
+ else
+ Result := 0;
+
+ end;
+end;
+
+
+// Tears the system using considering the number of
+// circuits specified by the user
+// The flag AddISrc indicates if its necessary to create
+// Isources at the edges of the link branches, the ISource
+// magnitude is equal to 0.000001, angle 0 (for A-Diakoptics)
+function ADiakoptics_Tearing(DSS: TDSSContext; AddISrc: Boolean): Integer;
var
- Prev_Mode: TSolveMode; // Stores the previous solution mode
- Num_Ckts : Integer; // Stores the number of Sub-Circuits created
-Begin
- DSS := DSS.GetPrime();
- DSS.ActiveChild := DSS;
- DSS.ActiveChildIndex := 0;
-
- With DSS.ActiveCircuit, Solution do
- Begin
- Num_Ckts := Tear_Circuit();
- Prev_mode := Dynavars.SolutionMode;
- Dynavars.SolutionMode := TSolveMode.SNAPSHOT;
- DSS.DSSExecutive.Command := 'set controlmode=off';
- Ymatrix.BuildYMatrix(DSS, WHOLEMATRIX, FALSE);
-// DoSolveCmd();
- if not DSS.SolutionAbort then
- Begin
- Save_SubCircuits();
- Dynavars.SolutionMode := Prev_mode; // Goes back to the previous solution mode
- DSS.ActiveCircuit.Num_SubCkts := Num_Ckts;
- DSS.GlobalResult := 'Sub-Circuits Created: ' + inttostr(Num_Ckts);
- Result := 0;
- End
- else
- Begin
- DSS.GlobalResult := 'There was an error when tearing the circuit ';
- Result := 1;
- End;
- End;
-End;
-
-{*******************************************************************************
-* Generates the subsystems, actors and memory space *
-* For using the A-Diakoptics parallelism *
-*******************************************************************************}
+ Prev_Mode: TSolveMode; // Stores the previous solution mode
+ Num_Ckts: Integer; // Stores the number of Sub-Circuits created
+begin
+ with DSS.ActiveCircuit, Solution do
+ begin
+ Num_Ckts := Tear_Circuit();
+ Prev_mode := Dynavars.SolutionMode;
+ Dynavars.SolutionMode := TSolveMode.SNAPSHOT;
+ DSS.DSSExecutive.Command := 'set controlmode=off';
+ Ymatrix.BuildYMatrix(DSS, WHOLEMATRIX, FALSE);
+ // DoSolveCmd();
+ if not DSS.SolutionAbort then
+ begin
+ Save_SubCircuits(AddISrc);
+ Dynavars.SolutionMode := Prev_mode; // Goes back to the previous solution mode
+ DSS.ActiveCircuit.Num_SubCkts := Num_Ckts;
+ DSS.GlobalResult := Format(_('Sub-Circuits Created: %d'), [Num_Ckts]);
+ Result := 0;
+ end
+ else
+ begin
+ DSS.GlobalResult := _('There was an error when tearing the circuit ');
+ Result := 1;
+ end;
+ end;
+end;
+
+// Generates the subsystems, actors and memory space
+// For using the A-Diakoptics parallelism
procedure ADiakopticsInit(DSS: TDSSContext);
var
- EMeter : TEnergyMeterObj;
- j,
- Local_State,
- Num_States,
- ErrorCode,
- DIdx,
- Diak_Actors : Integer;
- Dir, Proj_Dir,
- prog_Str,
- ErrorStr,
- FileRoot : String;
- Links : Array of String; // List of the Link Branches
- MQuit : Boolean; // To quit the State Machine
- {$IFNDEF FPC}
- ScriptEd : TScriptEdit;
- {$ENDIF}
-
-Begin
- // The program is built as a state machine to facilitate the error detection
- // and quitting the routines after an error is detected wihtout killing the prog
- MQuit := False;
- Num_States := 9; // Number of states of the machine
- Local_State := 0; // Current state
- prog_str := 'A-Diakoptics initialization summary:' + CRLF + CRLF;
-
- DSS := DSS.GetPrime();
- DSS.ActiveChild := DSS;
- DSS.ActiveChildIndex := 0;
-
- // Checks if the number of actors is within a reasonable limit
- if DSS.ActiveCircuit.Num_SubCkts > (CPU_Cores - 2) then
- DSS.ActiveCircuit.Num_SubCkts := CPU_Cores - 2;
-
- while(not MQuit) do
- Begin
- case Local_State of
- 0: Begin // Create subcircuits
-
- prog_Str := prog_str + '- Creating Sub-Circuits...' + CRLF;
-
- ErrorCode := ADiakoptics_Tearing(DSS);
- if ErrorCode <> 0 then
- ErrorStr := 'Error' + CRLF + 'The circuit cannot be decomposed' + CRLF
- else
- ErrorStr := ' ' + inttostr(DSS.ActiveCircuit.Num_SubCkts) + ' Sub-Circuits Created' + CRLF;
-
- prog_Str := prog_str + ErrorStr;
-
- End;
- 1: Begin // Saves the Link Branch list locally
-
- Diak_Actors := DSS.ActiveCircuit.Num_SubCkts + 1;
- prog_Str := prog_str + '- Indexing link branches...';
-
- setlength(Links,length(DSS.ActiveCircuit.Link_Branches));
- for DIdx := 0 to High(Links) do Links[DIdx] := DSS.ActiveCircuit.Link_Branches[DIdx];
-
- prog_Str := prog_str + 'Done';
- ErrorCode := 0; // No error handling here
-
- End;
- 2: Begin // Compile subsystems
-
- ErrorCode := 0;
- prog_Str := prog_str + CRLF + '- Setting up the Actors...';
- // Clears everything to create the actors and compile the subsystems
- DSS.Parallel_enabled := False;
- DSS.DSSExecutive.ClearAll;
- Fileroot := OutputDirectory {CurrentDSSDir}; // Gets the current directory
- DSS.SolutionAbort := False;
-
- // Compiles the interconnected Circuit for further calculations on actor 1
- ActiveActor := 1;
- Proj_Dir := 'compile "' + Fileroot + 'Torn_Circuit' + PathDelim + 'master_interconnected.dss"';
- DSS.DssExecutive.Command := 'set controlmode=Off';
- // Disables the Energymeters for the zones
- with DSS.ActiveCircuit, Solution do
- Begin
- EMeter := EnergyMeters.First;
- while EMeter <> Nil do
- begin
- j := ansipos('zone_',EMeter.Name);
- if j <> 0 then EMeter.Enabled := False;
- EMeter := EnergyMeters.Next;
- end;
- End;
- Ymatrix.BuildYMatrix(DSS, WHOLEMATRIX, FALSE);
- DSS.DSSExecutive.DoSolveCmd();
-
- // Creates the other actors
- for DIdx := 2 to Diak_Actors do
- Begin
- //New_Actor_Slot();
-
- if DIdx = 2 then
- Dir := ''
- else
- Dir := 'zone_' + inttostr(DIdx - 1) + PathDelim;
-
- Proj_Dir := 'compile "' + Fileroot + PathDelim + 'Torn_Circuit' + PathDelim + Dir + 'master.dss"';
- DSS.DssExecutive.Command := Proj_Dir;
- if DIdx > 2 then
- DSS.DssExecutive.Command := Links[DIdx - 2] + '.enabled=False';
-
- DSS.DssExecutive.Command := 'set controlmode=Off';
- DSS.DssExecutive.DoSolveCmd();
- if DSS.SolutionAbort then
- Begin
- ErrorCode := 1;
- Break;
- End;
- End;
- if ErrorCode <> 0 then ErrorStr := 'Error' + CRLF
- + 'One or sub-systems cannot be compiled' + CRLF
- else ErrorStr := 'Done';
- prog_Str := prog_str + ErrorStr;
-
- end;
- 3: Begin // Creates the contours matrix
- prog_Str := prog_str + CRLF + '- Building Contours...';
- // Builds the contour matrix
- ErrorCode := Calc_C_Matrix(DSS, @Links[0], length(Links));
- if ErrorCode <> 0 then ErrorStr := 'Error' + CRLF
- + 'One or more link branches are not lines' + CRLF
- else ErrorStr := 'Done';
- prog_Str := prog_str + ErrorStr;
-
- end;
- 4: Begin // Builds the ZLL matrix
-
- prog_Str := prog_str + CRLF + '- Building ZLL...';
- ErrorCode := Calc_ZLL(DSS, @Links[0],length(Links));
- if ErrorCode <> 0 then ErrorStr := 'Error'
- else ErrorStr := 'Done';
- prog_Str := prog_str + ErrorStr;
-
- end;
- 5: Begin
- // Opens the link branches in the interconnected Circuit and recalculates the YBus
- // The opening happens by replacing the line with a very high series impedance
- prog_Str := prog_str + CRLF + '- Opening link branches...';
- for DIdx := 1 to High(Links) do
- Begin
- DSS.DssExecutive.Command := Links[DIdx] + '.r0=10000000';
- DSS.DssExecutive.Command := Links[DIdx] + '.r1=10000000';
- DSS.DssExecutive.Command := Links[DIdx] + '.x0=0';
- DSS.DssExecutive.Command := Links[DIdx] + '.x1=0';
- End;
- Ymatrix.BuildYMatrix(DSS, WHOLEMATRIX, FALSE);
- prog_Str := prog_str + 'Done';
- ErrorCode := 0; // No error handling here
-
- end;
- 6: Begin // Builds the ZCC matrix
-
- prog_Str := prog_str + CRLF + '- Building ZCC...';
- Calc_ZCC(DSS, length(Links));
- prog_Str := prog_str + 'Done';
-
- End;
- 7: Begin // Inverts ZCC to get Y4
-
- prog_Str := prog_str + CRLF + '- Building Y4 ...';
- Calc_Y4(DSS);
- prog_Str := prog_str + 'Done';
- // Moves back the link branches list into actor 1 for further use
- setlength(DSS.ActiveCircuit.Link_Branches,length(Links));
- for DIdx := 0 to High(Links) do
- DSS.ActiveCircuit.Link_Branches[DIdx] := Links[DIdx];
- End;
- 8: Begin // Sends the index to the actors for uploading info
- prog_Str := prog_str + CRLF + '- Assigning indexes to actors ...';
- SendIdx2Actors(DSS);
- prog_Str := prog_str + 'Done';
- End;
- 9: Begin // Prints the statistics of the partitioning
- prog_Str := prog_str + CRLF + CRLF + 'Partitioning statistics';
- prog_Str := prog_str + get_Statistics(DSS);
- End
- else
- Begin
-
- End;
+ EMeter: TEnergyMeterObj;
+ j,
+ Local_State,
+ Num_States,
+ ErrorCode,
+ DIdx,
+ Diak_Actors: Integer;
+ Dir, Proj_Dir,
+ prog_Str,
+ ErrorStr,
+ FileRoot: String;
+ Links: array of String; // List of the Link Branches
+ MQuit: Boolean; // To quit the State Machine
+ ChDSS: TDSSContext = NIL;
+begin
+ ErrorCode := 0;
+ Diak_Actors := 0;
+ // The program is built as a state machine to facilitate the error detection
+ // and quitting the routines after an error is detected wihtout killing the prog
+ MQuit := FALSE;
+ Num_States := 9; // Number of states of the machine
+ Local_State := 0; // Current state
+ prog_str := _('A-Diakoptics initialization summary:') + CRLF + CRLF;
+
+ DSS := DSS.GetPrime();
+ DSS.ActiveChild := DSS;
+ DSS.ActiveChildIndex := 0;
+
+ // Checks if the number of actors is within a reasonable limit
+ if DSS.ActiveCircuit.Num_SubCkts > (CPU_Cores - 2) then
+ DSS.ActiveCircuit.Num_SubCkts := CPU_Cores - 2;
+
+ while (not MQuit) do
+ begin
+ case Local_State of
+ 0:
+ begin // Create subcircuits
+ prog_Str := prog_str + _('- Creating Sub-Circuits...') + CRLF;
+
+ ErrorCode := ADiakoptics_Tearing(DSS, FALSE);
+ if ErrorCode <> 0 then
+ ErrorStr := _('Error: The circuit cannot be decomposed') + CRLF
+ else
+ ErrorStr := Format(_(' %d Sub-Circuits Created'), [DSS.ActiveCircuit.Num_SubCkts]) + CRLF;
+
+ prog_Str := prog_str + ErrorStr;
+ end;
+ 1:
+ begin // Saves the Link Branch list locally
+ Diak_Actors := DSS.ActiveCircuit.Num_SubCkts + 1;
+ prog_Str := prog_str + _('- Indexing link branches...');
+
+ setlength(Links, length(DSS.ActiveCircuit.Link_Branches));
+ for DIdx := 0 to High(Links) do
+ Links[DIdx] := DSS.ActiveCircuit.Link_Branches[DIdx];
+
+ prog_Str := prog_str + _('Done');
+ ErrorCode := 0; // No error handling here
+ end;
+ 2:
+ begin // Compile subsystems
+ ErrorCode := 0;
+ prog_Str := prog_str + CRLF + _('- Setting up the Actors...');
+ // Clears everything to create the actors and compile the subsystems
+ DSS.Parallel_enabled := FALSE;
+ DSS.DSSExecutive.ClearAll;
+ Fileroot := DSS.OutputDirectory {CurrentDSSDir}; // Gets the current directory
+ DSS.SolutionAbort := FALSE;
+
+ // Compiles the interconnected Circuit for further calculations on actor 1
+ Proj_Dir := 'compile "' + Fileroot + 'Torn_Circuit' + PathDelim + 'Master_interconnected.dss"';
+ DSS.DssExecutive.Command := Proj_Dir;
+ DSS.DssExecutive.Command := 'set controlmode=Off';
+ // Disables the Energymeters for the zones
+ with DSS.ActiveCircuit, Solution do
+ begin
+ EMeter := EnergyMeters.First;
+ while EMeter <> NIL do
+ begin
+ j := ansipos('zone_', EMeter.Name);
+ if j <> 0 then
+ EMeter.Enabled := FALSE;
+ EMeter := EnergyMeters.Next;
+ end;
+ end;
+ Ymatrix.BuildYMatrix(DSS, WHOLEMATRIX, FALSE);
+ DSS.DSSExecutive.DoSolveCmd();
+
+ // Creates the other actors
+ for DIdx := 2 to Diak_Actors do
+ begin
+ New_Actor_Slot(DSS);
+
+ ChDSS := DSS.Children[DIdx - 1];
+ if DIdx = 2 then
+ Dir := ''
+ else
+ Dir := 'zone_' + inttostr(DIdx - 1) + PathDelim;
+
+ Proj_Dir := 'compile "' + Fileroot + PathDelim + 'Torn_Circuit' + PathDelim + Dir + 'Master.dss"';
+ ChDSS.DssExecutive.Command := Proj_Dir;
+ if DIdx > 2 then
+ ChDSS.DssExecutive.Command := Links[DIdx - 2] + '.enabled=False';
+
+ ChDSS.DssExecutive.Command := 'set controlmode=Off';
+ ChDSS.DssExecutive.DoSolveCmd();
+ if ChDSS.SolutionAbort then
+ begin
+ ErrorCode := 1;
+ Break;
+ end;
+ end;
+ if ErrorCode <> 0 then
+ ErrorStr := _('Error') + CRLF + _('One or sub-systems cannot be compiled') + CRLF
+ else
+ ErrorStr := _('Done');
+ prog_Str := prog_str + ErrorStr;
+ end;
+ 3:
+ begin
+ // Opens the link branches in the interconnected Circuit and recalculates the YBus
+ // The opening happens by replacing the line with a very high series impedance
+ prog_Str := prog_str + CRLF + _('- Opening link branches...');
+ for DIdx := 1 to High(Links) do
+ begin
+ DSS.ActiveCircuit.SetElementActive(String(Links[DIdx]));
+ DSS.ActiveCircuit.ActiveCktElement.Enabled := FALSE;
+ end;
+ DSS.ActiveCircuit.BusNameRedefined := FALSE;
+ Ymatrix.BuildYMatrix(DSS,WHOLEMATRIX, FALSE);
+ prog_Str := prog_str + _('Done');
+ ErrorCode := 0; // No error handling here
+ end;
+ 4:
+ begin // Creates the contours matrix
+ prog_Str := prog_str + CRLF + _('- Building Contours...');
+ // Builds the contour matrix
+ ErrorCode := Calc_C_Matrix(DSS, @Links[0], length(Links));
+ if ErrorCode <> 0 then
+ ErrorStr := _('Error') + CRLF + _('One or more link branches are not lines') + CRLF
+ else
+ ErrorStr := _('Done');
+ prog_Str := prog_str + ErrorStr;
+ end;
+ 5:
+ begin // Builds the ZLL matrix
+ prog_Str := prog_str + CRLF + _('- Building ZLL...');
+ ErrorCode := Calc_ZLL(DSS, @Links[0], length(Links));
+ if ErrorCode <> 0 then
+ ErrorStr := _('Error')
+ else
+ ErrorStr := _('Done');
+ prog_Str := prog_str + ErrorStr;
+ end;
+ 6:
+ begin // Builds the ZCC matrix
+ prog_Str := prog_str + CRLF + _('- Building ZCC...');
+ Calc_ZCC(DSS, length(Links));
+ prog_Str := prog_str + _('Done');
+ end;
+ 7:
+ begin // Inverts ZCC to get Y4
+ prog_Str := prog_str + CRLF + _('- Building Y4 ...');
+ Calc_Y4(DSS);
+ prog_Str := prog_str + _('Done');
+ // Moves back the link branches list into actor 1 for further use
+ setlength(DSS.ActiveCircuit.Link_Branches, length(Links));
+ for DIdx := 0 to High(Links) do
+ DSS.ActiveCircuit.Link_Branches[DIdx] := Links[DIdx];
+ end;
+ 8:
+ begin // Sends the index to the actors for uploading info
+ prog_Str := prog_str + CRLF + _('- Assigning indexes to actors ...');
+ SendIdx2Actors(DSS);
+ prog_Str := prog_str + _('Done');
+ end;
+ 9:
+ begin // Prints the statistics of the partitioning
+ prog_Str := prog_str + CRLF + CRLF + _('Partitioning statistics');
+ prog_Str := prog_str + get_Statistics(DSS);
+ // Assigns the processor per actor
+ for DIdx := 1 to DSS.NumOfActors do
+ begin
+ ChDSS := DSS.Children[DIdx - 1];//TODO: check
+ ChDSS.CPU := DIdx;
+ if ChDSS.ActorThread <> NIL then
+ begin
+ ChDSS.ActorThread.CPU := ChDSS.CPU;
+ // ChDSS.ActorThread.Priority := tpTimeCritical;
+ end;
+ end;
+ // Compiles the interconnected Circuit for further calculations on actor 1
+ prog_Str := prog_str + CRLF + _('- Closing link branches...');
+ for DIdx := 1 to High(Links) do
+ begin
+ DSS.ActiveCircuit.SetElementActive(String(Links[DIdx]));
+ DSS.ActiveCircuit.ActiveCktElement.Enabled := TRUE;
+ end;
+ DSS.ActiveCircuit.BusNameRedefined := FALSE;
+ Ymatrix.BuildYMatrix(DSS, WHOLEMATRIX, FALSE);
+
+ DSS.ActiveCircuit.Solution.SendCmd2Actors(INIT_ADIAKOPTICS);
+ DSS.ActiveCircuit.Solution.ADiak_init := TRUE;
+ end
+ end;
+ inc(Local_State);
+ MQuit := (Local_State > Num_States) or (ErrorCode <> 0);
+ end;
+
+ if ErrorCode <> 0 then
+ begin
+ ErrorStr := _('One or more errors found');
+ DSS.ActiveCircuit.Solution.ADiakoptics := FALSE;
+ end
+ else
+ begin
+ ErrorStr := _('A-Diakoptics initialized');
+ DSS.Parallel_enabled := TRUE;
+ DSS.ActiveCircuit.Solution.ADiakoptics := TRUE;
+ DSS.ActiveCircuit.Solution.ADiak_Init := FALSE; // Needed to force the subzones to remove VSource.Source
end;
- inc(Local_State);
- MQuit := (Local_State > Num_States) or (ErrorCode <> 0);
- End;
-
- DSS := DSS.GetPrime();
- if ErrorCode <> 0 then
- Begin
- ErrorStr := 'One or more errors found';
- DSS.ADiakoptics := False;
- End
- else
- Begin
- ErrorStr := 'A-Diakoptics initialized';
- DSS.Parallel_enabled := True;
- DSS.ADiakoptics := True;
- End;
-//TODO? ProgressCmd := True;
- prog_Str := CRLF + prog_str + CRLF + ErrorStr + CRLF;
- DSS.GlobalResult := ErrorStr;
-
- //TODO: check -- the previous GlobalResult is ignored here...
- {$IFNDEF FPC}
- if not IsDLL
- then
- ScriptEd.PublishMessage(prog_Str)
- else
- DSS.GlobalResult := prog_str;
- {$ELSE}
- DSS.GlobalResult := prog_str;
- {$ENDIF}
- // TEMc: TODO: should we report something here under FPC?
- // Davis: Done: This will add the needed report
-
- DSS.SolutionAbort := False;
-
-End;
+ //TODO? ProgressCmd := TRUE;
+ prog_Str := CRLF + prog_str + CRLF + ErrorStr + CRLF;
+ DSS.GlobalResult := ErrorStr;
+
+ //TODO: ScriptEd.PublishMessage(prog_Str)
+ //TODO: check -- the previous GlobalResult is ignored here...
+ DSS.GlobalResult := prog_str;
+ DSS.SolutionAbort := FALSE;
+end;
end.
diff --git a/src/Common/EventQueue.pas b/src/Common/EventQueue.pas
deleted file mode 100644
index 35c67aced..000000000
--- a/src/Common/EventQueue.pas
+++ /dev/null
@@ -1,14 +0,0 @@
-unit EventQueue;
-
-{
- ----------------------------------------------------------
- Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
- All rights reserved.
- ----------------------------------------------------------
-}
-
-interface
-
-implementation
-
-end.
diff --git a/src/Common/ExportCIMXML.pas b/src/Common/ExportCIMXML.pas
index 9f76d977b..6ae21aee9 100644
--- a/src/Common/ExportCIMXML.pas
+++ b/src/Common/ExportCIMXML.pas
@@ -2,20 +2,27 @@
{
----------------------------------------------------------
- Copyright (c) 2009-2015, Electric Power Research Institute, Inc.
+ Copyright (c) 2009-2021, Electric Power Research Institute, Inc.
All rights reserved.
----------------------------------------------------------
}
-{Write a CIM XML file using RDF Schema for the Common Distribution
- Power System Model, IEC 61968-13.}
+// Write a CIM XML file using RDF Schema for the Common Distribution
+// Power System Model, IEC 61968-13.
interface
uses
Classes, NamedObject, // for TUuid
DSSClass,
+ CktElement,
+ PDElement,
Transformer,
+ AutoTrans,
+ Storage2,
+ InvControl2,
+ ExpControl,
+ PVSystem2,
HashList;
@@ -26,10 +33,12 @@ interface
XfLoc, LoadLoc, LineLoc, CapLoc, Topo, ReacLoc, SolarLoc, BatteryLoc,
OpLimV, OpLimI, LoadResp, CIMVer, PosPt, CoordSys, TopoIsland, Station,
GeoRgn, SubGeoRgn, ZData, OpLimT, XfInfo, FdrLoc, OpLimAHi, OpLimALo,
- OpLimBHi, OpLimBLo, MachLoc, PVPanels, Battery, SrcLoc, TankInfo, TankAsset,
- TapInfo, TapCtrl, TapAsset, PUZ, WirePos, NormAmps, EmergAmps);
+ OpLimBHi, OpLimBLo, MachLoc, PVPanels, Battery, SrcLoc, TankInfo,
+ TapCtrl, PUZ, WirePos, NormAmps, EmergAmps,
+ I1547NameplateData, I1547NameplateDataApplied, I1547Signal, I1547VoltVar,
+ I1547WattVar, I1547ConstPF, I1547VoltWatt, I1547ConstQ);
- ProfileChoice = (FunPrf, EpPrf, GeoPrf, TopoPrf, CatPrf, SshPrf);
+ ProfileChoice = (FunPrf, EpPrf, GeoPrf, TopoPrf, CatPrf, SshPrf, DynPrf);
TCIMExporter = class;
@@ -39,17 +48,19 @@ TCIMBankObject = class(TNamedObject)
maxWindings: Integer;
nWindings: Integer;
connections: array of Integer;
+ bAuto: Boolean;
angles: array of Integer;
phaseA: array of Integer;
phaseB: array of Integer;
phaseC: array of Integer;
ground: array of Integer;
- a_unit: TTransfObj; // save this for writing the bank coordinates
+ pd_unit: TPDElement; // save this for writing the bank coordinates
constructor Create(MaxWdg: Integer);
destructor Destroy; OVERRIDE;
procedure AddTransformer(CE: TCIMExporter; pXf: TTransfObj);
+ procedure AddAutoTransformer(CE: TCIMExporter; pAuto: TAutoTransObj);
procedure BuildVectorGroup;
end;
@@ -89,13 +100,14 @@ TCIMExporter = class(TObject)
BankList: array of TCIMBankObject;
OpLimitHash: THashList;
OpLimitList: array of TCIMOpLimitObject;
- // the Combined XML can be broken into six separate profiles
+ // the Combined XML can be broken into six separate profiles
F_FUN: TFileStream;
F_EP: TFileStream;
F_SSH: TFileStream;
F_CAT: TFileStream;
F_GEO: TFileStream;
F_TOPO: TFileStream;
+ F_DYN: TFileStream;
roots: array[ProfileChoice] of String;
ids: array[ProfileChoice] of TUuid;
public
@@ -106,29 +118,82 @@ TCIMExporter = class(TObject)
procedure EndInstance(prf: ProfileChoice; Root: String);
end;
+ TRemoteSignalObject = class(TNamedObject)
+ public
+ busName: String;
+ pElem: TDSSCktElement;
+ trm: Integer;
+ phase: String; // want A, B, C, s1 or s2
+ ex: TCIMExporter;
+ constructor Create(exporter: TCIMExporter; aBusName:String; seq: Integer; invName: String);
+ destructor Destroy; override;
+ end;
+
+ TIEEE1547Controller = class(TObject)
+ private
+ ND_acVmax, ND_acVmin, AD_pMax, AD_pMaxOverPF, AD_overPF, AD_pMaxUnderPF: Double;
+ AD_underPF, AD_sMax, AD_qMaxInj, AD_qMaxAbs, AD_pMaxCharge: Double;
+ AD_apparentPowerChargeMax, AD_acVnom: Double;
+ VV_vRef, VV_vRefOlrt, VV_curveV1, VV_curveV2, VV_curveV3, VV_curveV4: Double;
+ VV_olrt, VV_curveQ1, VV_curveQ2, VV_curveQ3, VV_curveQ4: Double;
+ Q_reactivePower, PF_powerFactor, VW_olrt, VW_curveV1, VW_curveV2: Double;
+ VW_curveP1, VW_curveP2gen, VW_curveP2load: Double;
+ WV_curveP1gen, WV_curveP2gen, WV_curveP3gen: Double;
+ WV_curveP1load, WV_curveP2load, WV_curveP3load: Double;
+ WV_curveQ1gen, WV_curveQ2gen, WV_curveQ3gen: Double;
+ WV_curveQ1load, WV_curveQ2load, WV_curveQ3load: Double;
+
+ ND_normalOPcatKind, PF_constPFexcitationKind: String;
+ VV_enabled, WV_enabled, PF_enabled, Q_enabled, VW_enabled: Boolean;
+ VV_vRefAutoModeEnabled: Boolean;
+
+ pInvName: TNamedObject;
+ pPlateName: TNamedObject;
+ pSetName: TNamedObject;
+ pDERNames: TStringList;
+ pMonBuses: TStringList;
+ Signals: array of TRemoteSignalObject;
+
+ bNameplateSet: Boolean;
+ ex: TCIMExporter;
+
+ procedure FinishNameplate;
+ procedure SetStorageNameplate(pBat: TStorage2Obj);
+ procedure SetPhotovoltaicNameplate(pPV: TPVSystem2Obj);
+ procedure SetElementNameplate(pElem: TDSSCktElement);
+ procedure SetDefaults(bCatB: Boolean);
+ procedure FindSignalTerminals;
+ function CheckSignalMatch(sig: TRemoteSignalObject; pElm:TDSSCktElement; seq: Integer) : Boolean;
+ public
+ constructor Create(exporter: TCIMExporter);
+ destructor Destroy; override;
+
+ procedure PullFromInvControl(pInv: TInvControl2Obj);
+ procedure PullFromExpControl(pExp: TExpControlObj);
+ procedure WriteCIM (prf: ProfileChoice);
+ end;
+
implementation
uses
+ BufStream,
SysUtils,
Utilities,
Circuit,
DSSClassDefs,
DSSGlobals,
- CktElement,
- PDElement,
PCElement,
Generator,
Load,
RegControl,
Vsource,
Line,
- Ucomplex,
+ UComplex, DSSUcomplex,
UcMatrix,
LineCode,
Fuse,
Capacitor,
CapControl,
- CapControlvars,
Reactor,
ConductorData,
LineUnits,
@@ -145,6 +210,7 @@ implementation
PVSystem,
Relay,
Recloser,
+ XYCurve,
DSSObject,
DSSHelper,
CmdForms;
@@ -152,6 +218,7 @@ implementation
const
// CIM_NS = 'http://iec.ch/TC57/2012/CIM-schema-cim17';
CIM_NS = 'http://iec.ch/TC57/CIM100';
+ CatBQmin = 0.43; // for IEEE 1547 Category B estimate
type
TCIMExporterHelper = class helper for TCIMExporter
@@ -171,15 +238,16 @@ TCIMExporterHelper = class helper for TCIMExporter
function GetBaseVUuid(val: Double): TUuid;
function GetOpLimVUuid(val: Double): TUuid;
function GetOpLimIUuid(norm, emerg: Double): TUuid;
- function PhaseString(pElem: TDSSCktElement; bus: Integer): String; // if order doesn't matter
+ function PhaseString(pElem: TDSSCktElement; bus: Integer; bAllowSec: Boolean = True): String; // if order doesn't matter
+ function PhaseOrderString(pElem: TDSSCktElement; bus: Integer; bAllowSec: Boolean = True): String; // for transposition
procedure ParseSwitchClass(pLine: TLineObj; var swtCls: String; var ratedAmps, breakingAmps: Double);
procedure DoubleNode(prf: ProfileChoice; Node: String; val: Double);
procedure IntegerNode(prf: ProfileChoice; Node: String; val: Integer);
procedure BooleanNode(prf: ProfileChoice; Node: String; val: Boolean);
procedure RefNode(prf: ProfileChoice; Node: String; Obj: TNamedObject);
procedure UuidNode(prf: ProfileChoice; Node: String; ID: TUuid);
- procedure LineCodeRefNode(prf: ProfileChoice; List: TLineCode; Name: String);
- procedure LineSpacingRefNode(prf: ProfileChoice; List: TDSSClass; Name: String);
+ procedure LineCodeRefNode(prf: ProfileChoice; List: TLineCode; Obj: TLineCodeObj);
+ procedure LineSpacingRefNode(prf: ProfileChoice; Obj: TDSSObject);
procedure PhaseWireRefNode(prf: ProfileChoice; Obj: TConductorDataObj);
procedure CircuitNode(prf: ProfileChoice; Obj: TNamedObject);
function FirstPhaseString(pElem: TDSSCktElement; bus: Integer): String;
@@ -198,11 +266,15 @@ TCIMExporterHelper = class helper for TCIMExporter
procedure TransformerControlEnum(prf: ProfileChoice; val: String);
procedure MonitoredPhaseNode(prf: ProfileChoice; val: String);
procedure OpLimitDirectionEnum(prf: ProfileChoice; val: String);
+ procedure NormalOpCatEnum (prf: ProfileChoice; val: String);
+ //procedure SupportedModesEnum (prf: ProfileChoice; val: String);
+ procedure PowerFactorExcitationEnum (prf: ProfileChoice; val: String);
+ procedure RemoteInputSignalEnum (prf: ProfileChoice; val: String);
procedure StringNode(prf: ProfileChoice; Node: String; val: String);
procedure StartInstance(prf: ProfileChoice; Root: String; Obj: TNamedObject);
procedure StartFreeInstance(prf: ProfileChoice; Root: String; uuid: TUUID);
procedure EndInstance(prf: ProfileChoice; Root: String);
- procedure XfmrPhasesEnum(prf: ProfileChoice; pElem: TDSSCktElement; bus: Integer);
+ procedure XfmrTankPhasesAndGround(fprf: ProfileChoice; eprf: ProfileChoice; pXf:TTransfObj; bus: Integer);
procedure PhaseNode(prf: ProfileChoice; Root: String; val: String);
procedure PhaseKindNode(prf: ProfileChoice; Root: String; val: String);
procedure PhaseSideNode(prf: ProfileChoice; Root: String; Side: Integer; val: String);
@@ -210,15 +282,15 @@ TCIMExporterHelper = class helper for TCIMExporter
procedure WindingConnectionKindNode(prf: ProfileChoice; val: String); // D, Y, Z, Yn, Zn, A, I
procedure AttachLinePhases(pLine: TLineObj);
procedure AttachSwitchPhases(pLine: TLineObj);
- procedure AttachCapPhases(pCap: TCapacitorObj; geoUUID: TUuid);
+ procedure AttachCapPhases(pCap: TCapacitorObj; geoUUID: TUuid; sections: double);
procedure AttachSecondaryPhases(pLoad: TLoadObj; geoUUID: TUuid; pPhase: TNamedObject; p, q: Double; phs: String);
procedure AttachLoadPhases(pLoad: TLoadObj; geoUUID: TUuid);
procedure AttachSecondaryGenPhases(pGen: TGeneratorObj; geoUUID: TUuid; pPhase: TNamedObject; p, q: Double; phs: String);
procedure AttachGeneratorPhases(pGen: TGeneratorObj; geoUUID: TUuid);
- procedure AttachSecondarySolarPhases(pPV: TPVSystemObj; geoUUID: TUuid; pPhase: TNamedObject; p, q: Double; phs: String);
- procedure AttachSolarPhases(pPV: TPVSystemObj; geoUUID: TUuid);
- procedure AttachSecondaryStoragePhases(pBat: TStorageObj; geoUUID: TUuid; pPhase: TNamedObject; p, q: Double; phs: String);
- procedure AttachStoragePhases(pBat: TStorageObj; geoUUID: TUuid);
+ procedure AttachSecondarySolarPhases(pPV: TPVSystem2Obj; geoUUID: TUuid; pPhase: TNamedObject; p, q: Double; phs: String);
+ procedure AttachSolarPhases(pPV: TPVSystem2Obj; geoUUID: TUuid);
+ procedure AttachSecondaryStoragePhases(pBat: TStorage2Obj; geoUUID: TUuid; pPhase: TNamedObject; p, q: Double; phs: String);
+ procedure AttachStoragePhases(pBat: TStorage2Obj; geoUUID: TUuid);
procedure WriteLoadModel(Name: String; ID: TUuid;
zP: Double; iP: Double; pP: Double; zQ: Double; iQ: Double; pQ: Double;
eP: Double; eQ: Double);
@@ -231,6 +303,7 @@ TCIMExporterHelper = class helper for TCIMExporter
procedure WriteTapeData(pCab: TTSDataObj);
procedure WriteConcData(pCab: TCNDataObj);
procedure WriteWireData(pWire: TConductorDataObj);
+ procedure ConverterControlEnum(prf: ProfileChoice; varMode: Integer; CIMdynamics: Boolean);
procedure FD_Destroy;
procedure FD_Create(Combined: Boolean; FileName: String);
@@ -273,6 +346,8 @@ procedure TCIMExporter.WriteCimLn(prf: ProfileChoice; const s: String);
FSWriteLn(F_CAT, s);
SshPrf:
FSWriteLn(F_SSH, s);
+ DynPrf:
+ FSWriteLn(F_DYN, s);
end;
end
else
@@ -366,7 +441,7 @@ procedure TCIMExporterHelper.ParseSwitchClass(pLine: TLineObj; var swtCls: Strin
end;
// this returns s1, s2, or a combination of ABCN
-function TCIMExporterHelper.PhaseString(pElem: TDSSCktElement; bus: Integer): String; // if order doesn't matter
+function TCIMExporterHelper.PhaseString(pElem: TDSSCktElement; bus: Integer; bAllowSec: Boolean = True): String; // if order doesn't matter
var
val, phs: String;
dot: Integer;
@@ -376,13 +451,15 @@ function TCIMExporterHelper.PhaseString(pElem: TDSSCktElement; bus: Integer): St
for dot := 2 to bus do
phs := pElem.NextBus;
bSec := FALSE;
- if pElem.NPhases = 2 then
- if ActiveCircuit.Buses^[pElem.Terminals[bus - 1].BusRef].kVBase < 0.25 then
- bSec := TRUE;
- if pElem.NPhases = 1 then
- if ActiveCircuit.Buses^[pElem.Terminals[bus - 1].BusRef].kVBase < 0.13 then
- bSec := TRUE;
-
+ if bAllowSec then
+ begin
+ if pElem.NPhases = 2 then
+ if ActiveCircuit.Buses^[pElem.Terminals[bus - 1].BusRef].kVBase < 0.25 then
+ bSec := TRUE;
+ if pElem.NPhases = 1 then
+ if ActiveCircuit.Buses^[pElem.Terminals[bus - 1].BusRef].kVBase < 0.13 then
+ bSec := TRUE;
+ end;
dot := pos('.', phs);
if dot < 1 then
begin
@@ -421,15 +498,27 @@ function TCIMExporterHelper.PhaseString(pElem: TDSSCktElement; bus: Integer): St
Result := val;
end;
-function PhaseOrderString(pElem: TDSSCktElement; bus: Integer): String; // for transposition
+function TCIMExporterHelper.PhaseOrderString(pElem: TDSSCktElement; bus: Integer; bAllowSec: Boolean = True): String; // for transposition
var
phs: String;
dot: Integer;
+ bSec: Boolean;
begin
phs := pElem.FirstBus;
for dot := 2 to bus do
phs := pElem.NextBus;
+ bSec := false;
+ if bAllowSec then
+ begin
+ if pElem.NPhases = 2 then
+ if ActiveCircuit.Buses^[pElem.Terminals[bus - 1].BusRef].kVBase < 0.25 then
+ bSec := true;
+ if pElem.NPhases = 1 then
+ if ActiveCircuit.Buses^[pElem.Terminals[bus - 1].BusRef].kVBase < 0.13 then
+ bSec := true;
+ end;
+
dot := pos('.', phs);
if dot < 1 then
begin
@@ -438,6 +527,21 @@ function PhaseOrderString(pElem: TDSSCktElement; bus: Integer): String; // for t
else
begin
phs := Copy(phs, dot + 1, Length(phs));
+ if Pos ('3', phs) > 0 then
+ bSec := false; // i.e. it's a three-phase secondary, not split-phase
+ if bSec then
+ begin
+ if Pos ('1', phs) > 0 then
+ begin
+ Result := 's1';
+ if Pos ('2', phs) > 0 then
+ Result := Result + '2';
+ end
+ else
+ if Pos ('2', phs) > 0 then
+ Result := 's2';
+ end
+ else
if Pos('1.2.3', phs) > 0 then
Result := 'ABC'
else
@@ -548,6 +652,7 @@ constructor TCIMBankObject.Create(MaxWdg: Integer);
begin
maxWindings := MaxWdg;
nWindings := 0;
+ bAuto := False;
SetLength(connections, MaxWdg);
SetLength(angles, MaxWdg);
SetLength(phaseA, MaxWdg);
@@ -565,7 +670,7 @@ destructor TCIMBankObject.Destroy;
phaseB := NIL;
phaseC := NIL;
ground := NIL;
- a_unit := NIL;
+ pd_unit := NIL;
inherited Destroy;
end;
@@ -573,6 +678,14 @@ procedure TCIMBankObject.BuildVectorGroup;
var
i: Integer;
begin
+ if bAuto then
+ begin
+ if nWindings < 3 then
+ vectorGroup := 'YNa'
+ else
+ vectorGroup := 'YNad1';
+ Exit;
+ end;
vectorGroup := '';
i := 0; // dynamic arrays are zero-based
while i < nWindings do
@@ -593,7 +706,7 @@ procedure TCIMBankObject.BuildVectorGroup;
Inc(i)
end;
if Length(vectorGroup) > 0 then
- vectorGroup := UpperCase(LeftStr(vectorGroup, 1)) + RightStr(vectorGroup, Length(vectorGroup) - 1);
+ vectorGroup := AnsiUpperCase(LeftStr(vectorGroup, 1)) + RightStr(vectorGroup, Length(vectorGroup) - 1);
end;
procedure TCIMBankObject.AddTransformer(CE: TCIMExporter; pXf: TTransfObj);
@@ -601,11 +714,11 @@ procedure TCIMBankObject.AddTransformer(CE: TCIMExporter; pXf: TTransfObj);
i: Integer;
phs: String;
begin
- if pXf.NumberOfWindings > nWindings then
- nWindings := pXf.NumberOfWindings;
+ if pXf.NumWindings > nWindings then
+ nWindings := pXf.NumWindings;
- a_unit := pXf;
- for i := 1 to pXf.NumberOfWindings do
+ pd_unit := pXf;
+ for i := 1 to pXf.NumWindings do
begin
phs := CE.PhaseString(pXf, i);
if Pos('A', phs) > 0 then
@@ -623,6 +736,26 @@ procedure TCIMBankObject.AddTransformer(CE: TCIMExporter; pXf: TTransfObj);
end;
end;
+procedure TCIMBankObject.AddAutoTransformer(CE: TCIMExporter; pAuto: TAutoTransObj); // 3-phase, 2 or 3 windings
+var
+ i: integer;
+begin
+ if pAuto.NumWindings > nWindings then
+ nWindings := pAuto.NumWindings;
+
+ bAuto := True;
+ pd_unit := pAuto;
+ for i:=1 to pAuto.NumWindings do
+ begin
+ phaseA[i-1] := 1;
+ phaseB[i-1] := 1;
+ phaseC[i-1] := 1;
+ connections[i-1] := pAuto.WdgConnection[i];
+ if i = 2 then
+ ground[i-1] := 1;
+ end;
+end;
+
constructor TCIMOpLimitObject.Create(norm, emerg: Double);
begin
NormAmps := norm;
@@ -866,14 +999,8 @@ function TCIMExporterHelper.GetDevUuid(which: UuidChoice; Name: String; Seq: Int
key := 'Battery=';
TankInfo:
key := 'TankInfo=';
- TankAsset:
- key := 'TankAsset=';
- TapInfo:
- key := 'TapInfo=';
TapCtrl:
key := 'TapCtrl=';
- TapAsset:
- key := 'TapAsset=';
PUZ:
key := 'PUZ=';
WirePos:
@@ -882,6 +1009,22 @@ function TCIMExporterHelper.GetDevUuid(which: UuidChoice; Name: String; Seq: Int
key := 'NormAmps=';
EmergAmps:
key := 'EmergAmps=';
+ I1547NameplateData:
+ key := 'INameplate=';
+ I1547NameplateDataApplied:
+ key := 'IApplied=';
+ I1547Signal:
+ key := 'ISignal=';
+ I1547VoltVar:
+ key := 'IVVar=';
+ I1547WattVar:
+ key := 'IWVar=';
+ I1547ConstPF:
+ key := 'IPF=';
+ I1547VoltWatt:
+ key := 'IVWatt=';
+ I1547ConstQ:
+ key := 'IQ=';
end;
key := key + Name + '=' + IntToStr(Seq);
Result := GetHashedUuid(key);
@@ -982,26 +1125,14 @@ procedure TCIMExporterHelper.UuidNode(prf: ProfileChoice; Node: String; ID: TUui
FD.WriteCimLn(prf, Format(' ', [Node, UUIDToCIMString(ID)]));
end;
-procedure TCIMExporterHelper.LineCodeRefNode(prf: ProfileChoice; List: TLineCode; Name: String);
-var
- Obj: TLineCodeObj;
+procedure TCIMExporterHelper.LineCodeRefNode(prf: ProfileChoice; List: TLineCode; Obj: TLineCodeObj);
begin
- if List.SetActive(Name) then
- begin
- Obj := List.GetActiveObj;
- FD.WriteCimLn(prf, Format(' ', [Obj.CIM_ID]));
- end;
+ FD.WriteCimLn(prf, Format(' ', [Obj.CIM_ID]));
end;
-procedure TCIMExporterHelper.LineSpacingRefNode(prf: ProfileChoice; List: TDSSClass; Name: String);
-var
- Obj: TDSSObject; // should be a TLineGeometryObj or TLineSpacingObj
+procedure TCIMExporterHelper.LineSpacingRefNode(prf: ProfileChoice; Obj: TDSSObject);
begin
- if List.SetActive(Name) then
- begin
- Obj := List.GetActiveObj;
- FD.WriteCimLn(prf, Format(' ', [Obj.CIM_ID]));
- end;
+ FD.WriteCimLn(prf, Format(' ', [Obj.CIM_ID]));
end;
procedure TCIMExporterHelper.PhaseWireRefNode(prf: ProfileChoice; Obj: TConductorDataObj);
@@ -1035,13 +1166,13 @@ procedure TCIMExporterHelper.BatteryStateEnum(prf: ProfileChoice; val: Integer);
var
str: String;
begin
- str := 'Waiting';
+ str := 'waiting';
if val = STORE_CHARGING then
- str := 'Charging'
+ str := 'charging'
else
if val = STORE_DISCHARGING then
- str := 'Discharging';
- FD.WriteCimLn(prf, Format(' ',
+ str := 'discharging';
+ FD.WriteCimLn(prf, Format(' ',
[CIM_NS, str]));
end;
@@ -1123,6 +1254,32 @@ procedure TCIMExporterHelper.OpLimitDirectionEnum(prf: ProfileChoice; val: Strin
[CIM_NS, val]));
end;
+// next several for DERIEEEType1 CIM dynamics
+procedure TCIMExporterHelper.NormalOpCatEnum (prf: ProfileChoice; val: String);
+begin
+ FD.WriteCimLn (prf, Format (' ',
+ [CIM_NS, val]));
+end;
+
+// procedure TCIMExporterHelper.SupportedModesEnum (prf: ProfileChoice; val: String);
+// begin
+// FD.WriteCimLn (prf, Format (' ',
+// [CIM_NS, val]));
+// end;
+
+procedure TCIMExporterHelper.PowerFactorExcitationEnum (prf: ProfileChoice; val: String);
+begin
+ FD.WriteCimLn (prf, Format (' ',
+ [CIM_NS, val]));
+end;
+
+procedure TCIMExporterHelper.RemoteInputSignalEnum (prf: ProfileChoice; val: String);
+begin
+ FD.WriteCimLn (prf, Format (' ',
+ [CIM_NS, val]));
+end;
+// end of Enums for DERIEEEType1 CIM dynamics
+
procedure TCIMExporterHelper.StringNode(prf: ProfileChoice; Node: String; val: String);
begin
FD.WriteCimLn(prf, Format(' %s', [Node, val, Node]));
@@ -1143,10 +1300,69 @@ procedure TCIMExporterHelper.EndInstance(prf: ProfileChoice; Root: String);
FD.EndInstance(prf, Root);
end;
-procedure TCIMExporterHelper.XfmrPhasesEnum(prf: ProfileChoice; pElem: TDSSCktElement; bus: Integer);
+procedure TCIMExporterHelper.XfmrTankPhasesAndGround(fprf: ProfileChoice; eprf: ProfileChoice; pXf:TTransfObj; bus: Integer);
+var
+ ordered_phs, phs: String;
+ j1, j2: Integer;
+ reverse_ground, wye_ground, delta, wye_unground: Boolean;
begin
- FD.WriteCimLn(prf, Format(' ',
- [CIM_NS, PhaseString(pElem, bus)]));
+ j1 := (bus-1) * pXf.NConds + 1;
+ j2 := j1 + pXf.Nphases;
+ reverse_ground := False;
+ wye_ground := False;
+ wye_unground := False;
+ delta := False;
+ // writeln(Format(' Testing %d and %d', [j1, j2]));
+ if (pXf.Winding^[bus].Connection = 1) then
+ begin // delta
+ BooleanNode (fprf, 'TransformerEnd.grounded', false);
+ delta := True;
+ end
+ else
+ if (pXf.NodeRef^[j2] = 0) then
+ begin // last conductor is grounded solidly
+ BooleanNode (FunPrf, 'TransformerEnd.grounded', true);
+ DoubleNode (EpPrf, 'TransformerEnd.rground', 0.0);
+ DoubleNode (EpPrf, 'TransformerEnd.xground', 0.0);
+ wye_ground := True;
+ end
+ else
+ if (pXf.NodeRef^[j1] = 0) then
+ begin // first conductor is grounded solidly, but should be reversed
+ BooleanNode (FunPrf, 'TransformerEnd.grounded', true);
+ DoubleNode (EpPrf, 'TransformerEnd.rground', 0.0);
+ DoubleNode (EpPrf, 'TransformerEnd.xground', 0.0);
+ reverse_ground := True;
+ end
+ else
+ if (pXf.Winding^[bus].Rneut < 0.0) then
+ begin // probably wye ungrounded
+ BooleanNode (FunPrf, 'TransformerEnd.grounded', false);
+ wye_unground := True;
+ end
+ else
+ begin // not delta, not wye solidly grounded or ungrounded
+ BooleanNode (FunPrf, 'TransformerEnd.grounded', true);
+ DoubleNode (EpPrf, 'TransformerEnd.rground', pXf.Winding^[bus].Rneut);
+ DoubleNode (EpPrf, 'TransformerEnd.xground', pXf.Winding^[bus].Xneut);
+ end;
+
+ ordered_phs := PhaseOrderString(pXf, bus);
+ if (ordered_phs = 's1') then
+ ordered_phs := 's1N'
+ else if (ordered_phs = 's2') then
+ ordered_phs := 'Ns2'
+ else if reverse_ground then
+ ordered_phs := 'N' + ordered_phs
+ else if wye_ground then
+ ordered_phs := ordered_phs + 'N'
+ else if wye_unground then
+ ordered_phs := ordered_phs + 'N';
+
+ FD.WriteCimLn (fprf, Format(
+ ' ',
+ [CIM_NS, ordered_phs]
+ ));
end;
procedure TCIMExporterHelper.PhaseNode(prf: ProfileChoice; Root: String; val: String);
@@ -1229,6 +1445,18 @@ procedure TCIMExporterHelper.AttachSwitchPhases(pLine: TLineObj);
begin
phs1 := s1[i];
phs2 := s2[i];
+ if phs1 = 's' then
+ continue;
+ if phs2 = 's' then
+ continue;
+ if phs1 = '1' then
+ phs1 := 's1';
+ if phs1 = '2' then
+ phs1 := 's2';
+ if phs2 = '1' then
+ phs2 := 's1';
+ if phs2 = '2' then
+ phs2 := 's2';
pPhase.LocalName := pLine.Name + '_' + phs1;
pPhase.UUID := GetDevUuid(LinePhase, pPhase.LocalName, 1);
StartInstance(FunPrf, 'SwitchPhase', pPhase);
@@ -1242,7 +1470,7 @@ procedure TCIMExporterHelper.AttachSwitchPhases(pLine: TLineObj);
end;
end;
-procedure TCIMExporterHelper.AttachCapPhases(pCap: TCapacitorObj; geoUUID: TUuid);
+procedure TCIMExporterHelper.AttachCapPhases(pCap: TCapacitorObj; geoUUID: TUuid; sections: double);
var
s, phs: String;
i: Integer;
@@ -1256,7 +1484,7 @@ procedure TCIMExporterHelper.AttachCapPhases(pCap: TCapacitorObj; geoUUID: TUuid
with pCap do
begin
bph := 0.001 * Totalkvar / NomKV / NomKV / NumSteps / NPhases;
- if (Connection = 1) then
+ if (Connection = TCapacitorConnection.Delta) then
s := DeltaPhaseString(pCap);
end;
for i := 1 to length(s) do
@@ -1270,6 +1498,7 @@ procedure TCIMExporterHelper.AttachCapPhases(pCap: TCapacitorObj; geoUUID: TUuid
DoubleNode(EpPrf, 'LinearShuntCompensatorPhase.gPerSection', 0.0);
IntegerNode(EpPrf, 'ShuntCompensatorPhase.normalSections', pCap.NumSteps);
IntegerNode(EpPrf, 'ShuntCompensatorPhase.maximumSections', pCap.NumSteps);
+ DoubleNode(SshPrf, 'ShuntCompensatorPhase.sections', sections);
RefNode(FunPrf, 'ShuntCompensatorPhase.ShuntCompensator', pCap);
UuidNode(GeoPrf, 'PowerSystemResource.Location', geoUUID);
EndInstance(FunPrf, 'LinearShuntCompensatorPhase');
@@ -1295,22 +1524,25 @@ procedure TCIMExporterHelper.AttachLoadPhases(pLoad: TLoadObj; geoUUID: TUuid);
i: Integer;
pPhase: TNamedObject;
p, q: Double;
+ bAllowSec: Boolean;
begin
if pLoad.NPhases = 3 then
exit;
+ // TODO - use a more robust filter than pLoad.LoadClass, which is > 1 only for PNNL taxonomy imports
+ bAllowSec := (pLoad.LoadClass <= 1);
p := 1000.0 * pLoad.kWBase / pLoad.NPhases;
q := 1000.0 * pLoad.kvarBase / pLoad.NPhases;
if pLoad.Connection = TLoadConnection.Delta then
s := DeltaPhaseString(pLoad)
else
- s := PhaseString(pLoad, 1);
+ s := PhaseString(pLoad, 1, bAllowSec);
pPhase := TNamedObject.Create('dummy');
// first, filter out what appear to be split secondary loads
// these can be 2-phase loads (balanced) nominally 0.208 kV, or
// 1-phase loads (possibly unbalanced) nominally 0.12 kV
// TODO - handle s1 to s2 240-volt loads; these would be s12, which is not a valid SinglePhaseKind
- if pLoad.kVLoadBase < 0.25 then
+ if (pLoad.kVLoadBase < 0.25) and bAllowSec then
begin
if pLoad.NPhases = 2 then
begin
@@ -1401,7 +1633,7 @@ procedure TCIMExporterHelper.AttachGeneratorPhases(pGen: TGeneratorObj; geoUUID:
end;
end;
-procedure TCIMExporterHelper.AttachSecondarySolarPhases(pPV: TPVSystemObj; geoUUID: TUuid; pPhase: TNamedObject; p, q: Double; phs: String);
+procedure TCIMExporterHelper.AttachSecondarySolarPhases(pPV: TPVSystem2Obj; geoUUID: TUuid; pPhase: TNamedObject; p, q: Double; phs: String);
begin
pPhase.LocalName := pPV.Name + '_' + phs;
pPhase.UUID := GetDevUuid(SolarPhase, pPhase.LocalName, 1);
@@ -1414,7 +1646,7 @@ procedure TCIMExporterHelper.AttachSecondarySolarPhases(pPV: TPVSystemObj; geoUU
EndInstance(FunPrf, 'PowerElectronicsConnectionPhase');
end;
-procedure TCIMExporterHelper.AttachSolarPhases(pPV: TPVSystemObj; geoUUID: TUuid);
+procedure TCIMExporterHelper.AttachSolarPhases(pPV: TPVSystem2Obj; geoUUID: TUuid);
var
s, phs: String;
i: Integer;
@@ -1462,7 +1694,7 @@ procedure TCIMExporterHelper.AttachSolarPhases(pPV: TPVSystemObj; geoUUID: TUuid
end;
end;
-procedure TCIMExporterHelper.AttachSecondaryStoragePhases(pBat: TStorageObj; geoUUID: TUuid; pPhase: TNamedObject; p, q: Double; phs: String);
+procedure TCIMExporterHelper.AttachSecondaryStoragePhases(pBat: TStorage2Obj; geoUUID: TUuid; pPhase: TNamedObject; p, q: Double; phs: String);
begin
pPhase.LocalName := pBat.Name + '_' + phs;
pPhase.UUID := GetDevUuid(BatteryPhase, pPhase.LocalName, 1);
@@ -1475,7 +1707,7 @@ procedure TCIMExporterHelper.AttachSecondaryStoragePhases(pBat: TStorageObj; geo
EndInstance(FunPrf, 'PowerElectronicsConnectionPhase');
end;
-procedure TCIMExporterHelper.AttachStoragePhases(pBat: TStorageObj; geoUUID: TUuid);
+procedure TCIMExporterHelper.AttachStoragePhases(pBat: TStorage2Obj; geoUUID: TUuid);
var
s, phs: String;
i: Integer;
@@ -1661,20 +1893,14 @@ procedure TCIMExporterHelper.VbaseNode(prf: ProfileChoice; pElem: TDSSCktElement
procedure TCIMExporterHelper.WriteXfmrCode(pXfCd: TXfmrCodeObj);
var
- pName, pBank: TNamedObject;
- ratShort, ratEmerg, val, Zbase: Double;
+ pName: TNamedObject;
+ ratShort, ratEmerg, val, Zbase, pctIexc, r, x, TestKVA: Double;
i, j, seq: Integer;
begin
pName := TNamedObject.Create('dummy');
- pBank := TNamedObject.Create('dummy');
with pXfCd do
begin
- pBank.LocalName := pXfCd.Name + '_PowerXfInfo';
- pBank.UUID := GetDevUuid(XfInfo, pXfCd.Name, 1);
- StartInstance(CatPrf, 'PowerTransformerInfo', pBank);
- EndInstance(CatPrf, 'PowerTransformerInfo');
StartInstance(CatPrf, 'TransformerTankInfo', pXfCd);
- RefNode(CatPrf, 'TransformerTankInfo.PowerTransformerInfo', pBank);
EndInstance(CatPrf, 'TransformerTankInfo');
ratShort := NormMaxHKVA / Winding^[1].kva;
ratEmerg := EmergMaxHKVA / Winding^[1].kva;
@@ -1722,8 +1948,9 @@ procedure TCIMExporterHelper.WriteXfmrCode(pXfCd: TXfmrCodeObj);
StartInstance(CatPrf, 'NoLoadTest', pName);
UuidNode(CatPrf, 'NoLoadTest.EnergisedEnd', GetDevUuid(WdgInf, pXfCd.Name, 1));
DoubleNode(CatPrf, 'NoLoadTest.energisedEndVoltage', 1000.0 * Winding^[1].kvll);
- DoubleNode(CatPrf, 'NoLoadTest.excitingCurrent', pctImag);
- DoubleNode(CatPrf, 'NoLoadTest.excitingCurrentZero', pctImag);
+ pctIexc := sqrt(pctImag * pctImag + pctNoLoadLoss * pctNoLoadLoss);
+ DoubleNode(CatPrf, 'NoLoadTest.excitingCurrent', pctIexc);
+ DoubleNode(CatPrf, 'NoLoadTest.excitingCurrentZero', pctIexc);
val := 0.01 * pctNoLoadLoss * Winding^[1].kva; // losses to be in kW
DoubleNode(CatPrf, 'NoLoadTest.loss', val);
DoubleNode(CatPrf, 'NoLoadTest.lossZero', val);
@@ -1739,27 +1966,22 @@ procedure TCIMExporterHelper.WriteXfmrCode(pXfCd: TXfmrCodeObj);
pName.UUID := GetDevUuid(ScTest, pXfCd.Name, seq);
StartInstance(CatPrf, 'ShortCircuitTest', pName);
UuidNode(CatPrf, 'ShortCircuitTest.EnergisedEnd', GetDevUuid(WdgInf, pXfCd.Name, i));
- // NOTE: can insert more than one GroundedEnds for three-winding short-circuit tests
+ // NOTE: can insert more than one GroundedEnds for three-winding short-circuit tests
UuidNode(CatPrf, 'ShortCircuitTest.GroundedEnds', GetDevUuid(WdgInf, pXfCd.Name, j));
IntegerNode(CatPrf, 'ShortCircuitTest.energisedEndStep', Winding^[i].NumTaps div 2);
IntegerNode(CatPrf, 'ShortCircuitTest.groundedEndStep', Winding^[j].NumTaps div 2);
- Zbase := Winding^[i].kvll;
- Zbase := 1000.0 * Zbase * Zbase / Winding^[1].kva; // all DSS impedances are on winding 1 base
- val := Xsc^[seq] * Zbase;
+ TestKVA := Winding^[1].kva;
+ Zbase := 1000.0 * Zbase * Zbase / TestKVA; // all DSS impedances are on winding 1 kva base
+ // windings are not overloaded during short-circuit tests, but in OpenDSS Sbase is on Winding 1 always
+ x := Xsc^[seq];
+ r := Winding^[i].Rpu + Winding^[j].Rpu;
+ val := sqrt(r*r + x*x) * Zbase;
DoubleNode(CatPrf, 'ShortCircuitTest.leakageImpedance', val);
DoubleNode(CatPrf, 'ShortCircuitTest.leakageImpedanceZero', val);
- if seq = 1 then
- begin // put all the load loss on test from wdg1 to wdg2
- val := 0.01 * pctLoadLoss * Winding^[1].kva; // losses are to be in kW
- DoubleNode(CatPrf, 'ShortCircuitTest.loss', val);
- DoubleNode(CatPrf, 'ShortCircuitTest.lossZero', val);
- end
- else
- begin
- DoubleNode(CatPrf, 'ShortCircuitTest.loss', 0.0);
- DoubleNode(CatPrf, 'ShortCircuitTest.lossZero', 0.0);
- end;
- DoubleNode(CatPrf, 'TransformerTest.basePower', 1000.0 * Winding^[i].kva);
+ val := r * TestKVA;
+ DoubleNode(CatPrf, 'ShortCircuitTest.loss', val);
+ DoubleNode(CatPrf, 'ShortCircuitTest.lossZero', val);
+ DoubleNode(CatPrf, 'TransformerTest.basePower', 1000.0 * TestKVA);
DoubleNode(CatPrf, 'TransformerTest.temperature', 50.0);
EndInstance(CatPrf, 'ShortCircuitTest');
end;
@@ -1833,7 +2055,11 @@ procedure TCIMExporterHelper.WriteWireData(pWire: TConductorDataObj);
begin
with pWire do
begin
- StringNode(CatPrf, 'WireInfo.sizeDescription', DisplayName);
+ if DisplayName <> '' then
+ StringNode(CatPrf, 'WireInfo.sizeDescription', DisplayName)
+ else
+ StringNode(CatPrf, 'WireInfo.sizeDescription', DSSClassName + '_' + Name);
+
if CompareText(LeftStr(name, 2), 'AA') = 0 then
ConductorMaterialEnum(CatPrf, 'aluminum')
else
@@ -1863,9 +2089,879 @@ procedure TCIMExporterHelper.WriteWireData(pWire: TConductorDataObj);
end;
end;
+///////// begin helper class for exporting IEEE 1547 model parameters /////////////
+
+constructor TRemoteSignalObject.Create(exporter: TCIMExporter; aBusName: String; seq: Integer; invName: String);
+begin
+ ex := exporter;
+ busName := aBusName;
+ pElem := NIL;
+ trm := -1;
+ phase := 'A';
+ inherited Create('ISignal');
+ LocalName := invName + '_' + IntToStr(seq);
+ UUID := ex.GetDevUUID(I1547Signal, LocalName, seq);
+end;
+
+destructor TRemoteSignalObject.Destroy;
+begin
+ inherited Destroy;
+end;
+
+function TIEEE1547Controller.CheckSignalMatch(sig: TRemoteSignalObject; pElm: TDSSCktElement; seq: Integer): Boolean;
+var
+ elmPhases, trmBus: String;
+ dotpos: Integer;
+begin
+ Result := FALSE;
+ trmBus := pElm.GetBus(seq);
+ dotpos := ansipos('.', trmBus);
+ if dotpos > 0 then
+ begin
+ trmBus := trmBus.Substring(0, dotpos - 1);
+ end;
+
+ if CompareText(sig.busName, trmBus) = 0 then
+ begin
+ elmPhases := ex.PhaseString(pElm, seq, TRUE);
+ if Pos(sig.phase, elmPhases) > 0 then
+ begin
+ sig.trm := seq;
+ sig.pElem := pElm;
+ Result := TRUE;
+ end
+ else
+ if (Pos('1', elmPhases) > 0) and (sig.phase = 'A') then
+ begin // switch to secondary phasing
+ sig.trm := seq;
+ sig.pElem := pElm;
+ sig.phase := 's1';
+ Result := TRUE;
+ end
+ else
+ if (Pos('2', elmPhases) > 0) and (sig.phase = 'B') then
+ begin // switch to secondary phasing
+ sig.trm := seq;
+ sig.pElem := pElm;
+ sig.phase := 's2';
+ Result := TRUE;
+ end;
+ end;
+end;
+
+procedure TIEEE1547Controller.FindSignalTerminals;
+var
+ i, j, k, dotpos: Integer;
+ bus, phase: String;
+ elements: ArrayOfString;
+ found: Boolean;
+ pElem: TDSSCktElement;
+begin
+ if pMonBuses.Count < 1 then
+ begin
+ SetLength(Signals, 0);
+ Exit;
+ end;
+ // create just one remote signal for the main bus, based on the first MonBus
+ // IEEE 1547 doesn't allow different main buses
+ // IEEE 1547 also specifies that the average (pos seq) of all applicable voltages be used
+ SetLength(Signals, 1);
+
+ for i := Low(Signals) to High(Signals) do
+ begin
+ bus := pMonBuses.Strings[i];
+ Signals[i] := TRemoteSignalObject.Create(ex, bus, i + 1, pInvName.LocalName);
+ dotpos := ansipos('.', bus); // removes the dot
+ if dotpos > 0 then
+ begin
+ phase := bus.Substring(dotpos);
+ if Pos('3', phase) > 0 then
+ Signals[i].phase := 'C'
+ else
+ if Pos('2', phase) > 0 then
+ Signals[i].phase := 'B'
+ else
+ Signals[i].phase := 'A';
+ Signals[i].busName := bus.Substring(0, dotpos - 1);
+ end
+ else
+ begin // this is a three-phase bus, which must be ABC, not s1 and/or s2
+ Signals[i].phase := 'A'; // if user wants B and/or C as well, the MonBus input should have specified
+ end;
+
+ found := FALSE;
+ with ex.ActiveCircuit do
+ begin
+ elements := getPDEatBus(Signals[i].busName);
+ for j := Low(elements) to High(elements) do
+ begin
+ if found then
+ break;
+ if SetElementActive(elements[j]) > 0 then
+ begin
+ pElem := ActiveCktElement;
+ for k := 1 to pElem.NTerms do
+ begin
+ if CheckSignalMatch(Signals[i], pElem, k) then
+ begin
+ found := TRUE;
+ break;
+ end;
+ end;
+ end;
+ end;
+ if not found then
+ begin
+ elements := getPCEatBus(bus);
+ for j := Low(elements) to High(elements) do
+ begin
+ if found then
+ break;
+ if SetElementActive(elements[j]) > 0 then
+ begin
+ pElem := ActiveCktElement;
+ for k := 1 to pElem.NTerms do
+ begin
+ if CheckSignalMatch(Signals[i], pElem, k) then
+ begin
+ found := TRUE;
+ break;
+ end;
+ end;
+ end;
+ end;
+ end;
+ end;
+ end;
+end;
+
+constructor TIEEE1547Controller.Create(exporter: TCIMExporter);
+begin
+ inherited Create;
+ ex := exporter;
+ SetDefaults(FALSE);
+ pInvName := TNamedObject.Create('Inv');
+ pPlateName := TNamedObject.Create('Nameplate');
+ pSetName := TNamedObject.Create('Settings');
+ pDERNames := TStringList.Create;
+ pMonBuses := TStringList.Create;
+ Signals := NIL;
+end;
+
+destructor TIEEE1547Controller.Destroy;
+begin
+ pInvName.Free;
+ pPlateName.Free;
+ pSetName.Free;
+ pDERNames.Free;
+ pMonBuses.Free;
+ Signals := NIL;
+ inherited Destroy;
+end;
+
+procedure TIEEE1547Controller.PullFromInvControl(pInv: TInvControl2Obj);
+var
+ xy: TXYcurveObj;
+ bCatB, bValid, bSet1, bSet2, bSet3, bSet4, bSet5, bSet6: Boolean;
+ mode, combi, i: Integer;
+ v, p, q, qvslope: Double;
+begin
+ pInvName.LocalName := pInv.Name;
+ pInvName.UUID := pInv.UUID;
+ pDERNames.Assign(pInv.DERNameList);
+ if pInv.MonBusesNameList.Count > 0 then
+ pMonBuses.Assign(pInv.MonBusesNameList)
+ else
+ pMonBuses.Clear;
+
+ bCatB := FALSE;
+ xy := pInv.Fvvc_curve;
+ if (xy <> NIL) then
+ begin
+ for i := 1 to xy.NumPoints do
+ begin
+ if xy.YValue_pt[i] < -CatBQmin then
+ begin
+ bCatB := TRUE;
+ break;
+ end;
+ end;
+ end;
+ SetDefaults(bCatB);
+
+ VV_olrt := pInv.LPFTau * 2.3026;
+ VW_olrt := VV_olrt;
+
+ if (xy <> NIL) then
+ begin
+ i := 1;
+ bValid := FALSE;
+ bSet1 := FALSE;
+ bSet2 := FALSE;
+ bSet3 := FALSE;
+ bSet4 := FALSE;
+ while i <= xy.NumPoints do
+ begin
+ v := xy.XValue_pt[i];
+ if (v >= 0.77) and (v <= 1.25) then
+ bValid := TRUE;
+ if bValid then
+ begin
+ if not bSet1 then
+ begin
+ VV_curveV1 := v;
+ VV_curveQ1 := xy.YValue_pt[i];
+ bSet1 := TRUE;
+ end
+ else
+ if not bSet2 then
+ begin
+ if v > 1.05 then
+ begin
+ VV_curveV2 := 1.0;
+ VV_curveQ2 := 0.0;
+ if v > 1.08 then
+ begin
+ VV_curveV3 := 1.0;
+ VV_curveQ3 := 0.0;
+ bSet3 := TRUE;
+ VV_curveV4 := v;
+ VV_curveQ4 := xy.YValue_pt[i];
+ bSet4 := TRUE;
+ end;
+ end
+ else
+ begin
+ VV_curveV2 := v;
+ VV_curveQ2 := xy.YValue_pt[i];
+ end;
+ bSet2 := TRUE;
+ end
+ else
+ if not bSet3 then
+ begin
+ VV_curveV3 := v;
+ VV_curveQ3 := xy.YValue_pt[i];
+ bSet3 := TRUE;
+ end
+ else
+ if not bSet4 then
+ begin
+ VV_curveV4 := v;
+ VV_curveQ4 := xy.YValue_pt[i];
+ bSet4 := TRUE;
+ end;
+ end;
+ inc(i);
+ end;
+ end;
+
+ xy := pInv.Fvoltwatt_curve;
+ if (xy <> NIL) then
+ begin
+ i := 1;
+ bValid := FALSE;
+ bSet1 := FALSE;
+ bSet2 := FALSE;
+ while i <= xy.NumPoints do
+ begin
+ v := xy.XValue_pt[i];
+ p := xy.YValue_pt[i];
+ if (v >= 1.00) and (v <= 1.10) then // TODO: per standard, v should be >= 1.05 but we loosen that criteria for testing
+ bValid := TRUE;
+ if bValid then
+ begin
+ if not bSet1 then
+ begin
+ VW_curveV1 := v;
+ VW_curveP1 := p; // this is actually supposed to be 1.0 always
+ bSet1 := TRUE;
+ end
+ else
+ if not bSet2 then
+ begin
+ VW_curveV2 := v;
+ if p < 0.0 then
+ begin
+ VW_curveP2gen := 0.2; // TODO: should have a pMin
+ VW_curveP2load := p;
+ end
+ else
+ begin
+ VW_curveP2gen := p;
+ VW_curveP2load := 0.0;
+ end;
+ bSet2 := TRUE;
+ end;
+ end;
+ inc(i);
+ end;
+ end;
+
+ xy := pInv.FvoltwattCH_curve;
+ if (xy <> NIL) then
+ begin
+ p := 0.0;
+ i := 1;
+ while i <= xy.NumPoints do
+ begin
+ if xy.YValue_pt[i] > p then
+ p := xy.YValue_pt[i];
+ inc(i);
+ end;
+ if (-p < VW_curveP2load) then
+ VW_curveP2load := -p;
+ end;
+
+ xy := pInv.Fwattvar_curve;
+ if (xy <> NIL) then
+ begin
+ i := 1;
+ bValid := FALSE;
+ bSet1 := FALSE;
+ bSet2 := FALSE;
+ bSet3 := FALSE;
+ bSet4 := FALSE;
+ bSet5 := FALSE;
+ bSet6 := FALSE;
+ while i <= xy.NumPoints do
+ begin
+ p := xy.XValue_pt[i];
+ q := xy.YValue_pt[i];
+ if (p >= -1.0) and (p <= 1.0) then
+ bValid := TRUE;
+ if bValid then
+ begin
+ if not bSet1 then
+ begin
+ if p <= -0.5 then
+ begin
+ WV_curveP3load := p;
+ WV_curveQ3load := q;
+ end
+ else
+ begin
+ WV_curveP3load := -1.0;
+ WV_curveQ3load := 0.0;
+ dec(i); // re-scan
+ end;
+ bSet1 := TRUE;
+ end
+ else
+ if not bSet2 then
+ begin
+ if p <= -0.4 then
+ begin
+ WV_curveP2load := p;
+ WV_curveQ2load := q;
+ end
+ else
+ begin
+ WV_curveP2load := -0.5;
+ WV_curveQ2load := 0.0;
+ dec(i); // re-scan
+ end;
+ bSet2 := TRUE;
+ end
+ else
+ if not bSet3 then
+ begin
+ if p <= 0.0 then
+ begin
+ WV_curveP1load := p;
+ WV_curveQ1load := q;
+ end
+ else
+ begin
+ WV_curveP1load := -0.2;
+ WV_curveQ1load := 0.0;
+ dec(i); // re-scan
+ end;
+ bSet3 := TRUE;
+ end
+ else
+ if not bSet4 then
+ begin
+ if p <= 0.7 then
+ begin
+ WV_curveP1gen := p;
+ WV_curveQ1gen := q;
+ end
+ else
+ begin
+ WV_curveP1gen := 0.2;
+ WV_curveQ1gen := 0.0;
+ dec(i); // re-scan
+ end;
+ bSet4 := TRUE;
+ end
+ else
+ if not bSet5 then
+ begin
+ if p <= 0.8 then
+ begin
+ WV_curveP2gen := p;
+ WV_curveQ2gen := q;
+ end
+ else
+ begin
+ WV_curveP2gen := 0.5;
+ WV_curveQ2gen := 0.0;
+ dec(i); // re-scan
+ end;
+ bSet5 := TRUE;
+ end
+ else
+ if not bSet6 then
+ begin
+ if p <= 1.0 then
+ begin
+ WV_curveP3gen := p;
+ WV_curveQ3gen := q;
+ end
+ else
+ begin
+ WV_curveP3gen := 1.0;
+ WV_curveQ3gen := 0.0;
+ dec(i); // re-scan
+ end;
+ bSet6 := TRUE;
+ end;
+ end;
+ inc(i);
+ end;
+ // handle the edge cases when default zero watt-var points were not input
+ if WV_curveP1gen >= WV_curveP2gen then
+ WV_curveP1gen := WV_curveP2gen - 0.1;
+ if WV_curveP1load <= WV_curveP2load then
+ WV_curveP1load := WV_curveP2load + 0.1;
+ end;
+
+{* copied from InvControl!!
+ // Modes
+ NONE_MODE = 0;
+ VOLTVAR = 1;
+ VOLTWATT = 2;
+ DRC = 3;
+ WATTPF = 4;
+ WATTVAR = 5;
+ AVR = 6;
+
+ // Combi Modes
+ NONE_COMBMODE = 0;
+ VV_VW = 1;
+ VV_DRC = 2;
+*}
+ mode := Ord(pInv.ControlMode); // TODO: use enum values
+ combi := Ord(pInv.CombiMode); //TODO: use enum values
+ if combi = 1 then
+ begin
+ PF_enabled := FALSE;
+ VV_enabled := TRUE;
+ VW_enabled := TRUE;
+ end
+ else
+ if combi = 2 then
+ begin
+ PF_enabled := FALSE;
+ VV_enabled := TRUE;
+ end
+ else
+ if mode = 1 then
+ begin
+ PF_enabled := FALSE;
+ VV_enabled := TRUE;
+ end
+ else
+ if mode = 2 then
+ begin
+ PF_enabled := FALSE;
+ VW_enabled := TRUE;
+ end
+ else
+ if mode = 3 then // approximating AVR with DRC
+ begin
+ PF_enabled := False;
+ VV_enabled := True;
+ VV_vRefAutoModeEnabled := True;
+ VV_vRefOlrt := pInv.FDRCRollAvgWindowLength;
+ qvslope := 0.5 * (pInv.FArGraLowV + pInv.FArGraHiV);
+
+ if qvslope > 12.5 then
+ bCatB := True; // for catA, maximum slope would be 12.5
+
+ if bCatB then
+ q := 0.44
+ else
+ q := 0.25;
+
+ VV_curveQ1:=q;
+ VV_curveQ2:=VV_curveQ1;
+ VV_curveQ3:=-VV_curveQ1;
+ VV_curveQ4:=VV_curveQ3;
+ VV_curveV1:=0.50;
+ VV_curveV2:=1.0 - VV_curveQ2 / QVSlope;
+ VV_curveV3:=1.0 - VV_curveQ3 / QVSlope; // - because Q3 should be negative
+ VV_curveV4:=1.50;
+ end
+ else
+ if mode = 5 then
+ begin
+ PF_enabled := FALSE;
+ WV_enabled := TRUE;
+ end;
+end;
+
+procedure TIEEE1547Controller.PullFromExpControl(pExp: TExpControlObj);
+var
+ i: Integer;
+begin
+ pInvName.LocalName := pExp.Name;
+ pInvName.UUID := pExp.UUID;
+
+ i := 0;
+ while i < pExp.DERNameList.Count do
+ begin
+ pDERNames.Add(pExp.DERNameList.Strings[i]);
+ inc(i);
+ end;
+ pMonBuses.Clear;
+
+ if pExp.QMaxLead > CatBQmin then // catB estimate
+ SetDefaults(TRUE)
+ else
+ SetDefaults(FALSE);
+
+ PF_enabled := False;
+ VV_enabled := True;
+ VV_vRefAutoModeEnabled := TRUE;
+ VV_vRefOlrt := pExp.VregTau;
+ VV_olrt := pExp.TResponse;
+ VV_curveQ1 := pExp.QMaxLead;
+ VV_curveQ2 := VV_curveQ1;
+ VV_curveQ3 := -pExp.QMaxLag;
+ VV_curveQ4 := VV_curveQ3;
+ VV_curveV1 := 0.50;
+ VV_curveV2 := 1.0 - VV_curveQ2 / pExp.QVSlope;
+ VV_curveV3 := 1.0 - VV_curveQ3 / pExp.QVSlope; // - because Q3 should be negative
+ VV_curveV4 := 1.50;
+end;
+
+procedure TIEEE1547Controller.SetDefaults(bCatB: Boolean);
+begin
+ bNameplateSet := FALSE;
+ ND_acVmax := 1.05;
+ ND_acVmin := 0.95;
+ AD_pMax := 0.0;
+ AD_pMaxOverPF := 0.0;
+ AD_overPF := 0.0;
+ AD_pMaxUnderPF := 0.0;
+ AD_underPF := 0.0;
+ AD_sMax := 0.0;
+ AD_pMaxCharge := 0.0;
+ AD_apparentPowerChargeMax := 0.0;
+ AD_acVnom := 0.0;
+ AD_qMaxInj := 0.44;
+ if bCatB then
+ begin
+ ND_normalOPcatKind := 'catB';
+ AD_qMaxAbs := 0.44;
+ VV_curveV1 := 0.92;
+ VV_curveV2 := 0.98;
+ VV_curveV3 := 1.02;
+ VV_curveV4 := 1.08;
+ VV_curveQ1 := 0.44;
+ VV_curveQ2 := 0.0;
+ VV_curveQ3 := 0.0;
+ VV_curveQ4 := -0.44;
+ VV_olrt := 5.0;
+ WV_curveQ3load := 0.44;
+ end
+ else
+ begin
+ ND_normalOPcatKind := 'catA';
+ AD_qMaxAbs := 0.25;
+ VV_curveV1 := 0.90;
+ VV_curveV2 := 1.00;
+ VV_curveV3 := 1.00;
+ VV_curveV4 := 1.10;
+ VV_curveQ1 := 0.25;
+ VV_curveQ2 := 0.0;
+ VV_curveQ3 := 0.0;
+ VV_curveQ4 := -0.25;
+ VV_olrt := 10.0;
+ WV_curveQ3load := 0.25;
+ end;
+ VV_vRef := 1.0;
+ VV_vRefOlrt := 300.0;
+ Q_reactivePower := 0.0;
+ PF_powerFactor := 1.0;
+ VW_olrt := 10.0;
+ VW_curveV1 := 1.06;
+ VW_curveV2 := 1.10;
+ VW_curveP1 := 1.0;
+ VW_curveP2gen := 0.2;
+ VW_curveP2load := 0.0; // for storage, -1.0
+
+ WV_curveP1gen := 0.2;
+ WV_curveP2gen := 0.5;
+ WV_curveP3gen := 1.0;
+ WV_curveP1load := 0.0; // for storage, 0.2, 0.5, 1.0
+ WV_curveP2load := 0.0;
+ WV_curveP3load := 0.0;
+ WV_curveQ1gen := 0.0;
+ WV_curveQ2gen := 0.0;
+ WV_curveQ3gen := 0.44;
+ WV_curveQ1load := 0.0;
+ WV_curveQ2load := 0.0;
+
+ PF_constPFexcitationKind := 'inj';
+ VV_enabled := FALSE;
+ WV_enabled := FALSE;
+ PF_enabled := TRUE;
+ Q_enabled := FALSE;
+ VW_enabled := FALSE;
+ VV_vRefAutoModeEnabled := FALSE;
+end;
+
+procedure TIEEE1547Controller.FinishNameplate;
+begin
+ AD_overPF := AD_pMaxOverPF / AD_sMax;
+ AD_underPF := AD_pMaxUnderPF / AD_sMax;
+ bNameplateSet := TRUE;
+end;
+
+procedure TIEEE1547Controller.SetStorageNameplate(pBat: TStorage2Obj);
+begin
+ with pBat, StorageVars do
+ begin
+ AD_acVnom := PresentKV * 1000.0;
+ ND_acVmax := PresentKV * Vmaxpu * 1000.0;
+ ND_acVmin := PresentKV * Vmaxpu * 1000.0;
+ AD_sMax := kVARating * 1000.0;
+ AD_pMax := (kwRating * pctKwOut / 100.0) * 1000.0;
+ AD_pMaxOverPF := sqrt(FKvaRating * FKvaRating - FkvarLimit * FkvarLimit) * 1000.0;
+ AD_pMaxUnderPF := sqrt(FKvaRating * FKvaRating - FkvarLimitNeg * FkvarLimitNeg) * 1000.0;
+ AD_pMaxCharge := (kwRating * pctKwIn / 100.0) * 1000.0;
+ AD_apparentPowerChargeMax := FkvaRating * 1000.0;
+ AD_qMaxInj := Math.Min(Fkvarlimit, FkVARating) * 1000.0;
+ AD_qMaxAbs := Math.Min(FkvarlimitNeg, FkVARating) * 1000.0;
+ end;
+ FinishNameplate;
+end;
+
+procedure TIEEE1547Controller.SetPhotovoltaicNameplate(pPV: TPVSystem2Obj);
+var
+ qmaxinj, qmaxabs: Double;
+begin
+ with pPV, PVSystemVars do
+ begin
+ qmaxinj := Fkvarlimit;
+ if not kvarlimitset then
+ qmaxinj := 0.25 * kvarating; // unlike storage, defaults to category A
+
+ qmaxabs := FkvarlimitNeg;
+ if not kvarlimitnegset then
+ qmaxabs := 0.25 * kvarating; // unlike storage, defaults to category A
+
+ AD_acVnom := pPV.PresentKV * 1000.0;
+ ND_acVmax := pPV.PresentKV * pPV.Vmaxpu * 1000.0;
+ ND_acVmin := pPV.PresentKV * pPV.Vminpu * 1000.0;
+ AD_sMax := pPV.kVARating * 1000.0;
+ AD_pMax := pPV.Pmpp * 1000.0;
+ AD_pMaxOverPF := (sqrt(FKvaRating * FKvaRating - qmaxinj * qmaxinj)) * 1000.0;
+ AD_pMaxUnderPF := (sqrt(FKvaRating * FKvaRating - qmaxabs * qmaxabs)) * 1000.0;
+ AD_pMaxCharge := (0.0) * 1000.0;
+ AD_apparentPowerChargeMax := (0.0) * 1000.0;
+ AD_qMaxInj := qmaxinj * 1000.0;
+ AD_qMaxAbs := qmaxabs * 1000.0;
+ end;
+ FinishNameplate;
+end;
+
+procedure TIEEE1547Controller.SetElementNameplate(pElem: TDSSCktElement);
+begin
+ if bNameplateSet then
+ exit;
+ if pElem.DSSObjType = (PC_ELEMENT + PVSYSTEM_ELEMENT) then
+ SetPhotovoltaicNameplate(TPVSystem2Obj(pElem));
+ if pElem.DSSObjType = (PC_ELEMENT + STORAGE_ELEMENT) then
+ SetStorageNameplate(TStorage2Obj(pElem));
+ FinishNameplate;
+end;
+
+procedure TIEEE1547Controller.WriteCIM(prf: ProfileChoice);
+var
+ i: Integer;
+ pPV: TPVSystem2Obj;
+ pBat: TStorage2Obj;
+begin
+ with ex do
+ begin
+ FindSignalTerminals;
+ StartInstance(prf, 'DERIEEEType1', pInvName);
+ BooleanNode(prf, 'DynamicsFunctionBlock.enabled', TRUE);
+ BooleanNode (prf, 'DERIEEEType1.phaseToGroundApplicable', True); // seems to be the only OpenDSS option
+ BooleanNode (prf, 'DERIEEEType1.phaseToNeutralApplicable', False);
+ BooleanNode (prf, 'DERIEEEType1.phaseToPhaseApplicable', False);
+ with ex.ActiveCircuit do
+ begin
+ if pDERNames.Count < 1 then
+ begin
+ pBat := StorageElements.First;
+ while pBat <> NIL do
+ begin
+ if pBat.Enabled then
+ begin
+ RefNode(prf, 'DERDynamics.PowerElectronicsConnection', pBat);
+ SetStorageNameplate(pBat);
+ end;
+ pBat := StorageElements.Next;
+ end;
+ pPV := PVSystems.First;
+ while pPV <> NIL do
+ begin
+ if pPV.Enabled then
+ begin
+ RefNode(prf, 'DERDynamics.PowerElectronicsConnection', pPV);
+ SetPhotovoltaicNameplate(pPV);
+ end;
+ pPV := PVSystems.Next;
+ end;
+ end
+ else
+ begin
+ for i := 1 to pDERNames.Count do
+ begin
+ ActiveCircuit.SetElementActive(pDERNames.Strings[i - 1]);
+ RefNode(prf, 'DERDynamics.PowerElectronicsConnection', ActiveCktElement);
+ SetElementNameplate(ActiveCktElement);
+ end;
+ end;
+ end;
+ for i := Low(Signals) to High(Signals) do
+ RefNode(prf, 'DERDynamics.RemoteInputSignal', Signals[i]);
+ EndInstance(prf, 'DERIEEEType1');
+
+ for i := Low(Signals) to High(Signals) do
+ begin
+ StartInstance(prf, 'RemoteInputSignal', Signals[i]);
+ RemoteInputSignalEnum(prf, 'remoteBusVoltageAmplitude');
+ UuidNode(prf, 'RemoteInputSignal.Terminal', GetTermUuid(Signals[i].pElem, Signals[i].trm));
+ EndInstance(prf, 'RemoteInputSignal');
+ end;
+
+ pPlateName.LocalName := pInvName.LocalName;
+ pPlateName.UUID := GetDevUuid(I1547NameplateData, pInvName.LocalName, 1);
+ StartInstance(prf, 'DERNameplateData', pPlateName);
+ RefNode(prf, 'DERNameplateData.DERIEEEType1', pInvName);
+ NormalOpCatEnum(prf, ND_normalOPcatKind);
+ BooleanNode(prf, 'DERNameplateData.supportsConstPFmode', True);
+ BooleanNode(prf, 'DERNameplateData.supportsConstQmode', True);
+ BooleanNode(prf, 'DERNameplateData.supportsQVmode', True);
+ if ND_normalOPcatKind = 'catB' then
+ begin
+ BooleanNode(prf, 'DERNameplateData.supportsPVmode', True);
+ BooleanNode(prf, 'DERNameplateData.supportsQPmode', True);
+ end
+ else
+ begin
+ BooleanNode(prf, 'DERNameplateData.supportsPVmode', False);
+ BooleanNode(prf, 'DERNameplateData.supportsQPmode', False);
+ end;
+ BooleanNode(prf, 'DERNameplateData.supportsPFmode', False); // no frequency response in GridAPPS-D
+ DoubleNode(prf, 'DERNameplateData.acVmax', ND_acVmax);
+ DoubleNode(prf, 'DERNameplateData.acVmin', ND_acVmin);
+ EndInstance(prf, 'DERNameplateData');
+
+ pSetName.LocalName := pInvName.LocalName;
+ pSetName.UUID := GetDevUuid(I1547NameplateDataApplied, pSetName.LocalName, 1);
+ StartInstance(prf, 'DERNameplateDataApplied', pSetName);
+ RefNode(prf, 'DERNameplateDataApplied.DERNameplateData', pPlateName);
+ DoubleNode(prf, 'DERNameplateDataApplied.pMax', AD_pMax);
+ DoubleNode(prf, 'DERNameplateDataApplied.pMaxOverPF', AD_pMaxOverPF);
+ DoubleNode(prf, 'DERNameplateDataApplied.overPF', AD_overPF);
+ DoubleNode(prf, 'DERNameplateDataApplied.pMaxUnderPF', AD_pMaxUnderPF);
+ DoubleNode(prf, 'DERNameplateDataApplied.underPF', AD_underPF);
+ DoubleNode(prf, 'DERNameplateDataApplied.sMax', AD_sMax);
+ DoubleNode(prf, 'DERNameplateDataApplied.qMaxInj', AD_qMaxInj);
+ DoubleNode(prf, 'DERNameplateDataApplied.qMaxAbs', AD_qMaxAbs);
+ DoubleNode(prf, 'DERNameplateDataApplied.pMaxCharge', AD_pMaxCharge);
+ DoubleNode(prf, 'DERNameplateDataApplied.apparentPowerChargeMax', AD_apparentPowerChargeMax);
+ DoubleNode(prf, 'DERNameplateDataApplied.acVnom', AD_acVnom);
+ EndInstance(prf, 'DERNameplateDataApplied');
+
+ pSetName.UUID := GetDevUuid(I1547VoltVar, pSetName.LocalName, 1);
+ StartInstance(prf, 'VoltVarSettings', pSetName);
+ RefNode(prf, 'VoltVarSettings.DERIEEEType1', pInvName);
+ BooleanNode(prf, 'VoltVarSettings.enabled', VV_enabled);
+ BooleanNode(prf, 'VoltVarSettings.vRefAutoModeEnabled', VV_vRefAutoModeEnabled);
+ DoubleNode(prf, 'VoltVarSettings.vRef', VV_vRef);
+ DoubleNode(prf, 'VoltVarSettings.vRefOlrt', VV_vRefOlrt);
+ DoubleNode(prf, 'VoltVarSettings.curveV1', VV_curveV1);
+ DoubleNode(prf, 'VoltVarSettings.curveV2', VV_curveV2);
+ DoubleNode(prf, 'VoltVarSettings.curveV3', VV_curveV3);
+ DoubleNode(prf, 'VoltVarSettings.curveV4', VV_curveV4);
+ DoubleNode(prf, 'VoltVarSettings.curveQ1', VV_curveQ1);
+ DoubleNode(prf, 'VoltVarSettings.curveQ2', VV_curveQ2);
+ DoubleNode(prf, 'VoltVarSettings.curveQ3', VV_curveQ3);
+ DoubleNode(prf, 'VoltVarSettings.curveQ4', VV_curveQ4);
+ DoubleNode(prf, 'VoltVarSettings.olrt', VV_olrt);
+ EndInstance(prf, 'VoltVarSettings');
+
+ pSetName.UUID := GetDevUuid(I1547WattVar, pSetName.LocalName, 1);
+ StartInstance(prf, 'WattVarSettings', pSetName);
+ RefNode(prf, 'WattVarSettings.DERIEEEType1', pInvName);
+ BooleanNode(prf, 'WattVarSettings.enabled', WV_enabled);
+ DoubleNode(prf, 'WattVarSettings.curveP1gen', WV_curveP1gen);
+ DoubleNode(prf, 'WattVarSettings.curveP2gen', WV_curveP2gen);
+ DoubleNode(prf, 'WattVarSettings.curveP3gen', WV_curveP3gen);
+ DoubleNode(prf, 'WattVarSettings.curveQ1gen', WV_curveQ1gen);
+ DoubleNode(prf, 'WattVarSettings.curveQ2gen', WV_curveQ2gen);
+ DoubleNode(prf, 'WattVarSettings.curveQ3gen', WV_curveQ3gen);
+ DoubleNode(prf, 'WattVarSettings.curveP1load', WV_curveP1load);
+ DoubleNode(prf, 'WattVarSettings.curveP2load', WV_curveP2load);
+ DoubleNode(prf, 'WattVarSettings.curveP3load', WV_curveP3load);
+ DoubleNode(prf, 'WattVarSettings.curveQ1load', WV_curveQ1load);
+ DoubleNode(prf, 'WattVarSettings.curveQ2load', WV_curveQ2load);
+ DoubleNode(prf, 'WattVarSettings.curveQ3load', WV_curveQ3load);
+ EndInstance(prf, 'WattVarSettings');
+
+ pSetName.UUID := GetDevUuid(I1547ConstPF, pSetName.LocalName, 1);
+ StartInstance(prf, 'ConstantPowerFactorSettings', pSetName);
+ RefNode(prf, 'ConstantPowerFactorSettings.DERIEEEType1', pInvName);
+ BooleanNode(prf, 'ConstantPowerFactorSettings.enabled', PF_enabled);
+ PowerFactorExcitationEnum(prf, PF_constPFexcitationKind);
+ DoubleNode(prf, 'ConstantPowerFactorSettings.powerFactor', PF_powerFactor);
+ EndInstance(prf, 'ConstantPowerFactorSettings');
+
+ pSetName.UUID := GetDevUuid(I1547ConstQ, pSetName.LocalName, 1);
+ StartInstance(prf, 'ConstantReactivePowerSettings', pSetName);
+ RefNode(prf, 'ConstantReactivePowerSettings.DERIEEEType1', pInvName);
+ BooleanNode(prf, 'ConstantReactivePowerSettings.enabled', Q_enabled);
+ DoubleNode(prf, 'ConstantReactivePowerSettings.reactivePower', Q_reactivePower);
+ EndInstance(prf, 'ConstantReactivePowerSettings');
+
+ pSetName.UUID := GetDevUuid(I1547VoltWatt, pSetName.LocalName, 1);
+ StartInstance(prf, 'VoltWattSettings', pSetName);
+ RefNode(prf, 'VoltWattSettings.DERIEEEType1', pInvName);
+ BooleanNode(prf, 'VoltWattSettings.enabled', VW_enabled);
+ DoubleNode(prf, 'VoltWattSettings.curveV1', VW_curveV1);
+ DoubleNode(prf, 'VoltWattSettings.curveV2', VW_curveV2);
+ DoubleNode(prf, 'VoltWattSettings.curveP1', VW_curveP1);
+ DoubleNode(prf, 'VoltWattSettings.curveP2gen', VW_curveP2gen);
+ DoubleNode(prf, 'VoltWattSettings.curveP2load', VW_curveP2load);
+ DoubleNode(prf, 'VoltWattSettings.olrt', VW_olrt);
+ EndInstance(prf, 'VoltWattSettings');
+ end; // with ex
+end;
+
+///////// end helper class for exporting IEEE 1547 model parameters /////////////
+
+
procedure TCIMExporterHelper.StartCIMFile(F: TFileStream; FileNm: String; prf: ProfileChoice);
begin
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, '');
FSWriteln(F, '');
@@ -1921,15 +3017,19 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
pLoad: TLoadObj;
pVsrc: TVsourceObj;
pGen: TGeneratorObj;
- pPV: TPVSystemObj;
- pBat: TStorageObj;
+ pPV: TPVSystem2Obj;
+ pBat: TStorage2Obj;
pCap: TCapacitorObj;
pCapC: TCapControlObj;
pXf: TTransfObj;
+ pAuto: TAutoTransObj;
pReg: TRegControlObj;
pLine: TLineObj;
pReac: TReactorObj;
+ pInv: TInvControl2Obj;
+ pExp : TExpControlObj;
+ pI1547: TIEEE1547Controller;
clsLnCd: TLineCode;
clsGeom: TLineGeometry;
@@ -1976,8 +3076,8 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
i2 := ActiveCircuit.Transformers.Count * 11; // bank, info, 3 wdg, 3 wdg info, 3sctest
StartUuidList(i1 + i2);
end;
- StartBankList(ActiveCircuit.Transformers.Count);
- StartOpLimitList(ActiveCircuit.Lines.Count);
+ StartBankList(ActiveCircuit.Transformers.Count + ActiveCircuit.AutoTransformers.Count);
+ StartOpLimitList(ActiveCircuit.Lines.Count + ActiveCircuit.Transformers.Count + ActiveCircuit.AutoTransformers.Count);
DSSInfoMessageDlg(FileNm + '<=' + ActiveCircuit.Name + '<-' + Substation + '<-' + SubGeographicRegion + '<-' + GeographicRegion);
@@ -2210,6 +3310,9 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
StartInstance(FunPrf, 'PhotovoltaicUnit', pName1);
geoUUID := GetDevUuid(SolarLoc, pPV.localName, 1);
UuidNode(GeoPrf, 'PowerSystemResource.Location', geoUUID);
+ DoubleNode(EpPrf, 'PowerElectronicsUnit.maxP', pPV.Pmpp * 1000.0);
+ with pPV do
+ DoubleNode(EpPrf, 'PowerElectronicsUnit.minP', (min (FPctCutIn, FPctCutOut) * kVARating / 100.0) * 1000.0);
EndInstance(FunPrf, 'PhotovoltaicUnit');
StartInstance(FunPrf, 'PowerElectronicsConnection', pPV);
CircuitNode(FunPrf, ActiveCircuit);
@@ -2218,13 +3321,20 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
// if FD.Separate then StartFreeInstance (SshPrf, 'PowerElectronicsConnection', pPV.UUID);
DoubleNode(SshPrf, 'PowerElectronicsConnection.p', pPV.Presentkw * 1000.0);
DoubleNode(SshPrf, 'PowerElectronicsConnection.q', pPV.Presentkvar * 1000.0);
+ ConverterControlEnum(SshPrf, pPV.VarMode, pPV.UsingCIMDynamics);
// if FD.Separate then EndInstance (SshPrf, 'PowerElectronicsConnection');
DoubleNode(EpPrf, 'PowerElectronicsConnection.ratedS', pPV.PVSystemVars.fkvarating * 1000.0);
- DoubleNode(EpPrf, 'PowerElectronicsConnection.ratedU', pPV.Presentkv * 1000.0);
+ if pPV.nphases = 1 then
+ DoubleNode(EpPrf, 'PowerElectronicsConnection.ratedU', pPV.Presentkv * 1000.0 * sqrt(3.0))
+ else
+ DoubleNode(EpPrf, 'PowerElectronicsConnection.ratedU', pPV.Presentkv * 1000.0);
+
+ DoubleNode(EpPrf, 'PowerElectronicsConnection.maxQ', pPV.PVSystemVars.Fkvarlimit * 1000.0);
+ DoubleNode(EpPrf, 'PowerElectronicsConnection.minQ', -pPV.PVSystemVars.FkvarlimitNeg * 1000.0);
UuidNode(GeoPrf, 'PowerSystemResource.Location', geoUUID);
EndInstance(FunPrf, 'PowerElectronicsConnection');
AttachSolarPhases(pPV, geoUUID);
- // we want the location using PV unit name
+ // we want the location using PV unit name
WriteReferenceTerminals(pPV, pPV.UUID);
s := pPV.LocalName;
pPV.LocalName := pName1.LocalName;
@@ -2242,6 +3352,8 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
pName1.LocalName := pBat.Name; // + '_Cells';
pName1.UUID := GetDevUuid(Battery, pBat.LocalName, 1);
StartInstance(FunPrf, 'BatteryUnit', pName1);
+ DoubleNode(EpPrf, 'PowerElectronicsUnit.maxP', (pBat.StorageVars.kwRating * pBat.pctKwOut / 100.0) * 1000.0);
+ DoubleNode(EpPrf, 'PowerElectronicsUnit.minP', -(pBat.StorageVars.kwRating * pBat.pctKwIn / 100.0) * 1000.0);
DoubleNode(SshPrf, 'BatteryUnit.ratedE', pBat.StorageVars.kwhRating * 1000.0);
DoubleNode(SshPrf, 'BatteryUnit.storedE', pBat.StorageVars.kwhStored * 1000.0);
BatteryStateEnum(SshPrf, pBat.StorageState);
@@ -2254,12 +3366,19 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
DoubleNode(EpPrf, 'PowerElectronicsConnection.maxIFault', 1.0 / pBat.MinModelVoltagePU);
DoubleNode(SshPrf, 'PowerElectronicsConnection.p', pBat.Presentkw * 1000.0);
DoubleNode(SshPrf, 'PowerElectronicsConnection.q', pBat.Presentkvar * 1000.0);
- DoubleNode(EpPrf, 'PowerElectronicsConnection.ratedS', pBat.StorageVars.kvarating * 1000.0);
- DoubleNode(EpPrf, 'PowerElectronicsConnection.ratedU', pBat.Presentkv * 1000.0);
+ ConverterControlEnum(SshPrf, pBat.VarMode, pBat.UsingCIMDynamics);
+ DoubleNode(EpPrf, 'PowerElectronicsConnection.ratedS', pBat.kvarating * 1000.0);
+ if pBat.nphases = 1 then
+ DoubleNode(EpPrf, 'PowerElectronicsConnection.ratedU', pBat.Presentkv * 1000.0 * sqrt(3.0))
+ else
+ DoubleNode(EpPrf, 'PowerElectronicsConnection.ratedU', pBat.Presentkv * 1000.0);
+
+ DoubleNode(EpPrf, 'PowerElectronicsConnection.maxQ', Math.Min(pBat.StorageVars.Fkvarlimit, pBat.StorageVars.FkVArating) * 1000.0);
+ DoubleNode(EpPrf, 'PowerElectronicsConnection.minQ', -Math.Min(pBat.StorageVars.FkvarlimitNeg, pBat.StorageVars.FkVArating) * 1000.0);
UuidNode(GeoPrf, 'PowerSystemResource.Location', geoUUID);
EndInstance(FunPrf, 'PowerElectronicsConnection');
AttachStoragePhases(pBat, geoUUID);
- // we want the location using battery unit name
+ // we want the location using battery unit name
WriteReferenceTerminals(pBat, pBat.UUID);
s := pBat.LocalName;
pBat.LocalName := pName1.LocalName;
@@ -2269,6 +3388,35 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
pBat := ActiveCircuit.StorageElements.Next;
end;
+ with ActiveCircuit do
+ begin
+ if (InvControls.Count > 0) or (ExpControls.Count > 0) then
+ begin
+ pI1547 := TIEEE1547Controller.Create(self);
+ pInv := InvControls.First;
+ while pInv <> NIL do
+ begin
+ if pInv.Enabled then
+ begin
+ pI1547.PullFromInvControl(pInv);
+ pI1547.WriteCIM(DynPrf);
+ end;
+ pInv := InvControls.Next;
+ end;
+ pExp := ExpControls.First;
+ while pExp <> NIL do
+ begin
+ if pExp.Enabled then
+ begin
+ pI1547.PullFromExpControl(pExp);
+ pI1547.WriteCIM(DynPrf);
+ end;
+ pExp := ExpControls.Next;
+ end;
+ pI1547.Free;
+ end;
+ end;
+
pVsrc := ActiveCircuit.Sources.First; // pIsrc are in the same list
while pVsrc <> NIL do
begin
@@ -2342,7 +3490,7 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
end;
DoubleNode(EpPrf, 'ShuntCompensator.aVRDelay', val);
- if Connection = 0 then
+ if Connection = TCapacitorConnection.Delta then
begin
ShuntConnectionKindNode(FunPrf, 'ShuntCompensator', 'Y');
BooleanNode(FunPrf, 'ShuntCompensator.grounded', TRUE); // TODO - check bus 2
@@ -2357,10 +3505,15 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
DoubleNode(EpPrf, 'LinearShuntCompensator.g0PerSection', 0.0);
IntegerNode(EpPrf, 'ShuntCompensator.normalSections', NumSteps);
IntegerNode(EpPrf, 'ShuntCompensator.maximumSections', NumSteps);
+ val := 0;
+ for i := 1 to NumSteps do
+ if States[i] > 0 then
+ val := val + 1.0;
+ DoubleNode(SshPrf, 'ShuntCompensator.sections', val);
geoUUID := GetDevUuid(CapLoc, pCap.localName, 1);
UuidNode(GeoPrf, 'PowerSystemResource.Location', geoUUID);
EndInstance(FunPrf, 'LinearShuntCompensator');
- AttachCapPhases(pCap, geoUUID);
+ AttachCapPhases(pCap, geoUUID, val);
WriteTerminals(pCap, geoUUID, crsUUID, pCap.NormAmps, pCap.EmergAmps);
end;
end;
@@ -2375,10 +3528,8 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
StartInstance(FunPrf, 'RegulatingControl', pCapC);
UuidNode(GeoPrf, 'PowerSystemResource.Location', GetDevUuid(CapLoc, This_Capacitor.Name, 1));
RefNode(FunPrf, 'RegulatingControl.RegulatingCondEq', This_Capacitor);
- i1 := GetCktElementIndex(ElementName); // Global function
- UuidNode(FunPrf, 'RegulatingControl.Terminal',
- GetTermUuid(ActiveCircuit.CktElements.Get(i1), ElementTerminal));
- s := FirstPhaseString(ActiveCircuit.CktElements.Get(i1), 1);
+ UuidNode(FunPrf, 'RegulatingControl.Terminal', GetTermUuid(MonitoredElement, ElementTerminal));
+ s := FirstPhaseString(MonitoredElement, 1);
if PTPhase > 0 then
MonitoredPhaseNode(FunPrf, Char(Ord(s[1]) + PTPhase - 1))
else
@@ -2423,27 +3574,179 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
pCapC := ActiveCircuit.CapControls.Next;
end;
- // begin the transformers;
- // 1. if balanced three-phase and no XfmrCode, use PowerTransformerEnd(s), mesh impedances and core admittances with no tanks
- // 2. with XfmrCode, write TransformerTank, TransformerTankEnd(s) and references to TransformerTankInfoInfo
- // 3. otherwise, write TransformerTank, then create and reference TransformerTankInfo classes
+ // size the auxiliary winding, mesh, and core lists for transformer export
+ maxWdg := 3; // start with the size of autos
+ pXf := ActiveCircuit.Transformers.First;
+ while pXf <> NIL do
+ begin
+ if pXf.Enabled then
+ if pXf.NumWindings > maxWdg then
+ maxWdg := pXf.NumWindings;
+ pXf := ActiveCircuit.Transformers.Next;
+ end;
+
+ if MaxWdg > 0 then
+ begin
+ SetLength(WdgList, maxWdg);
+ SetLength(CoreList, maxWdg);
+ SetLength(MeshList, (maxWdg - 1) * maxWdg div 2);
+ for i := 1 to maxWdg do
+ WdgList[i - 1] := TNamedObject.Create('dummy');
+ CoreList[0] := TNamedObject.Create('dummy');
+ for i := 1 to ((maxWdg - 1) * maxWdg div 2) do
+ MeshList[i - 1] := TNamedObject.Create('dummy');
+ end;
+
+ // do the autotransformers as balanced, three-phase autos, PowerTransformerEnd(s), mesh impedances and core admittances
+ // only considering 2 windings, vector group YNa, or 3 windings, vector group YNad1
+ pAuto := ActiveCircuit.AutoTransformers.First;
+ while pAuto <> NIL do
+ begin
+ if pAuto.Enabled then
+ with pAuto do
+ begin
+ if XfmrBank = '' then
+ sBank := '=' + pAuto.Name
+ else
+ sBank := XfmrBank;
+ pBank := GetBank(sBank);
+ if pBank = NIL then
+ begin
+ pBank := TCIMBankObject.Create(maxWdg);
+ pBank.localName := sBank;
+ pBank.UUID := GetDevUuid(Bank, sBank, 0);
+ AddBank(pBank);
+ end;
+ pBank.AddAutoTransformer(self, pAuto);
+ geoUUID := GetDevUuid(XfLoc, pAuto.Name, 1);
+ WritePositions(pAuto, geoUUID, crsUUID);
+ // pre-make the winding, mesh and core name objects for easy reference
+ for i := 1 to NumWindings do
+ begin
+ WdgList[i - 1].localName := pAuto.Name + '_End_' + IntToStr(i);
+ WdgList[i - 1].UUID := GetDevUuid(Wdg, pAuto.Name, i);
+ end;
+ CoreList[0].LocalName := pAuto.Name + '_Yc';
+ CoreList[0].UUID := GetDevUuid(XfCore, pAuto.Name, 1);
+ for i := 1 to ((maxWdg - 1) * maxWdg div 2) do
+ begin
+ MeshList[i - 1].localName := pAuto.Name + '_Zsc_' + IntToStr(i);
+ MeshList[i - 1].UUID := GetDevUuid(XfMesh, pAuto.Name, i);
+ end;
+ val := BaseKVLL[1]; // write core Y
+ zbase := 1000.0 * val * val / WdgKva[1];
+ StartInstance(EpPrf, 'TransformerCoreAdmittance', CoreList[0]);
+ val := pAuto.pctNoLoadLoss / 100.0 / zbase;
+ DoubleNode(EpPrf, 'TransformerCoreAdmittance.g', val);
+ DoubleNode(EpPrf, 'TransformerCoreAdmittance.g0', val);
+ val := pAuto.pctImag / 100.0 / zbase;
+ DoubleNode(EpPrf, 'TransformerCoreAdmittance.b', val);
+ DoubleNode(EpPrf, 'TransformerCoreAdmittance.b0', val);
+ RefNode(EpPrf, 'TransformerCoreAdmittance.TransformerEnd', WdgList[0]);
+ EndInstance(EpPrf, 'TransformerCoreAdmittance');
+ seq := 1; // write mesh Z
+ for i := 1 to NumWindings do
+ begin
+ for k := i + 1 to NumWindings do
+ begin
+ val := BaseKVLL[i];
+ zbase := 1000.0 * val * val / WdgKva[i];
+ StartInstance(EpPrf, 'TransformerMeshImpedance', MeshList[seq - 1]);
+ val := zbase * (WdgResistance[i] + WdgResistance[k]);
+ DoubleNode(EpPrf, 'TransformerMeshImpedance.r', val);
+ DoubleNode(EpPrf, 'TransformerMeshImpedance.r0', val);
+ val := zbase * XscVal[seq];
+ inc(seq);
+ DoubleNode(EpPrf, 'TransformerMeshImpedance.x', val);
+ DoubleNode(EpPrf, 'TransformerMeshImpedance.x0', val);
+ RefNode(EpPrf, 'TransformerMeshImpedance.FromTransformerEnd', WdgList[i - 1]);
+ RefNode(EpPrf, 'TransformerMeshImpedance.ToTransformerEnd', WdgList[k - 1]);
+ EndInstance(EpPrf, 'TransformerMeshImpedance');
+ end;
+ end;
+ // write the Ends, and a Terminal with operational limit for each End
+ for i := 1 to NumWindings do
+ begin
+ StartInstance(FunPrf, 'PowerTransformerEnd', WdgList[i - 1]);
+ RefNode(FunPrf, 'PowerTransformerEnd.PowerTransformer', pBank);
+ DoubleNode(EpPrf, 'PowerTransformerEnd.ratedS', 1000 * WdgKva[i]);
+ DoubleNode(EpPrf, 'PowerTransformerEnd.ratedU', 1000 * Winding^[i].kvll);
+ zbase := 1000.0 * BaseKVLL[i] * BaseKVLL[i] / WdgKva[i];
+ DoubleNode(EpPrf, 'PowerTransformerEnd.r', zbase * WdgResistance[i]);
+ if i = 1 then
+ begin
+ WindingConnectionKindNode(FunPrf, 'Y');
+ IntegerNode(FunPrf, 'PowerTransformerEnd.phaseAngleClock', 0);
+ BooleanNode(FunPrf, 'TransformerEnd.grounded', FALSE);
+ end
+ else
+ if i = 2 then
+ begin
+ WindingConnectionKindNode(FunPrf, 'A');
+ IntegerNode(FunPrf, 'PowerTransformerEnd.phaseAngleClock', 0);
+ BooleanNode(FunPrf, 'TransformerEnd.grounded', TRUE);
+ DoubleNode(EpPrf, 'TransformerEnd.rground', 0.0); // no rneut or xneut for autotrans
+ DoubleNode(EpPrf, 'TransformerEnd.xground', 0.0);
+ end
+ else
+ begin
+ WindingConnectionKindNode(FunPrf, 'D');
+ IntegerNode(FunPrf, 'PowerTransformerEnd.phaseAngleClock', 1);
+ BooleanNode(FunPrf, 'TransformerEnd.grounded', FALSE);
+ end;
+ IntegerNode(FunPrf, 'TransformerEnd.endNumber', i);
+ j := pAuto.Terminals[i - 1].BusRef;
+ pName2.LocalName := pAuto.Name + '_T' + IntToStr(i);
+ pName2.UUID := GetTermUuid(pAuto, i);
+ RefNode(FunPrf, 'TransformerEnd.Terminal', pName2);
+ UuidNode(FunPrf, 'TransformerEnd.BaseVoltage', GetBaseVUuid(sqrt(3.0) * ActiveCircuit.Buses^[j].kVBase));
+ EndInstance(FunPrf, 'PowerTransformerEnd');
+ // write the Terminal for this End
+ StartInstance(FunPrf, 'Terminal', pName2);
+ RefNode(FunPrf, 'Terminal.ConductingEquipment', pBank);
+ IntegerNode(FunPrf, 'ACDCTerminal.sequenceNumber', i);
+ FD.WriteCimLn(TopoPrf, Format(' ',
+ [ActiveCircuit.Buses[j].CIM_ID]));
+ if i = 1 then
+ begin // write the current limit on HV winding, assuming that's winding 1
+ LimitName := GetOpLimIName(pAuto.NormAmps, pAuto.EmergAmps);
+ pILimit := GetOpLimit(LimitName);
+ if pILimit = NIL then
+ begin
+ pILimit := TCIMOpLimitObject.Create(pAuto.NormAmps, pAuto.EmergAmps);
+ pILimit.localName := LimitName;
+ pILimit.UUID := GetDevUuid(OpLimI, LimitName, 0);
+ AddOpLimit(pILimit);
+ end;
+ LimiTUuid := GetDevUuid(OpLimI, LimitName, 0);
+ UuidNode(FunPrf, 'ACDCTerminal.OperationalLimitSet', LimiTUuid);
+ end;
+ EndInstance(FunPrf, 'Terminal');
+ end;
+ end;
+ pAuto := ActiveCircuit.AutoTransformers.Next;
+ end;
+
+ // begin the transformers;
+ // 1. if balanced three-phase and no XfmrCode, use PowerTransformerEnd(s), mesh impedances and core admittances with no tanks
+ // 2. with XfmrCode, write TransformerTank, TransformerTankEnd(s) and references to TransformerTankInfoInfo
+ // 3. otherwise, write TransformerTank, then create and reference TransformerTankInfo classes
- // for case 3, it's better to identify and create the info classes first
- // TODO: side effect is that these transformers will reference XfmrCode until the text file is reloaded. Solution results should be the same.
+ // for case 3, it's better to identify and create the info classes first
+ // TODO: side effect is that these transformers will reference XfmrCode until the text file is reloaded. Solution results should be the same.
pXf := ActiveCircuit.Transformers.First;
while pXf <> NIL do
begin
if pXf.Enabled then
begin
- if (length(pXf.XfmrCode) < 1) and (pXf.NPhases <> 3) then
+ if (pXf.XfmrCodeObj = NIL) and (pXf.NPhases <> 3) then
begin
sBank := 'CIMXfmrCode_' + pXf.Name;
clsXfCd.NewObject(sBank);
- clsXfCd.Code := sBank;
- pXfCd := DSS.ActiveXfmrCodeObj;
+ pXfCd := clsXfCd.Find(sBank);
pXfCd.UUID := GetDevUuid(TankInfo, pXfCd.Name, 1);
pXfCd.PullFromTransformer(pXf);
- pXf.XfmrCode := pXfCd.Name;
+ pXf.XfmrCodeObj := pXfCd;
end;
end;
pXf := ActiveCircuit.Transformers.Next;
@@ -2454,45 +3757,31 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
while pXfCd <> NIL do
begin
WriteXfmrCode(pXfCd);
- // link to the transformers using this XfmrCode
- pName1.LocalName := 'TankAsset_' + pXfCd.Name;
- pName1.UUID := GetDevUuid(TankAsset, pXfCd.Name, 1);
- StartInstance(CatPrf, 'Asset', pName1);
- RefNode(CatPrf, 'Asset.AssetInfo', pXfCd);
- pXf := ActiveCircuit.Transformers.First;
- while pXf <> NIL do
- begin
- if pXf.XfmrCode = pXfCd.Name then
- RefNode(CatPrf, 'Asset.PowerSystemResources', pXf);
- pXf := ActiveCircuit.Transformers.Next;
- end;
- EndInstance(CatPrf, 'Asset');
pXfCd := clsXfCd.ElementList.Next;
end;
- // create all the banks (CIM PowerTransformer)
- maxWdg := 0;
+ // create all the banks (CIM PowerTransformer) for regular transformers
pXf := ActiveCircuit.Transformers.First;
while pXf <> NIL do
begin
if pXf.Enabled then
- if pXf.NumberOfWindings > maxWdg then
- maxWdg := pXf.NumberofWindings;
+ begin
+ if pXf.XfmrBank = '' then
+ sBank := '=' + pXf.Name
+ else
+ sBank := pXf.XfmrBank;
+ pBank := GetBank(sBank);
+ if pBank = NIL then
+ begin
+ pBank := TCIMBankObject.Create(maxWdg);
+ pBank.localName := sBank;
+ pBank.UUID := GetDevUuid(Bank, sBank, 0);
+ AddBank(pBank);
+ end;
+ end;
pXf := ActiveCircuit.Transformers.Next;
end;
- if MaxWdg > 0 then
- begin
- SetLength(WdgList, maxWdg);
- SetLength(CoreList, maxWdg);
- SetLength(MeshList, (maxWdg - 1) * maxWdg div 2);
- for i := 1 to maxWdg do
- WdgList[i - 1] := TNamedObject.Create('dummy');
- CoreList[0] := TNamedObject.Create('dummy');
- for i := 1 to ((maxWdg - 1) * maxWdg div 2) do
- MeshList[i - 1] := TNamedObject.Create('dummy');
- end;
-
pXf := ActiveCircuit.Transformers.First;
while pXf <> NIL do
begin
@@ -2514,20 +3803,20 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
pXf := ActiveCircuit.Transformers.Next;
end;
- // write all the transformers, according to the three cases
+ // write all the transformers, according to the three cases
pXf := ActiveCircuit.Transformers.First;
while pXf <> NIL do
begin
if pXf.Enabled then
with pXf do
begin
- // collect this transformer into tanks and banks, and make a location
+ // collect this transformer into tanks and banks, and make a location
if pXf.XfmrBank = '' then
sBank := '=' + pXf.Name
else
sBank := pXf.XfmrBank;
bTanks := TRUE; // defaults to case 2 or 3 if XfmrCode exists
- if (length(pXf.XfmrCode) < 1) and (pXf.NPhases = 3) then
+ if (pXf.XfmrCodeObj = NIL) and (pXf.NPhases = 3) then
bTanks := FALSE; // case 1, balanced three-phase
pBank := GetBank(sBank);
@@ -2538,6 +3827,8 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
begin
StartInstance(FunPrf, 'TransformerTank', pXf);
CircuitNode(FunPrf, ActiveCircuit);
+ pXfCd := pXf.XfmrCodeObj as TXfmrCodeObj;
+ RefNode(FunPrf, 'TransformerTank.TransformerTankInfo', pXfCd);
RefNode(FunPrf, 'TransformerTank.PowerTransformer', pBank);
UuidNode(GeoPrf, 'PowerSystemResource.Location', geoUUID);
EndInstance(FunPrf, 'TransformerTank');
@@ -2548,8 +3839,8 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
WritePositions(pXf, geoUUID, crsUUID);
end;
- // make the winding, mesh and core name objects for easy reference
- for i := 1 to NumberOfWindings do
+ // make the winding, mesh and core name objects for easy reference
+ for i := 1 to NumWindings do
begin
WdgList[i - 1].localName := pXf.Name + '_End_' + IntToStr(i);
WdgList[i - 1].UUID := GetDevUuid(Wdg, pXf.Name, i);
@@ -2567,18 +3858,18 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
val := BaseKVLL[1];
zbase := 1000.0 * val * val / WdgKva[1];
StartInstance(EpPrf, 'TransformerCoreAdmittance', CoreList[0]);
- val := pXf.noLoadLossPct / 100.0 / zbase;
+ val := pXf.pctNoLoadLoss / 100.0 / zbase;
DoubleNode(EpPrf, 'TransformerCoreAdmittance.g', val);
DoubleNode(EpPrf, 'TransformerCoreAdmittance.g0', val);
- val := pXf.imagPct / 100.0 / zbase;
+ val := pXf.pctImag / 100.0 / zbase;
DoubleNode(EpPrf, 'TransformerCoreAdmittance.b', val);
DoubleNode(EpPrf, 'TransformerCoreAdmittance.b0', val);
RefNode(EpPrf, 'TransformerCoreAdmittance.TransformerEnd', WdgList[0]);
EndInstance(EpPrf, 'TransformerCoreAdmittance');
seq := 1; // write mesh Z
- for i := 1 to NumberOfWindings do
+ for i := 1 to NumWindings do
begin
- for k := i + 1 to NumberOfWindings do
+ for k := i + 1 to NumWindings do
begin
val := BaseKVLL[i];
zbase := 1000.0 * val * val / WdgKva[i];
@@ -2597,13 +3888,13 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
end;
end;
- // write the Ends, and a Terminal for each End
- for i := 1 to NumberOfWindings do
+ // write the Ends, and a Terminal for each End
+ for i := 1 to NumWindings do
begin
if bTanks then
begin
StartInstance(FunPrf, 'TransformerTankEnd', WdgList[i - 1]);
- XfmrPhasesEnum(FunPrf, pXf, i);
+ XfmrTankPhasesAndGround(FunPrf, EpPrf, pXf, i);
RefNode(FunPrf, 'TransformerTankEnd.TransformerTank', pXf);
end
else
@@ -2625,33 +3916,31 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
IntegerNode(FunPrf, 'PowerTransformerEnd.phaseAngleClock', 1)
else
IntegerNode(FunPrf, 'PowerTransformerEnd.phaseAngleClock', 0);
+ j := (i - 1) * pXf.NConds + pXf.Nphases + 1;
+ if (Winding^[i].Connection = 1) then
+ begin // delta
+ BooleanNode(FunPrf, 'TransformerEnd.grounded', FALSE);
+ end
+ else
+ if (pXf.NodeRef^[j] = 0) then
+ begin // last conductor is grounded solidly
+ BooleanNode(FunPrf, 'TransformerEnd.grounded', TRUE);
+ DoubleNode(EpPrf, 'TransformerEnd.rground', 0.0);
+ DoubleNode(EpPrf, 'TransformerEnd.xground', 0.0);
+ end
+ else
+ if (Winding^[i].Rneut < 0.0) then
+ begin // probably wye ungrounded
+ BooleanNode(FunPrf, 'TransformerEnd.grounded', FALSE);
+ end
+ else
+ begin // not delta, not wye solidly grounded or ungrounded
+ BooleanNode(FunPrf, 'TransformerEnd.grounded', TRUE);
+ DoubleNode(EpPrf, 'TransformerEnd.rground', Winding^[i].Rneut);
+ DoubleNode(EpPrf, 'TransformerEnd.xground', Winding^[i].Xneut);
+ end;
end;
IntegerNode(FunPrf, 'TransformerEnd.endNumber', i);
- j := (i - 1) * pXf.NConds + pXf.Nphases + 1;
-// Writeln (Format ('# %s wdg=%d conn=%d nterm=%d nref=%d',
-// [pXf.Name, i, Winding^[i].Connection, j, pXf.NodeRef^[j]]));
- if (Winding^[i].Connection = 1) then
- begin // delta
- BooleanNode(FunPrf, 'TransformerEnd.grounded', FALSE);
- end
- else
- if (pXf.NodeRef^[j] = 0) then
- begin // last conductor is grounded solidly
- BooleanNode(FunPrf, 'TransformerEnd.grounded', TRUE);
- DoubleNode(EpPrf, 'TransformerEnd.rground', 0.0);
- DoubleNode(EpPrf, 'TransformerEnd.xground', 0.0);
- end
- else
- if (Winding^[i].Rneut < 0.0) then
- begin // probably wye ungrounded
- BooleanNode(FunPrf, 'TransformerEnd.grounded', FALSE);
- end
- else
- begin // not delta, not wye solidly grounded or ungrounded
- BooleanNode(FunPrf, 'TransformerEnd.grounded', TRUE);
- DoubleNode(EpPrf, 'TransformerEnd.rground', Winding^[i].Rneut);
- DoubleNode(EpPrf, 'TransformerEnd.xground', Winding^[i].Xneut);
- end;
j := pXf.Terminals[i - 1].BusRef;
pName2.LocalName := pXf.Name + '_T' + IntToStr(i);
pName2.UUID := GetTermUuid(pXf, i);
@@ -2661,7 +3950,7 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
EndInstance(FunPrf, 'TransformerTankEnd')
else
EndInstance(FunPrf, 'PowerTransformerEnd');
- // write the Terminal for this End
+ // write the Terminal for this End
StartInstance(FunPrf, 'Terminal', pName2);
RefNode(FunPrf, 'Terminal.ConductingEquipment', pBank);
IntegerNode(FunPrf, 'ACDCTerminal.sequenceNumber', i);
@@ -2687,7 +3976,7 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
pXf := ActiveCircuit.Transformers.Next;
end;
- // finally, write all the transformer banks (CIM PowerTransformer)
+ // finally, write all the transformer banks (CIM PowerTransformer)
for i := Low(BankList) to High(BankList) do
begin
pBank := BankList[i];
@@ -2701,7 +3990,7 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
CircuitNode(FunPrf, ActiveCircuit);
StringNode(FunPrf, 'PowerTransformer.vectorGroup', pBank.vectorGroup);
UuidNode(GeoPrf, 'PowerSystemResource.Location',
- GetDevUuid(XfLoc, pBank.a_unit.Name, 1));
+ GetDevUuid(XfLoc, pBank.pd_unit.Name, 1));
EndInstance(FunPrf, 'PowerTransformer');
end;
@@ -2709,20 +3998,13 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
CoreList := NIL;
MeshList := NIL;
- // voltage regulators
+ // voltage regulators
pReg := ActiveCircuit.RegControls.First;
while (pReg <> NIL) do
begin
with pReg do
begin
- pName1.LocalName := pReg.LocalName + '_Info';
- pName1.UUID := GetDevUuid(TapInfo, pReg.LocalName, 1);
- StartInstance(CatPrf, 'TapChangerInfo', pName1);
- DoubleNode(CatPrf, 'TapChangerInfo.ptRatio', PT);
- DoubleNode(CatPrf, 'TapChangerInfo.ctRatio', CT / 0.2);
- DoubleNode(CatPrf, 'TapChangerInfo.ctRating', CT);
- EndInstance(CatPrf, 'TapChangerInfo');
-
+ v1 := Transformer.BaseVoltage[TrWinding] / PTRatio;
pName2.LocalName := pReg.LocalName + '_Ctrl';
pName2.UUID := GetDevUuid(TapCtrl, pReg.LocalName, 1);
StartInstance(FunPrf, 'TapChangerControl', pName2);
@@ -2731,25 +4013,28 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
MonitoredPhaseNode(FunPrf, FirstPhaseString(Transformer, TrWinding));
BooleanNode(FunPrf, 'RegulatingControl.enabled', pReg.Enabled);
BooleanNode(EpPrf, 'RegulatingControl.discrete', TRUE);
- DoubleNode(EpPrf, 'RegulatingControl.targetValue', TargetVoltage);
- DoubleNode(EpPrf, 'RegulatingControl.targetDeadband', BandVoltage);
- BooleanNode(EpPrf, 'TapChangerControl.lineDropCompensation', UseLineDrop);
- DoubleNode(EpPrf, 'TapChangerControl.lineDropR', LineDropR);
- DoubleNode(EpPrf, 'TapChangerControl.lineDropX', LineDropX);
- if UseReverseDrop then
+ DoubleNode(EpPrf, 'RegulatingControl.targetValue', Vreg);
+ DoubleNode(EpPrf, 'RegulatingControl.targetDeadband', Bandwidth);
+ BooleanNode(EpPrf, 'TapChangerControl.lineDropCompensation', LDCActive);
+ DoubleNode(EpPrf, 'TapChangerControl.lineDropR', R);
+ DoubleNode(EpPrf, 'TapChangerControl.lineDropX', X);
+ if IsReversible then
begin
- DoubleNode(EpPrf, 'TapChangerControl.reverseLineDropR', RevLineDropR);
- DoubleNode(EpPrf, 'TapChangerControl.reverseLineDropX', RevLineDropX)
+ DoubleNode(EpPrf, 'TapChangerControl.reverseLineDropR', revR);
+ DoubleNode(EpPrf, 'TapChangerControl.reverseLineDropX', revX)
end
else
begin
DoubleNode(EpPrf, 'TapChangerControl.reverseLineDropR', 0.0);
DoubleNode(EpPrf, 'TapChangerControl.reverseLineDropX', 0.0)
end;
- if UseLimit then
- DoubleNode(EpPrf, 'TapChangerControl.limitVoltage', VoltageLimit)
- else
- DoubleNode(EpPrf, 'TapChangerControl.limitVoltage', 0.0);
+ if VLimitActive then
+ // maxLimitVoltage only in OpenDSS
+ DoubleNode (EpPrf, 'TapChangerControl.maxLimitVoltage', Vlimit)
+ else
+ DoubleNode (EpPrf, 'TapChangerControl.maxLimitVoltage', MaxTap * v1);
+ DoubleNode (EpPrf, 'TapChangerControl.minLimitVoltage', MinTap * v1);
+
UuidNode(GeoPrf, 'PowerSystemResource.Location',
GetDevUuid(XfLoc, Transformer.Name, 1));
EndInstance(FunPrf, 'TapChangerControl');
@@ -2764,49 +4049,40 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
IntegerNode(EpPrf, 'TapChanger.lowStep', -NumTaps div 2);
IntegerNode(EpPrf, 'TapChanger.neutralStep', 0);
IntegerNode(EpPrf, 'TapChanger.normalStep', 0);
- DoubleNode(EpPrf, 'TapChanger.neutralU', 120.0 * PT);
- DoubleNode(EpPrf, 'TapChanger.initialDelay', InitialDelay);
- DoubleNode(EpPrf, 'TapChanger.subsequentDelay', SubsequentDelay);
+ DoubleNode(EpPrf, 'TapChanger.neutralU', v1 * PTRatio);
+ DoubleNode(EpPrf, 'TapChanger.initialDelay', TimeDelay);
+ DoubleNode(EpPrf, 'TapChanger.subsequentDelay', TapDelay);
BooleanNode(EpPrf, 'TapChanger.ltcFlag', TRUE);
BooleanNode(SshPrf, 'TapChanger.controlEnabled', pReg.Enabled);
DoubleNode(SshPrf, 'TapChanger.step', TapNum);
+ DoubleNode(EpPrf, 'TapChanger.ptRatio', PTRatio);
+ DoubleNode(EpPrf, 'TapChanger.ctRatio', CTRating / 0.2);
+ DoubleNode(EpPrf, 'TapChanger.ctRating', CTRating);
UuidNode(GeoPrf, 'PowerSystemResource.Location',
GetDevUuid(XfLoc, Transformer.Name, 1));
EndInstance(FunPrf, 'RatioTapChanger');
-
- pName2.LocalName := 'TapChangerAsset_' + pReg.LocalName;
- pName2.UUID := GetDevUuid(TapAsset, pReg.LocalName, 1);
- StartInstance(CatPrf, 'Asset', pName2);
- RefNode(CatPrf, 'Asset.AssetInfo', pName1);
- RefNode(CatPrf, 'Asset.PowerSystemResources', pReg);
- EndInstance(CatPrf, 'Asset');
end;
pReg := ActiveCircuit.RegControls.Next;
end;
// done with the transformers
- // series reactors, exported as lines
+ // series reactors, exported as SeriesCompensators
pReac := ActiveCircuit.Reactors.First;
while pReac <> NIL do
begin
if pReac.Enabled then
begin
- StartInstance(FunPrf, 'ACLineSegment', pReac);
+ StartInstance(FunPrf, 'SeriesCompensator', pReac);
CircuitNode(FunPrf, ActiveCircuit);
VbaseNode(FunPrf, pReac);
geoUUID := GetDevUuid(ReacLoc, pReac.Name, 1);
UuidNode(GeoPrf, 'PowerSystemResource.Location', geoUUID);
- DoubleNode(FunPrf, 'Conductor.length', 1.0);
- DoubleNode(EpPrf, 'ACLineSegment.r', pReac.SimpleR);
- DoubleNode(EpPrf, 'ACLineSegment.x', pReac.SimpleX);
- DoubleNode(EpPrf, 'ACLineSegment.bch', 0.0);
- DoubleNode(EpPrf, 'ACLineSegment.gch', 0.0);
- DoubleNode(EpPrf, 'ACLineSegment.r0', pReac.SimpleR);
- DoubleNode(EpPrf, 'ACLineSegment.x0', pReac.SimpleX);
- DoubleNode(EpPrf, 'ACLineSegment.b0ch', 0.0);
- DoubleNode(EpPrf, 'ACLineSegment.b0ch', 0.0);
- EndInstance(FunPrf, 'ACLineSegment');
+ DoubleNode(EpPrf, 'SeriesCompensator.r', pReac.SimpleR);
+ DoubleNode(EpPrf, 'SeriesCompensator.x', pReac.SimpleX);
+ DoubleNode(EpPrf, 'SeriesCompensator.r0', pReac.SimpleR);
+ DoubleNode(EpPrf, 'SeriesCompensator.x0', pReac.SimpleX);
+ EndInstance(FunPrf, 'SeriesCompensator');
// AttachLinePhases (F_, pReac); // for the 8500-node circuit, we only need 3 phase series reactors
WriteTerminals(pReac, geoUUID, crsUUID, pReac.NormAmps, pReac.EmergAmps);
end;
@@ -2831,8 +4107,8 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
if breakingAmps > 0.0 then
DoubleNode(EpPrf, 'ProtectedSwitch.breakingCapacity', breakingAmps); // Fuse and Sectionaliser don't have this, others do
DoubleNode(EpPrf, 'Switch.ratedCurrent', ratedAmps);
- // some OpenDSS models have enabled=false to signal open switches, but we can't actually
- // export them because disabled elements don't have terminal references in memory
+ // some OpenDSS models have enabled=false to signal open switches, but we can't actually
+ // export them because disabled elements don't have terminal references in memory
if Enabled then
begin
BooleanNode(FunPrf, 'Switch.normalOpen', not pLine.Closed[0]);
@@ -2856,19 +4132,19 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
if LineCodeSpecified then
begin
DoubleNode(FunPrf, 'Conductor.length', Len * v1);
- LineCodeRefNode(EpPrf, clsLnCd, pLine.CondCode);
+ LineCodeRefNode(EpPrf, clsLnCd, pLine.LineCodeObj);
end
else
if GeometrySpecified then
begin
DoubleNode(FunPrf, 'Conductor.length', Len * v1);
- LineSpacingRefNode(CatPrf, clsGeom, pLine.GeometryCode);
+ LineSpacingRefNode(CatPrf, pLine.LineGeometryObj);
end
else
if SpacingSpecified then
begin
DoubleNode(FunPrf, 'Conductor.length', Len * v1);
- LineSpacingRefNode(CatPrf, clsSpac, pLine.SpacingCode);
+ LineSpacingRefNode(CatPrf, pLine.LineSpacingObj);
end
else
begin
@@ -3024,10 +4300,10 @@ procedure TCIMExporter.ExportCDPSM(FileNm: String;
begin
if pLine.Enabled then
begin
- if pLine.CondCode = pLnCd.LocalName then
+ if (pLine.LineCodeObj <> NIL) and (pLine.LineCodeObj.Name = pLnCd.LocalName) then
begin
pLnCd.Units := pLine.LengthUnits;
-// writeln ('Setting Units on ' + pLnCd.LocalName + ' to ' + LineUnitsStr(pLnCd.Units));
+ // writeln ('Setting Units on ' + pLnCd.LocalName + ' to ' + LineUnitsStr(pLnCd.Units));
break;
end;
end;
@@ -3230,7 +4506,8 @@ procedure TCIMEXporterHelper.FD_Create(Combined: Boolean; FileName: String);
StartCIMFile(F_TOPO, FileName + '_TOPO.XML', TopoPrf);
StartCIMFile(F_SSH, FileName + '_SSH.XML', SshPrf);
StartCIMFile(F_CAT, FileName + '_CAT.XML', CatPrf);
- StartCIMFile(F_EP, FileName + '_EP.XML', EpPrf)
+ StartCIMFile(F_EP, FileName + '_EP.XML', EpPrf);
+ StartCIMFile(F_DYN, FileName + '_DYN.XML', EpPrf)
end
else
begin
@@ -3249,14 +4526,31 @@ procedure TCIMEXporterHelper.FD_Destroy;
FSWriteLn(F_SSH, '');
FSWriteLn(F_TOPO, '');
FSWriteLn(F_EP, '');
+ FSWriteLn(F_DYN, '');
FreeAndNil(F_GEO);
FreeAndNil(F_CAT);
FreeAndNil(F_SSH);
FreeAndNil(F_TOPO);
FreeAndNil(F_EP);
+ FreeAndNil(F_DYN);
end;
// inherited Destroy;
end;
+procedure TCIMEXporterHelper.ConverterControlEnum(prf: ProfileChoice; varMode: Integer; CIMdynamics: Boolean);
+var
+ str: String;
+begin
+ str := 'constantPowerFactor'; // VARMODEPF
+ if CIMDynamics then
+ str := 'dynamic'
+ else if varMode = VARMODEKVAR then
+ str := 'constantReactivePower';
+
+ FD.WriteCimLn (prf, Format(
+ ' ',
+ [CIM_NS, str]
+ ));
+end;
end.
diff --git a/src/Common/ExportResults.pas b/src/Common/ExportResults.pas
index 345694a1d..04bdcb945 100644
--- a/src/Common/ExportResults.pas
+++ b/src/Common/ExportResults.pas
@@ -6,10 +6,6 @@
All rights reserved.
----------------------------------------------------------
}
-{
- 2-25-00 Created
- 5-30-00 Added code for handling positive sequence mode
-}
interface
@@ -68,17 +64,19 @@ procedure ExportIncMatrixRows(DSS: TDSSContext; FileNm: String);
procedure ExportIncMatrixCols(DSS: TDSSContext; FileNm: String);
procedure ExportBusLevels(DSS: TDSSContext; FileNm: String);
procedure ExportLaplacian(DSS: TDSSContext; FileNm: String);
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
procedure ExportZLL(DSS: TDSSContext; FileNm: String);
procedure ExportZCC(DSS: TDSSContext; FileNm: String);
procedure ExportY4(DSS: TDSSContext; FileNm: String);
procedure ExportC(DSS: TDSSContext; FileNm: String);
-
+{$ENDIF}
implementation
uses
+ BufStream,
Classes,
- uComplex,
+ UComplex, DSSUcomplex,
Arraydef,
Sysutils,
Circuit,
@@ -116,7 +114,7 @@ implementation
LineSpacing,
CNData,
TSData,
- BufStream;
+ DSSObject;
procedure WriteElementVoltagesExportFile(DSS: TDSSContext; F: TFileStream; pElem: TDSSCktElement; MaxNumNodes: Integer);
@@ -131,7 +129,7 @@ procedure WriteElementVoltagesExportFile(DSS: TDSSContext; F: TFileStream; pElem
Nterm := pElem.Nterms;
k := 0;
BusName := (StripExtension(pElem.FirstBus));
- FSWrite(F, Format('%s.%s', [pElem.DSSClassName, pElem.Name]));
+ FSWrite(F, pElem.FullName);
FSWrite(F, Format(',%d', [NTerm]));
@@ -141,7 +139,7 @@ procedure WriteElementVoltagesExportFile(DSS: TDSSContext; F: TFileStream; pElem
FSWrite(F, Format(',%d,', [j]));
FSWrite(F, Format('%d,%d,', [NCond, pElem.NPhases]));
- FSWrite(F, Format('%s,', [UpperCase(BusName)]));
+ FSWrite(F, Format('%s,', [AnsiUpperCase(BusName)]));
for i := 1 to NCond do
begin
Inc(k);
@@ -195,16 +193,14 @@ procedure ExportSeqVoltages(DSS: TDSSContext; FileNm: String);
V_NEMA: Double;
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Bus, V1, p.u.,Base kV, V2, %V2/V1, V0, %V0/V1, Vresidual, %NEMA');
with DSS.ActiveCircuit do
begin
for i := 1 to NumBuses do
begin
-
if Buses^[i].NumNodesThisBus < 3 then
begin
V0 := 0.0;
@@ -221,17 +217,16 @@ procedure ExportSeqVoltages(DSS: TDSSContext; FileNm: String);
end
else
begin
-
with DSS.ActiveCircuit.Solution, Buses^[i] do
for j := 1 to 3 do
begin // first nodes named 1, 2, 3
Vph[j] := NodeV^[GetRef(FindIdx(j))];
end;
- {Compute LL voltages for Nema unbalance calc}
- VphLL[1] := Csub(Vph[1], Vph[2]);
- VphLL[2] := Csub(Vph[2], Vph[3]);
- VphLL[3] := Csub(Vph[3], Vph[1]);
+ // Compute LL voltages for Nema unbalance calc
+ VphLL[1] := Vph[1] - Vph[2];
+ VphLL[2] := Vph[2] - Vph[3];
+ VphLL[3] := Vph[3] - Vph[1];
Phase2SymComp(@Vph, @V012);
@@ -261,11 +256,11 @@ procedure ExportSeqVoltages(DSS: TDSSContext; FileNm: String);
Vresidual := CZERO;
with DSS.ActiveCircuit.Solution do
for j := 1 to Buses^[i].NumNodesThisBus do
- Caccum(Vresidual, NodeV^[Buses^[i].GetRef(j)]);
+ Vresidual += NodeV^[Buses^[i].GetRef(j)];
FSWriteln(F,
Format('"%s", %10.6g, %9.5g, %8.2f, %10.6g, %8.4g, %10.6g, %8.4g, %10.6g, %8.4g',
- [Uppercase(BusList.NameOfIndex(i)), V1, Vpu, (Buses^[i].kvbase * SQRT3), V2, V2V1, V0, V0V1, Cabs(Vresidual), V_NEMA]
+ [AnsiUpperCase(BusList.NameOfIndex(i)), V1, Vpu, (Buses^[i].kvbase * SQRT3), V2, V2V1, V0, V0V1, Cabs(Vresidual), V_NEMA]
));
@@ -279,7 +274,6 @@ procedure ExportSeqVoltages(DSS: TDSSContext; FileNm: String);
FreeAndNil(F);
end;
-
end;
//-------------------------------------------------------------------
@@ -300,7 +294,6 @@ procedure ExportVoltages(DSS: TDSSContext; FileNm: String);
begin
-
{Find max nodes at a bus}
MaxNumNodes := 0;
with DSS.ActiveCircuit do
@@ -308,7 +301,7 @@ procedure ExportVoltages(DSS: TDSSContext; FileNm: String);
MaxNumNodes := max(MaxNumNodes, Buses^[i].NumNodesThisBus);
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWrite(F, 'Bus, BasekV');
for i := 1 to MaxNumNodes do
@@ -320,7 +313,7 @@ procedure ExportVoltages(DSS: TDSSContext; FileNm: String);
for i := 1 to NumBuses do
begin
BusName := BusList.NameOfIndex(i);
- FSWrite(F, Format('"%s", %.5g', [UpperCase(BusName), Buses^[i].kvbase * SQRT3]));
+ FSWrite(F, Format('"%s", %.5g', [AnsiUpperCase(BusName), Buses^[i].kvbase * SQRT3]));
jj := 1;
with Buses^[i] do
@@ -356,7 +349,6 @@ procedure ExportVoltages(DSS: TDSSContext; FileNm: String);
FreeAndNil(F);
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -373,7 +365,6 @@ procedure CalcAndWriteSeqCurrents(DSS: TDSSContext; F: TFileStream; j: Integer;
NCond := pelem.NConds;
if (pelem.Nphases >= 3) then
begin
-
for i := 1 to 3 do
begin
k := (j - 1) * Ncond + i;
@@ -433,16 +424,14 @@ procedure CalcAndWriteSeqCurrents(DSS: TDSSContext; F: TFileStream; j: Integer;
Iresidual := CZERO;
for i := 1 to Ncond do
- Caccum(Iresidual, cBuffer^[i]);
+ Iresidual += cBuffer^[i];
FSWriteln(F, Format('"%s", %3d, %10.6g, %8.4g, %8.4g, %10.6g, %8.4g, %10.6g, %8.4g, %10.6g, %8.4g',
- [(pelem.DSSClassName + '.' + UpperCase(pelem.Name)), j, I1, iNormal, iEmerg, I2, I2I1, I0, I0I1, Cabs(Iresidual), I_NEMA]));
+ [(pelem.DSSClassName + '.' + AnsiUpperCase(pelem.Name)), j, I1, iNormal, iEmerg, I2, I2I1, I0, I0I1, Cabs(Iresidual), I_NEMA]));
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure ExportSeqCurrents(DSS: TDSSContext; FileNm: String);
-
var
F: TFileStream = nil;
j: Integer;
@@ -450,18 +439,15 @@ procedure ExportSeqCurrents(DSS: TDSSContext; FileNm: String);
PDElem: TPDElement;
PCelem: TPCelement;
cBuffer: pComplexArray; // Allocate to max total conductors
-
begin
-
cBuffer := NIL;
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
- {Sequence Currents}
+ // Sequence Currents
FSWriteln(F, 'Element, Terminal, I1, %Normal, %Emergency, I2, %I2/I1, I0, %I0/I1, Iresidual, %NEMA');
- {Allocate cBuffer big enough for largest circuit element}
+ // Allocate cBuffer big enough for largest circuit element
Getmem(cbuffer, SizeOf(Complex) * GetMaxCktElementSize(DSS));
@@ -532,16 +518,13 @@ procedure ExportSeqCurrents(DSS: TDSSContext; FileNm: String);
end;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
-
procedure CalcAndWriteCurrents(F: TFileStream; pElem: TDSSCktElement; Cbuffer: pComplexArray; CondWidth, TermWidth: Integer);
var
i, j, k: Integer;
Iresid: Complex;
-
begin
k := 0;
- FSWrite(F, Format('%s', [pelem.DSSClassName + '.' + UpperCase(pElem.Name)]));
+ FSWrite(F, Format('%s', [pelem.DSSClassName + '.' + AnsiUpperCase(pElem.Name)]));
for j := 1 to pElem.Nterms do
begin
Iresid := CZERO;
@@ -550,7 +533,7 @@ procedure CalcAndWriteCurrents(F: TFileStream; pElem: TDSSCktElement; Cbuffer: p
Inc(k);
FSWrite(F,
Format(', %10.6g, %8.2f', [Cabs(cBuffer^[k]), cdang(cBuffer^[k])]));
- Caccum(Iresid, cBuffer^[k]);
+ Iresid += cBuffer^[k];
end;
for i := pElem.Nconds + 1 to CondWidth do
FSWrite(F, Format(', %10.6g, %8.2f', [0.0, 0.0]));
@@ -606,7 +589,7 @@ procedure CalcAndWriteMaxCurrents(DSS: TDSSContext; F: TFileStream; pElem: TPDEl
DSS.SeasonalRating := FALSE; // The user didn't define the seasonal signal
end;
- FSWrite(F, Format('%s.%s', [pelem.DSSClassName, UpperCase(pElem.Name)]));
+ FSWrite(F, Format('%s.%s', [pelem.DSSClassName, AnsiUpperCase(pElem.Name)]));
MaxCurrent := 0.0;
for i := 1 to pElem.Nphases do
begin
@@ -615,7 +598,7 @@ procedure CalcAndWriteMaxCurrents(DSS: TDSSContext; F: TFileStream; pElem: TPDEl
MaxCurrent := Currmag;
end;
//----pElem.ActiveTerminalIdx := 1;
- LocalPower := CmulReal(pElem.Power[1], 0.001);
+ LocalPower := pElem.Power[1] * 0.001;
if (pElem.NormAmps = 0.0) or (pElem.EmergAmps = 0.0) then
FSWrite(F, Format(', %10.6g, %8.2f, %8.2f', [MaxCurrent, 0.0, 0.0]))
else
@@ -639,11 +622,10 @@ procedure ExportCurrents(DSS: TDSSContext; FileNm: String);
i, j: Integer;
begin
-
cBuffer := NIL;
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
Getmem(cBuffer, sizeof(cBuffer^[1]) * GetMaxCktElementSize(DSS));
@@ -730,7 +712,6 @@ procedure ExportCurrents(DSS: TDSSContext; FileNm: String);
FreeAndNil(F);
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -742,11 +723,10 @@ procedure WriteNodeList(DSS: TDSSContext; F: TFileStream; const CktElementName:
begin
-
if DSS.ActiveCircuit <> NIL then
if not DSS.ActiveCircuit.IsSolved then
begin
- DoSimpleMsg(DSS, 'Circuit must be solved for this command to execute properly.', 222001);
+ DoSimpleMsg(DSS, _('Circuit must be solved for this command to execute properly.'), 222001);
Exit;
end;
@@ -768,105 +748,65 @@ procedure WriteNodeList(DSS: TDSSContext; F: TFileStream; const CktElementName:
FSWriteln(F);
end
end;
-
-
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure ExportNodeOrder(DSS: TDSSContext; FileNm: String);
-
-{ Writes NodeLists in same order as Export Currents function
-}
-
+// Writes NodeLists in same order as Export Currents function
var
F: TFileStream = nil;
pElem: TDSSCktElement;
- strName: String;
-
begin
-
-
try
- F := TFileStream.Create(FileNm, fmCreate);
-
- {Header Record}
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
+ // Header Record
FSWrite(F, 'Element, Nterminals, Nconductors, Node-1, Node-2, Node-3, ...');
FSWriteln(F);
-
-
- // Sources first
+ // Sources first
pElem := DSS.ActiveCircuit.Sources.First;
while pElem <> NIL do
begin
if pElem.Enabled then
- begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
- WriteNodeList(DSS, F, strName);
- end;
+ WriteNodeList(DSS, F, pElem.FullName);
pElem := DSS.ActiveCircuit.Sources.Next;
end;
-
-
- // PDELEMENTS first
+ // PDELEMENTS first
pElem := DSS.ActiveCircuit.PDElements.First;
while pElem <> NIL do
begin
if pElem.Enabled then
- begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
- WriteNodeList(DSS, F, strName);
- end;
+ WriteNodeList(DSS, F, pElem.FullName);
pElem := DSS.ActiveCircuit.PDElements.Next;
end;
-
- // Faults
+ // Faults
pElem := DSS.ActiveCircuit.Faults.First;
while pElem <> NIL do
begin
if pElem.Enabled then
- begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
- WriteNodeList(DSS, F, strName);
- end;
+ WriteNodeList(DSS, F, pElem.FullName);
pElem := DSS.ActiveCircuit.Faults.Next;
end;
-
- // PCELEMENTS next
+ // PCELEMENTS next
pElem := DSS.ActiveCircuit.PCElements.First;
while pElem <> NIL do
begin
if pElem.Enabled then
- begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
- WriteNodeList(DSS, F, strName);
- end;
+ WriteNodeList(DSS, F, pElem.FullName);
pElem := DSS.ActiveCircuit.PCElements.Next;
end;
-
DSS.GlobalResult := FileNm;
-
-
finally
FreeAndNil(F);
-
end;
-
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
-
procedure WriteElemCurrents(DSS: TDSSContext; F: TFileStream; const CktElementName: String);
var
NValues, i: Integer;
-
-
begin
-
-
if DSS.ActiveCircuit <> NIL then
if not DSS.ActiveCircuit.Issolved then
begin
- DoSimpleMsg(DSS, 'Circuit must be solved for this command to execute properly.', 222001);
+ DoSimpleMsg(DSS, _('Circuit must be solved for this command to execute properly.'), 222001);
Exit;
end;
@@ -888,108 +828,70 @@ procedure WriteElemCurrents(DSS: TDSSContext; F: TFileStream; const CktElementNa
FSWriteln(F);
end
end;
-
-
end;
-
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure ExportElemCurrents(DSS: TDSSContext; FileNm: String);
-
-{ Export currents in same order as NodeOrder export
-}
+// Export currents in same order as NodeOrder export
var
F: TFileStream = nil;
pElem: TDSSCktElement;
- strName: String;
-
begin
-
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
- {Header Record}
+ // Header Record
FSWrite(F, 'Element, Nterminals, Nconductors, I_1, Ang_1, ...');
FSWriteln(F);
-
-
- // Sources first
+ // Sources first
pElem := DSS.ActiveCircuit.Sources.First;
while pElem <> NIL do
begin
if pElem.Enabled then
- begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
- WriteElemCurrents(DSS, F, strName);
- end;
+ WriteElemCurrents(DSS, F, pElem.FullName);
pElem := DSS.ActiveCircuit.Sources.Next;
end;
-
-
- // PDELEMENTS first
+ // PDELEMENTS first
pElem := DSS.ActiveCircuit.PDElements.First;
while pElem <> NIL do
begin
if pElem.Enabled then
- begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
- WriteElemCurrents(DSS, F, strName);
- end;
+ WriteElemCurrents(DSS, F, pElem.FullName);
pElem := DSS.ActiveCircuit.PDElements.Next;
end;
-
- // Faults
+ // Faults
pElem := DSS.ActiveCircuit.Faults.First;
while pElem <> NIL do
begin
if pElem.Enabled then
- begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
- WriteElemCurrents(DSS, F, strName);
- end;
+ WriteElemCurrents(DSS, F, pElem.FullName);
pElem := DSS.ActiveCircuit.Faults.Next;
end;
-
- // PCELEMENTS next
+ // PCELEMENTS next
pElem := DSS.ActiveCircuit.PCElements.First;
while pElem <> NIL do
begin
if pElem.Enabled then
- begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
- WriteElemCurrents(DSS, F, strName);
- end;
+ WriteElemCurrents(DSS, F, pElem.FullName);
pElem := DSS.ActiveCircuit.PCElements.Next;
end;
DSS.GlobalResult := FileNm;
-
-
finally
FreeAndNil(F);
-
end;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
-
procedure WriteElemVoltages(DSS: TDSSContext; F: TFileStream; const CktElementName: String);
var
NValues, i: Integer;
-
-
begin
-
-
if DSS.ActiveCircuit <> NIL then
if not DSS.ActiveCircuit.Issolved then
begin
- DoSimpleMsg(DSS, 'Circuit must be solved for this command to execute properly.', 222001);
+ DoSimpleMsg(DSS, _('Circuit must be solved for this command to execute properly.'), 222001);
Exit;
end;
-
if Length(CktElementName) > 0 then
begin
SetObject(DSS, CktElementName);
@@ -1007,108 +909,71 @@ procedure WriteElemVoltages(DSS: TDSSContext; F: TFileStream; const CktElementNa
FSWriteln(F);
end
end;
-
-
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure ExportElemVoltages(DSS: TDSSContext; FileNm: String);
-{ Export conductor voltages in same order as NodeOrder export
-}
+// Export conductor voltages in same order as NodeOrder export
var
F: TFileStream = nil;
pElem: TDSSCktElement;
- strName: String;
-
begin
-
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
- {Header Record}
+ // Header Record
FSWrite(F, 'Element, Nterminals, Nconductors, V_1, Ang_1, ...');
FSWriteln(F);
-
-
- // Sources first
+ // Sources first
pElem := DSS.ActiveCircuit.Sources.First;
while pElem <> NIL do
begin
if pElem.Enabled then
- begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
- WriteElemVoltages(DSS, F, strName);
- end;
+ WriteElemVoltages(DSS, F, pElem.FullName);
pElem := DSS.ActiveCircuit.Sources.Next;
end;
-
-
- // PDELEMENTS first
+ // PDELEMENTS first
pElem := DSS.ActiveCircuit.PDElements.First;
while pElem <> NIL do
begin
if pElem.Enabled then
- begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
- WriteElemVoltages(DSS, F, strName);
- end;
+ WriteElemVoltages(DSS, F, pElem.FullName);
pElem := DSS.ActiveCircuit.PDElements.Next;
end;
-
- // Faults
+ // Faults
pElem := DSS.ActiveCircuit.Faults.First;
while pElem <> NIL do
begin
if pElem.Enabled then
- begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
- WriteElemVoltages(DSS, F, strName);
- end;
+ WriteElemVoltages(DSS, F, pElem.FullName);
pElem := DSS.ActiveCircuit.Faults.Next;
end;
-
- // PCELEMENTS next
+ // PCELEMENTS next
pElem := DSS.ActiveCircuit.PCElements.First;
while pElem <> NIL do
begin
if pElem.Enabled then
- begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
- WriteElemVoltages(DSS, F, strName);
- end;
+ WriteElemVoltages(DSS, F, pElem.FullName);
pElem := DSS.ActiveCircuit.PCElements.Next;
end;
DSS.GlobalResult := FileNm;
-
-
finally
FreeAndNil(F);
-
end;
-
end;
-
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
-
procedure WriteElemPowers(DSS: TDSSContext; F: TFileStream; const CktElementName: String);
var
NValues, i: Integer;
S: Complex;
-
begin
-
-
if DSS.ActiveCircuit <> NIL then
if not DSS.ActiveCircuit.Issolved then
begin
- DoSimpleMsg(DSS, 'Circuit must be solved for this command to execute properly.', 222001);
+ DoSimpleMsg(DSS, _('Circuit must be solved for this command to execute properly.'), 222001);
Exit;
end;
-
if Length(CktElementName) > 0 then
begin
SetObject(DSS, CktElementName);
@@ -1122,14 +987,12 @@ procedure WriteElemPowers(DSS: TDSSContext; F: TFileStream; const CktElementName
NValues := NConds * Nterms;
for i := 1 to NValues do
begin
- S := Cmul(Vterminal^[i], Conjg(Iterminal^[i]));
+ S := Vterminal^[i] * cong(Iterminal^[i]);
FSWrite(F, Format(', %10.6g, %10.6g', [S.re * 0.001, S.im * 0.001]));
end;
FSWriteln(F);
end
end;
-
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -1144,9 +1007,8 @@ procedure ExportElemPowers(DSS: TDSSContext; FileNm: String);
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
{Header Record}
FSWrite(F, 'Element, Nterminals, Nconductors, P_1, Q_1, ...');
@@ -1159,7 +1021,7 @@ procedure ExportElemPowers(DSS: TDSSContext; FileNm: String);
begin
if pElem.Enabled then
begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
+ strName := pElem.FullName;
WriteElemPowers(DSS, F, strName);
end;
pElem := DSS.ActiveCircuit.Sources.Next;
@@ -1172,7 +1034,7 @@ procedure ExportElemPowers(DSS: TDSSContext; FileNm: String);
begin
if pElem.Enabled then
begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
+ strName := pElem.FullName;
WriteElemPowers(DSS, F, strName);
end;
pElem := DSS.ActiveCircuit.PDElements.Next;
@@ -1184,7 +1046,7 @@ procedure ExportElemPowers(DSS: TDSSContext; FileNm: String);
begin
if pElem.Enabled then
begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
+ strName := pElem.FullName;
WriteElemPowers(DSS, F, strName);
end;
pElem := DSS.ActiveCircuit.Faults.Next;
@@ -1196,7 +1058,7 @@ procedure ExportElemPowers(DSS: TDSSContext; FileNm: String);
begin
if pElem.Enabled then
begin
- strName := pElem.ParentClass.Name + '.' + pElem.Name;
+ strName := pElem.FullName;
WriteElemPowers(DSS, F, strName);
end;
pElem := DSS.ActiveCircuit.PCElements.Next;
@@ -1209,7 +1071,6 @@ procedure ExportElemPowers(DSS: TDSSContext; FileNm: String);
FreeAndNil(F);
end;
-
end;
@@ -1230,9 +1091,8 @@ procedure ExportPowers(DSS: TDSSContext; FileNm: String; opt: Integer);
sout: String;
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
Separator := ', ';
@@ -1254,12 +1114,12 @@ procedure ExportPowers(DSS: TDSSContext; FileNm: String; opt: Integer);
for j := 1 to NTerm do
begin
- WriteStr(sout, Pad('"' + PDelem.DSSClassName + '.' + UpperCase(PDElem.Name) + '"', 24), Separator, j: 3);
+ WriteStr(sout, Pad('"' + PDelem.DSSClassName + '.' + AnsiUpperCase(PDElem.Name) + '"', 24), Separator, j: 3);
FSWrite(F, sout);
//----PDElem.ActiveTerminalIdx := j;
S := PDElem.Power[j];
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
WriteStr(sout, Separator, S.re * 0.001: 11: 1);
FSWrite(F, sout);
WriteStr(sout, Separator, S.im * 0.001: 11: 1);
@@ -1269,14 +1129,14 @@ procedure ExportPowers(DSS: TDSSContext; FileNm: String; opt: Integer);
//----PDelem.ActiveTerminalIdx := 1;
S := PDElem.ExcesskVANorm[1];
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
WriteStr(sout, Separator, Abs(S.re): 11: 1);
FSWrite(F, sout);
WriteStr(sout, Separator, Abs(S.im): 11: 1);
FSWrite(F, sout);
S := PDElem.ExcesskVAEmerg[1];
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
WriteStr(sout, Separator, Abs(S.re): 11: 1);
FSWrite(F, sout);
WriteStr(sout, Separator, Abs(S.im): 11: 1);
@@ -1293,18 +1153,17 @@ procedure ExportPowers(DSS: TDSSContext; FileNm: String; opt: Integer);
while PCElem <> NIL do
begin
-
if (PCElem.Enabled) then
begin
Nterm := PCElem.Nterms;
for j := 1 to NTerm do
begin
- FSWrite(F, Pad('"' + PCElem.DSSClassName + '.' + UpperCase(PCElem.Name) + '"', 24), Separator, Format('%3d', [j]));
+ FSWrite(F, Pad('"' + PCElem.DSSClassName + '.' + AnsiUpperCase(PCElem.Name) + '"', 24), Separator, Format('%3d', [j]));
//----pcElem.ActiveTerminalIdx := j;
S := pCElem.Power[j];
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
WriteStr(sout, Separator, S.re * 0.001: 11: 1);
FSWrite(F, sout);
@@ -1339,9 +1198,8 @@ procedure ExportLosses(DSS: TDSSContext; FileNm: String);
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Element, Total(W), Total(var), I2R(W), I2X(var), No-load(W), No-load(var)');
// PDELEMENTS first
@@ -1352,7 +1210,7 @@ procedure ExportLosses(DSS: TDSSContext; FileNm: String);
if (PDElem.Enabled) then
begin
PDElem.GetLosses(S_total, S_Load, S_NoLoad);
- FSWriteln(F, Format('%s.%s, %.7g, %.7g, %.7g, %.7g, %.7g, %.7g', [PDElem.ParentClass.Name, UpperCase(PDElem.Name), S_total.re, S_total.im, S_Load.re, S_Load.im, S_NoLoad.re, S_NoLoad.im]));
+ FSWriteln(F, Format('%s.%s, %.7g, %.7g, %.7g, %.7g, %.7g, %.7g', [PDElem.ParentClass.Name, AnsiUpperCase(PDElem.Name), S_total.re, S_total.im, S_Load.re, S_Load.im, S_NoLoad.re, S_NoLoad.im]));
end;
PDElem := DSS.ActiveCircuit.PDElements.Next;
end;
@@ -1383,9 +1241,8 @@ procedure ExportPbyphase(DSS: TDSSContext; FileNm: String; opt: Integer);
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
case Opt of
1:
@@ -1405,12 +1262,12 @@ procedure ExportPbyphase(DSS: TDSSContext; FileNm: String; opt: Integer);
begin
ComputeITerminal;
ComputeVTerminal;
- FSWrite(F, Format('"%s.%s", %d, %d, %d', [DSSClassName, Uppercase(Name), NTerms, NConds, Nphases]));
+ FSWrite(F, Format('"%s.%s", %d, %d, %d', [DSSClassName, AnsiUpperCase(Name), NTerms, NConds, Nphases]));
for i := 1 to Yorder do
begin
- S := CmulReal(Cmul(Vterminal^[i], conjg(ITerminal^[i])), 0.001);
+ S := Vterminal^[i] * cong(ITerminal^[i]) * 0.001;
if Opt = 1 then
- S := CmulReal(S, 0.001); // convert to MVA
+ S := S * 0.001; // convert to MVA
FSWrite(F, Format(', %10.3f, %10.3f', [S.re, S.im]));
end;
end;
@@ -1424,19 +1281,18 @@ procedure ExportPbyphase(DSS: TDSSContext; FileNm: String; opt: Integer);
while PCElem <> NIL do
begin
-
if (PCElem.Enabled) then
begin
with PCelem do
begin
ComputeITerminal;
ComputeVTerminal;
- FSWrite(F, Format('"%s.%s", %d, %d, %d', [DSSClassName, Uppercase(Name), NTerms, NConds, NPhases]));
+ FSWrite(F, Format('"%s.%s", %d, %d, %d', [DSSClassName, AnsiUpperCase(Name), NTerms, NConds, NPhases]));
for i := 1 to Yorder do
begin
- S := CmulReal(Cmul(Vterminal^[i], conjg(ITerminal^[i])), 0.001);
+ S := Vterminal^[i] * cong(ITerminal^[i]) * 0.001;
if Opt = 1 then
- S := CmulReal(S, 0.001); // convert to MVA
+ S := S * 0.001; // convert to MVA
FSWrite(F, Format(', %10.3f, %10.3f', [S.re, S.im]));
end;
end;
@@ -1476,11 +1332,10 @@ procedure ExportSeqPowers(DSS: TDSSContext; FileNm: String; opt: Integer);
sout: String;
begin
-
cBuffer := NIL;
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
Separator := ', ';
Getmem(cBuffer, sizeof(cBuffer^[1]) * GetMaxCktElementSize(DSS));
@@ -1505,7 +1360,7 @@ procedure ExportSeqPowers(DSS: TDSSContext; FileNm: String; opt: Integer);
for j := 1 to NTerm do
begin
- FSWrite(F, Pad('"' + PDelem.DSSClassName + '.' + Uppercase(PDElem.Name) + '"', 24), Separator, Format('%3d', [j]));
+ FSWrite(F, Pad('"' + PDelem.DSSClassName + '.' + AnsiUpperCase(PDElem.Name) + '"', 24), Separator, Format('%3d', [j]));
for i := 1 to PDElem.NPhases do
begin
k := (j - 1) * Ncond + i;
@@ -1537,24 +1392,24 @@ procedure ExportSeqPowers(DSS: TDSSContext; FileNm: String; opt: Integer);
end;
end;
- S := Cmul(V012[2], conjg(I012[2]));
+ S := V012[2] * cong(I012[2]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
WriteStr(sout, Separator, S.re * 0.003: 11: 1);
FSWrite(F, sout);
WriteStr(sout, Separator, S.im * 0.003: 11: 1);
FSWrite(F, sout);
- S := Cmul(V012[3], conjg(I012[3]));
+ S := V012[3] * cong(I012[3]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
WriteStr(sout, Separator, S.re * 0.003: 11: 1);
FSWrite(F, sout);
WriteStr(sout, Separator, S.im * 0.003: 11: 1);
FSWrite(F, sout);
- S := Cmul(V012[1], conjg(I012[1]));
+ S := V012[1] * cong(I012[1]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
WriteStr(sout, Separator, S.re * 0.003: 8: 1);
FSWrite(F, sout);
WriteStr(sout, Separator, S.im * 0.003: 8: 1);
@@ -1565,14 +1420,14 @@ procedure ExportSeqPowers(DSS: TDSSContext; FileNm: String; opt: Integer);
//----PDelem.ActiveTerminalIdx := 1;
S := PDElem.ExcesskVANorm[1];
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
WriteStr(sout, Separator, Abs(S.re): 11: 1);
FSWrite(F, sout);
WriteStr(sout, Separator, Abs(S.im): 11: 1);
FSWrite(F, sout);
S := PDElem.ExcesskVAEmerg[1];
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
WriteStr(sout, Separator, Abs(S.re): 11: 1);
FSWrite(F, sout);
WriteStr(sout, Separator, Abs(S.im): 11: 1);
@@ -1590,7 +1445,6 @@ procedure ExportSeqPowers(DSS: TDSSContext; FileNm: String; opt: Integer);
while PCElem <> NIL do
begin
-
if (PCElem.Enabled) then
begin
NCond := PCElem.NConds;
@@ -1599,7 +1453,7 @@ procedure ExportSeqPowers(DSS: TDSSContext; FileNm: String; opt: Integer);
for j := 1 to NTerm do
begin
- FSWrite(F, Pad('"' + PCElem.DSSClassName + '.' + Uppercase(PCElem.Name) + '"', 24), Separator, Format('%3d', [j]));
+ FSWrite(F, Pad('"' + PCElem.DSSClassName + '.' + AnsiUpperCase(PCElem.Name) + '"', 24), Separator, Format('%3d', [j]));
for i := 1 to PCElem.NPhases do
begin
k := (j - 1) * Ncond + i;
@@ -1631,23 +1485,23 @@ procedure ExportSeqPowers(DSS: TDSSContext; FileNm: String; opt: Integer);
end;
end;
- S := Cmul(V012[2], conjg(I012[2]));
+ S := V012[2] * cong(I012[2]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
WriteStr(sout, Separator, S.re * 0.003: 11: 1);
FSWrite(F, sout);
WriteStr(sout, Separator, S.im * 0.003: 11: 1);
FSWrite(F, sout);
- S := Cmul(V012[3], conjg(I012[3]));
+ S := V012[3] * cong(I012[3]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
WriteStr(sout, Separator, S.re * 0.003: 11: 1);
FSWrite(F, sout);
WriteStr(sout, Separator, S.im * 0.003: 11: 1);
FSWrite(F, sout);
- S := Cmul(V012[1], conjg(I012[1]));
+ S := V012[1] * cong(I012[1]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
WriteStr(sout, Separator, S.re * 0.003: 8: 1);
FSWrite(F, sout);
WriteStr(sout, Separator, S.im * 0.003: 8: 1);
@@ -1685,26 +1539,23 @@ procedure ExportFaultStudy(DSS: TDSSContext; FileNm: String);
CurrMag: Double;
begin
-
try
-
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
Separator := ', ';
- { Set source voltage injection currents }
+ // Set source voltage injection currents
with DSS.ActiveCircuit do
begin
with Solution do
begin
-
- {All Phase Faults}
+ // All Phase Faults
FSWriteln(F, 'Bus, 3-Phase, 1-Phase, L-L');
for iBus := 1 to NumBuses do
- {Bus Norton Equivalent Current, Isc has been previously computed}
+ // Bus Norton Equivalent Current, Isc has been previously computed
with Buses^[iBus] do
begin
- FSWrite(F, Pad(Uppercase(BusList.NameOfIndex(iBus)), 12));
+ FSWrite(F, Pad(AnsiUpperCase(BusList.NameOfIndex(iBus)), 12));
MaxCurr := 0.0;
for i := 1 to NumNodesThisBus do
begin
@@ -1714,14 +1565,14 @@ procedure ExportFaultStudy(DSS: TDSSContext; FileNm: String);
end;
FSWrite(F, Separator, Format('%10f', [maxCurr]));
- {One Phase Faults}
+ // One Phase Faults
- { Solve for Fault Injection Currents}
+ // Solve for Fault Injection Currents
YFault := TcMatrix.CreateMatrix(NumNodesThisBus);
Getmem(VFault, Sizeof(Complex) * NumNodesThisBus);
- {Build YscTemp}
+ // Build YscTemp
GFault := Cmplx(10000.0, 0.0);
@@ -1732,24 +1583,24 @@ procedure ExportFaultStudy(DSS: TDSSContext; FileNm: String);
YFault.CopyFrom(Ysc);
YFault.AddElement(iphs, iphs, GFault);
- { Solve for Injection Currents}
+ // Solve for Injection Currents
YFault.Invert;
YFault.MvMult(VFault, BusCurrent); {Gets voltage appearing at fault}
- Currmag := Cabs(Cmul(VFault^[iphs], GFault));
+ Currmag := Cabs(VFault^[iphs] * GFault);
if CurrMag > MaxCurr then
MaxCurr := Currmag;
- end; {For iphase}
- {Now, Stuff it in the Css Array where it belongs}
+ end;
+ // Now, Stuff it in the Css Array where it belongs
FSWrite(F, Separator, Format('%10f', [maxCurr]));
Freemem(VFault);
YFault.Free;
- {Node-Node Faults}
+ // Node-Node Faults
- {Bus Norton Equivalent Current, Isc has been previously computed}
+ // Bus Norton Equivalent Current, Isc has been previously computed
YFault := TcMatrix.CreateMatrix(NumNodesThisBus);
Getmem(VFault, Sizeof(VFault^[1]) * NumNodesThisBus);
@@ -1763,17 +1614,18 @@ procedure ExportFaultStudy(DSS: TDSSContext; FileNm: String);
YFault.CopyFrom(Ysc);
YFault.AddElement(iphs, iphs, GFault);
YFault.AddElement(iphs + 1, iphs + 1, GFault);
- YFault.AddElemSym(iphs, iphs + 1, Cnegate(GFault));
+ YFault.AddElemSym(iphs, iphs + 1, -GFault);
- { Solve for Injection Currents}
+ // Solve for Injection Currents
YFault.Invert;
- YFault.MvMult(VFault, BusCurrent); {Gets voltage appearing at fault}
+ YFault.MvMult(VFault, BusCurrent); // Gets voltage appearing at fault
- CurrMag := Cabs(Cmul(Csub(VFault^[iphs], VFault^[iphs + 1]), GFault));
+ CurrMag := Cabs((VFault^[iphs] - VFault^[iphs + 1]) * GFault);
if CurrMag > MaxCurr then
MaxCurr := CurrMag;
- end; {For iphase}
- {Now, Stuff it in the Css Array where it belongs}
+ end; // For iphase
+
+ // Now, Stuff it in the Css Array where it belongs
FSWrite(F, Separator, Format('%10f', [maxCurr]));
@@ -1781,10 +1633,10 @@ procedure ExportFaultStudy(DSS: TDSSContext; FileNm: String);
YFault.Free;
FSWriteln(F);
- end; {With bus}
+ end; // With bus
- end; {With Solution}
- end; {With DSS.ActiveCircuit}
+ end; // With Solution
+ end; // With ActiveCircuit
DSS.GlobalResult := Filenm;
@@ -1795,10 +1647,7 @@ procedure ExportFaultStudy(DSS: TDSSContext; FileNm: String);
end;
end;
-
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure ExportEstimation(DSS: TDSSContext; FileNm: String);
-
var
F: TFileStream = nil;
i: Integer;
@@ -1814,13 +1663,11 @@ procedure ExportEstimation(DSS: TDSSContext; FileNm: String);
TempX[ii] := 0.0;
end;
-
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate); // clears file
+ F := TBufferedFileStream.Create(FileNm, fmCreate); // clears file
- {Do the EnergyMeters first}
+ // Do the EnergyMeters first
FSWriteln(F, '"Energy Meters" ');
FSWriteln(F, '"energyMeter", "I1 Target", "I2 Target", "I3 Target", "I1 Calc", "I2 Calc", "I3 Calc", "I1 %Err", "I2 %Err", "I3 %Err"'{, "I1 Factor", "I2 Factor", "I3 Factor"'});
@@ -1829,38 +1676,36 @@ procedure ExportEstimation(DSS: TDSSContext; FileNm: String);
begin
if pEnergyMeterObj.Enabled then
begin
- FSWrite(F, Format('"Energymeter.%s"', [Uppercase(pEnergyMeterObj.Name)]));
- {Sensor currents (Target)}
+ FSWrite(F, Format('"Energymeter.%s"', [AnsiUpperCase(pEnergyMeterObj.Name)]));
+ // Sensor currents (Target)
ZeroTempXArray;
for i := 1 to pEnergyMeterObj.Nphases do
TempX[i] := pEnergyMeterObj.SensorCurrent^[i];
for i := 1 to 3 do
FSWrite(F, Format(', %.6g', [TempX[i]]));
- {Calculated Currents}
+ // Calculated Currents
ZeroTempXArray;
for i := 1 to pEnergyMeterObj.Nphases do
TempX[i] := Cabs(pEnergyMeterObj.CalculatedCurrent^[i]);
for i := 1 to 3 do
FSWrite(F, Format(', %.6g', [TempX[i]]));
- {Percent Error}
+ // Percent Error
for i := 1 to pEnergyMeterObj.Nphases do
TempX[i] := (1.0 - TempX[i] / Max(0.001, pEnergyMeterObj.SensorCurrent^[i])) * 100.0;
for i := 1 to 3 do
FSWrite(F, Format(', %.6g', [TempX[i]]));
- (**** Not all that useful
- {Allocation Factors}
- ZeroTempXArray;
- For i := 1 to pEnergyMeterObj.Nphases do TempX[i] := pEnergyMeterObj.PhsAllocationFactor^[i];
- For i := 1 to 3 do FSWrite(F, Format(' %.6g,',[TempX[i]]));
- *****)
-
+ // **** Not all that useful
+ // Allocation Factors
+ // ZeroTempXArray;
+ // For i := 1 to pEnergyMeterObj.Nphases do TempX[i] := pEnergyMeterObj.PhsAllocationFactor^[i];
+ // For i := 1 to 3 do FSWrite(F, Format(' %.6g,',[TempX[i]]));
FSWriteln(F);
end;
pEnergyMeterObj := DSS.ActiveCircuit.EnergyMeters.Next;
end;
- {Do the Sensors Next}
+ // Do the Sensors Next
FSWriteln(F);
FSWriteln(F, '"Sensors" ');
FSWrite(F, '"Sensor", "I1 Target", "I2 Target", "I3 Target", "I1 Calc", "I2 Calc", "I3 Calc", "I1 %Err", "I2 %Err", "I3 %Err",');
@@ -1871,42 +1716,42 @@ procedure ExportEstimation(DSS: TDSSContext; FileNm: String);
begin
if pSensorObj.Enabled then
begin
- FSWrite(F, Format('"Sensor.%s"', [Uppercase(pSensorObj.Name)]));
- {Sensor currents (Target)}
+ FSWrite(F, Format('"Sensor.%s"', [AnsiUpperCase(pSensorObj.Name)]));
+ // Sensor currents (Target)
ZeroTempXArray;
for i := 1 to pSensorObj.Nphases do
TempX[i] := pSensorObj.SensorCurrent^[i];
for i := 1 to 3 do
FSWrite(F, Format(', %.6g', [TempX[i]]));
- {Calculated Currents}
+ // Calculated Currents
ZeroTempXArray;
for i := 1 to pSensorObj.Nphases do
TempX[i] := Cabs(pSensorObj.CalculatedCurrent^[i]);
for i := 1 to 3 do
FSWrite(F, Format(', %.6g', [TempX[i]]));
- {Percent Error}
+ // Percent Error
for i := 1 to pSensorObj.Nphases do
TempX[i] := (1.0 - TempX[i] / Max(0.001, pSensorObj.SensorCurrent^[i])) * 100.0;
for i := 1 to 3 do
FSWrite(F, Format(', %.6g', [TempX[i]]));
- {Sensor Voltage (Target)}
+ // Sensor Voltage (Target)
ZeroTempXArray;
for i := 1 to pSensorObj.Nphases do
TempX[i] := pSensorObj.SensorVoltage^[i];
for i := 1 to 3 do
FSWrite(F, Format(', %.6g', [TempX[i]]));
- {Calculated Voltage}
+ // Calculated Voltage
ZeroTempXArray;
for i := 1 to pSensorObj.Nphases do
TempX[i] := Cabs(pSensorObj.CalculatedVoltage^[i]);
for i := 1 to 3 do
FSWrite(F, Format(', %.6g', [TempX[i]]));
- {Percent Error}
+ // Percent Error
for i := 1 to pSensorObj.Nphases do
TempX[i] := (1.0 - TempX[i] / Max(0.001, pSensorObj.SensorVoltage^[i])) * 100.0;
for i := 1 to 3 do
FSWrite(F, Format(', %.6g', [TempX[i]]));
- {WLS Errors}
+ // WLS Errors
ZeroTempXArray;
FSWrite(F, Format(', %.6g, %.6g', [pSensorObj.WLSVoltageError, pSensorObj.WLSCurrentError]));
@@ -1914,21 +1759,14 @@ procedure ExportEstimation(DSS: TDSSContext; FileNm: String);
end;
pSensorObj := DSS.ActiveCircuit.Sensors.Next;
end;
-
-
finally
AppendGlobalResult(DSS, FileNm);
FreeAndNil(F);
-
end;
-
-
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure WriteMultipleMeterFiles(DSS: TDSSContext);
-
var
F: TFileStream = nil;
i, j: Integer;
@@ -1936,9 +1774,7 @@ procedure WriteMultipleMeterFiles(DSS: TDSSContext);
MeterClass: TEnergyMeter;
FileNm,
Separator: String;
-
begin
-
MeterClass := TEnergyMeter(GetDSSClassPtr(DSS, 'Energymeter'));
if MeterClass = NIL then
Exit; // oops somewhere!!
@@ -1950,11 +1786,11 @@ procedure WriteMultipleMeterFiles(DSS: TDSSContext);
if pElem.Enabled then
begin
try
- FileNm := DSS.OutputDirectory + 'EXP_MTR_' + Uppercase(pElem.Name) + '.CSV';
+ FileNm := DSS.OutputDirectory + 'EXP_MTR_' + AnsiUpperCase(pElem.Name) + '.csv';
if not FileExists(FileNm) then
begin
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
{Write New Header}
FSWrite(F, 'Year, LDCurve, Hour, Meter');
for i := 1 to NumEMRegisters do
@@ -1963,12 +1799,12 @@ procedure WriteMultipleMeterFiles(DSS: TDSSContext);
FreeAndNil(F);
end;
- F := TFileStream.Create(FileNm, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(FileNm, fmOpenReadWrite);
F.Seek(0, soEnd);
FSWrite(F, IntToStr(DSS.ActiveCircuit.Solution.Year), Separator);
FSWrite(F, DSS.ActiveCircuit.LoadDurCurve, Separator);
FSWrite(F, IntToStr(DSS.ActiveCircuit.Solution.DynaVars.intHour), Separator);
- FSWrite(F, Pad('"' + Uppercase(pElem.Name) + '"', 14));
+ FSWrite(F, Pad('"' + AnsiUpperCase(pElem.Name) + '"', 14));
for j := 1 to NumEMRegisters do
FSWrite(F, Separator, Format('%10.0f', [PElem.Registers[j]]));
FSWriteln(F);
@@ -1980,8 +1816,6 @@ procedure WriteMultipleMeterFiles(DSS: TDSSContext);
end;
pElem := DSS.ActiveCircuit.EnergyMeters.Next;
end;
-
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -1994,19 +1828,18 @@ procedure WriteSingleMeterFile(DSS: TDSSContext; const FileNm: String);
Separator: String;
RewriteFile: Boolean;
begin
-
Separator := ', ';
try
if FileExists(FileNm) then
begin // See if it has already been written on
- F := TFileStream.Create(FileNm, fmOpenRead);
+ F := TBufferedFileStream.Create(FileNm, fmOpenRead or fmShareDenyWrite);
if ((F.Position+1) < F.Size) then
begin
FSReadLn(F, TestStr);
- {See if it likely that the file is OK}
+ // See if it likely that the file is OK
if CompareText(Copy(TestStr, 1, 4), 'Year') = 0 then
RewriteFile := FALSE // Assume the file is OK
else
@@ -2023,12 +1856,12 @@ procedure WriteSingleMeterFile(DSS: TDSSContext; const FileNm: String);
ReWriteFile := TRUE;
end;
- {Either open or append the file}
+ // Either open or append the file
if RewriteFile then
begin
FreeAndNil(F);
- F := TFileStream.Create(FileNm, fmCreate);
- {Write New Header}
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
+ // Write New Header
pElem := DSS.ActiveCircuit.energyMeters.First;
FSWrite(F, 'Year, LDCurve, Hour, Meter');
for i := 1 to NumEMRegisters do
@@ -2038,7 +1871,7 @@ procedure WriteSingleMeterFile(DSS: TDSSContext; const FileNm: String);
else
begin
FreeAndNil(F);
- F := TFileStream.Create(FileNm, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(FileNm, fmOpenReadWrite);
F.Seek(0, soEnd);
end;
@@ -2051,7 +1884,7 @@ procedure WriteSingleMeterFile(DSS: TDSSContext; const FileNm: String);
FSWrite(F, IntToStr(DSS.ActiveCircuit.Solution.Year), Separator);
FSWrite(F, DSS.ActiveCircuit.LoadDurCurve, Separator);
FSWrite(F, IntToStr(DSS.ActiveCircuit.Solution.DynaVars.intHour), Separator);
- FSWrite(F, Pad('"' + Uppercase(pElem.Name) + '"', 14));
+ FSWrite(F, Pad('"' + AnsiUpperCase(pElem.Name) + '"', 14));
for j := 1 to NumEMRegisters do
FSWrite(F, Separator, Format('%10.0f', [PElem.Registers[j]]));
FSWriteln(F);
@@ -2066,7 +1899,6 @@ procedure WriteSingleMeterFile(DSS: TDSSContext; const FileNm: String);
FreeAndNil(F);
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -2080,8 +1912,7 @@ procedure ExportMeters(DSS: TDSSContext; FileNm: String);
begin
-
- if Lowercase(Copy(FileNm, 1, 2)) = '/m' then
+ if AnsiLowerCase(Copy(FileNm, 1, 2)) = '/m' then
WriteMultipleMeterFiles(DSS)
else
WriteSingleMeterFile(DSS, FileNM);
@@ -2099,7 +1930,6 @@ procedure WriteMultipleGenMeterFiles(DSS: TDSSContext);
Separator: String;
begin
-
GeneratorClass := DSS.GeneratorClass;
if GeneratorClass = NIL then
Exit; // oops somewhere!!
@@ -2111,11 +1941,11 @@ procedure WriteMultipleGenMeterFiles(DSS: TDSSContext);
if pElem.Enabled then
begin
try
- FileNm := DSS.OutputDirectory + 'EXP_GEN_' + Uppercase(pElem.Name) + '.CSV';
+ FileNm := DSS.OutputDirectory + 'EXP_GEN_' + AnsiUpperCase(pElem.Name) + '.csv';
if not FileExists(FileNm) then
begin
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
{Write New Header}
FSWrite(F, 'Year, LDCurve, Hour, Generator');
for i := 1 to NumGenRegisters do
@@ -2124,14 +1954,14 @@ procedure WriteMultipleGenMeterFiles(DSS: TDSSContext);
FreeAndNil(F);
end;
- F := TFileStream.Create(FileNm, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(FileNm, fmOpenReadWrite);
F.Seek(0, soEnd);
with DSS.ActiveCircuit do
begin
FSWrite(F, IntToStr(Solution.Year), Separator);
FSWrite(F, LoadDurCurve, Separator);
FSWrite(F, IntToStr(Solution.DynaVars.intHour), Separator);
- FSWrite(F, Pad('"' + Uppercase(pElem.Name) + '"', 14));
+ FSWrite(F, Pad('"' + AnsiUpperCase(pElem.Name) + '"', 14));
for j := 1 to NumGenRegisters do
FSWrite(F, Separator, Format('%10.0f', [PElem.Registers[j]]));
FSWriteln(F);
@@ -2144,7 +1974,6 @@ procedure WriteMultipleGenMeterFiles(DSS: TDSSContext);
end;
pElem := DSS.ActiveCircuit.Generators.Next;
end;
-
end;
@@ -2161,7 +1990,6 @@ procedure WriteSingleGenMeterFile(DSS: TDSSContext; FileNm: String);
begin
-
GeneratorClass := DSS.GeneratorClass;
if GeneratorClass = NIL then
Exit; // oops somewhere!!
@@ -2172,11 +2000,11 @@ procedure WriteSingleGenMeterFile(DSS: TDSSContext; FileNm: String);
if FileExists(FileNm) then
begin // See if it has already been written on
- F := TFileStream.Create(FileNm, fmOpenRead);
+ F := TBufferedFileStream.Create(FileNm, fmOpenRead or fmShareDenyWrite);
if ((F.Position+1) < F.Size) then
begin
FSReadLn(F, TestStr);
- {See if it likely that the file is OK}
+ // See if it likely that the file is OK
if CompareText(Copy(TestStr, 1, 4), 'Year') = 0 then
RewriteFile := FALSE // Assume the file is OK
else
@@ -2193,12 +2021,12 @@ procedure WriteSingleGenMeterFile(DSS: TDSSContext; FileNm: String);
ReWriteFile := TRUE;
end;
- {Either open or append the file}
+ // Either open or append the file
if RewriteFile then
begin
FreeAndNil(F);
- F := TFileStream.Create(FileNm, fmCreate);
- {Write New Header}
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
+ // Write New Header
FSWrite(F, 'Year, LDCurve, Hour, Generator');
for i := 1 to NumGenRegisters do
FSWrite(F, Separator, '"' + GeneratorClass.RegisterNames[i] + '"');
@@ -2207,7 +2035,7 @@ procedure WriteSingleGenMeterFile(DSS: TDSSContext; FileNm: String);
else
begin
FreeAndNil(F);
- F := TFileStream.Create(FileNm, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(FileNm, fmOpenReadWrite);
F.Seek(0, soEnd);
end;
@@ -2221,7 +2049,7 @@ procedure WriteSingleGenMeterFile(DSS: TDSSContext; FileNm: String);
FSWrite(F, IntToStr(Solution.Year), Separator);
FSWrite(F, LoadDurCurve, Separator);
FSWrite(F, IntToStr(Solution.DynaVars.intHour), Separator);
- FSWrite(F, Pad('"' + Uppercase(pElem.Name) + '"', 14));
+ FSWrite(F, Pad('"' + AnsiUpperCase(pElem.Name) + '"', 14));
for j := 1 to NumGenRegisters do
FSWrite(F, Separator, Format('%10.0f', [PElem.Registers[j]]));
FSWriteln(F);
@@ -2237,8 +2065,6 @@ procedure WriteSingleGenMeterFile(DSS: TDSSContext; FileNm: String);
FreeAndNil(F);
end;
-
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -2270,12 +2096,12 @@ procedure WriteMultiplePVSystemMeterFiles(DSS: TDSSContext);
if pElem.Enabled then
begin
try
- FileNm := DSS.OutputDirectory + 'EXP_PV_' + Uppercase(pElem.Name) + '.CSV';
+ FileNm := DSS.OutputDirectory + 'EXP_PV_' + AnsiUpperCase(pElem.Name) + '.csv';
if not FileExists(FileNm) then
begin
- F := TFileStream.Create(FileNm, fmCreate);
- {Write New Header}
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
+ // Write New Header
FSWrite(F, 'Year, LDCurve, Hour, PVSystem');
for i := 1 to NumPVSystemRegisters do
FSWrite(F, Separator, '"' + DSS.PVSystemClass.RegisterNames[i] + '"');
@@ -2283,14 +2109,14 @@ procedure WriteMultiplePVSystemMeterFiles(DSS: TDSSContext);
FreeAndNil(F);
end;
- F := TFileStream.Create(FileNm, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(FileNm, fmOpenReadWrite);
F.Seek(0, soEnd);
with DSS.ActiveCircuit do
begin
FSWrite(F, IntToStr(Solution.Year), Separator);
FSWrite(F, LoadDurCurve, Separator);
FSWrite(F, IntToStr(Solution.DynaVars.intHour), Separator);
- FSWrite(F, Pad('"' + Uppercase(pElem.Name) + '"', 14));
+ FSWrite(F, Pad('"' + AnsiUpperCase(pElem.Name) + '"', 14));
for j := 1 to NumPVSystemRegisters do
FSWrite(F, Separator, Format('%10.0f', [PElem.Registers[j]]));
FSWriteln(F);
@@ -2303,7 +2129,6 @@ procedure WriteMultiplePVSystemMeterFiles(DSS: TDSSContext);
end;
pElem := DSS.ActiveCircuit.PVSystems.Next;
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -2317,7 +2142,6 @@ procedure WriteMultiplePVSystem2MeterFiles(DSS: TDSSContext);
Separator: String;
begin
-
if DSS.PVSystem2Class = NIL then
Exit; // oops somewhere!!
Separator := ', ';
@@ -2328,12 +2152,12 @@ procedure WriteMultiplePVSystem2MeterFiles(DSS: TDSSContext);
if pElem.Enabled then
begin
try
- FileNm := DSS.OutputDirectory + 'EXP_PV_' + Uppercase(pElem.Name) + '.CSV';
+ FileNm := DSS.OutputDirectory + 'EXP_PV_' + AnsiUpperCase(pElem.Name) + '.csv';
if not FileExists(FileNm) then
begin
- F := TFileStream.Create(FileNm, fmCreate);
- {Write New Header}
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
+ // Write New Header
FSWrite(F, 'Year, LDCurve, Hour, PVSystem');
for i := 1 to NumPVSystem2Registers do
FSWrite(F, Separator, '"' + DSS.PVSystem2Class.RegisterNames[i] + '"');
@@ -2341,14 +2165,14 @@ procedure WriteMultiplePVSystem2MeterFiles(DSS: TDSSContext);
FreeAndNil(F);
end;
- F := TFileStream.Create(FileNm, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(FileNm, fmOpenReadWrite);
F.Seek(0, soEnd);
with DSS.ActiveCircuit do
begin
FSWrite(F, IntToStr(Solution.Year), Separator);
FSWrite(F, LoadDurCurve, Separator);
FSWrite(F, IntToStr(Solution.DynaVars.intHour), Separator);
- FSWrite(F, Pad('"' + Uppercase(pElem.Name) + '"', 14));
+ FSWrite(F, Pad('"' + AnsiUpperCase(pElem.Name) + '"', 14));
for j := 1 to NumPVSystem2Registers do
FSWrite(F, Separator, Format('%10.0f', [PElem.Registers[j]]));
FSWriteln(F);
@@ -2361,7 +2185,6 @@ procedure WriteMultiplePVSystem2MeterFiles(DSS: TDSSContext);
end;
pElem := DSS.ActiveCircuit.PVSystems.Next;
end;
-
end;
@@ -2394,11 +2217,11 @@ procedure WriteSinglePVSystemMeterFile(DSS: TDSSContext; FileNm: String);
if FileExists(FileNm) then
begin // See if it has already been written on
- F := TFileStream.Create(FileNm, fmOpenRead);
+ F := TBufferedFileStream.Create(FileNm, fmOpenRead or fmShareDenyWrite);
if ((F.Position+1) < F.Size) then
begin
FSReadLn(F, TestStr);
- {See if it likely that the file is OK}
+ // See if it likely that the file is OK
if CompareText(Copy(TestStr, 1, 4), 'Year') = 0 then
RewriteFile := FALSE // Assume the file is OK
else
@@ -2415,12 +2238,12 @@ procedure WriteSinglePVSystemMeterFile(DSS: TDSSContext; FileNm: String);
ReWriteFile := TRUE;
end;
- {Either open or append the file}
+ // Either open or append the file
if RewriteFile then
begin
FreeAndNil(F);
- F := TFileStream.Create(FileNm, fmCreate);
- {Write New Header}
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
+ // Write New Header
FSWrite(F, 'Year, LDCurve, Hour, PVSystem');
for i := 1 to NumGenRegisters do
FSWrite(F, Separator, '"' + DSS.PVSystemClass.RegisterNames[i] + '"');
@@ -2429,7 +2252,7 @@ procedure WriteSinglePVSystemMeterFile(DSS: TDSSContext; FileNm: String);
else
begin
FreeAndNil(F);
- F := TFileStream.Create(FileNm, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(FileNm, fmOpenReadWrite);
F.Seek(0, soEnd);
end;
@@ -2443,7 +2266,7 @@ procedure WriteSinglePVSystemMeterFile(DSS: TDSSContext; FileNm: String);
FSWrite(F, IntToStr(Solution.Year), Separator);
FSWrite(F, LoadDurCurve, Separator);
FSWrite(F, IntToStr(Solution.DynaVars.intHour), Separator);
- FSWrite(F, Pad('"' + Uppercase(pElem.Name) + '"', 14));
+ FSWrite(F, Pad('"' + AnsiUpperCase(pElem.Name) + '"', 14));
for j := 1 to NumPVSystemRegisters do
FSWrite(F, Separator, Format('%10.0f', [PElem.Registers[j]]));
FSWriteln(F);
@@ -2459,8 +2282,6 @@ procedure WriteSinglePVSystemMeterFile(DSS: TDSSContext; FileNm: String);
FreeAndNil(F);
end;
-
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -2474,7 +2295,6 @@ procedure WriteSinglePVSystem2MeterFile(DSS: TDSSContext; FileNm: String);
begin
-
if DSS.PVSystem2Class = NIL then
Exit; // oops somewhere!!
Separator := ', ';
@@ -2484,11 +2304,11 @@ procedure WriteSinglePVSystem2MeterFile(DSS: TDSSContext; FileNm: String);
if FileExists(FileNm) then
begin // See if it has already been written on
- F := TFileStream.Create(FileNm, fmOpenRead);
+ F := TBufferedFileStream.Create(FileNm, fmOpenRead or fmShareDenyWrite);
if ((F.Position+1) < F.Size) then
begin
FSReadLn(F, TestStr);
- {See if it likely that the file is OK}
+ // See if it likely that the file is OK
if CompareText(Copy(TestStr, 1, 4), 'Year') = 0 then
RewriteFile := FALSE // Assume the file is OK
else
@@ -2504,12 +2324,12 @@ procedure WriteSinglePVSystem2MeterFile(DSS: TDSSContext; FileNm: String);
ReWriteFile := TRUE;
end;
- {Either open or append the file}
+ // Either open or append the file
if RewriteFile then
begin
FreeAndNil(F);
- F := TFileStream.Create(FileNm, fmCreate);
- {Write New Header}
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
+ // Write New Header
FSWrite(F, 'Year, LDCurve, Hour, PVSystem');
for i := 1 to NumGenRegisters do
FSWrite(F, Separator, '"' + DSS.PVSystem2Class.RegisterNames[i] + '"');
@@ -2518,7 +2338,7 @@ procedure WriteSinglePVSystem2MeterFile(DSS: TDSSContext; FileNm: String);
else
begin
FreeAndNil(F);
- F := TFileStream.Create(FileNm, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(FileNm, fmOpenReadWrite);
F.Seek(0, soEnd);
end;
@@ -2532,7 +2352,7 @@ procedure WriteSinglePVSystem2MeterFile(DSS: TDSSContext; FileNm: String);
FSWrite(F, IntToStr(Solution.Year), Separator);
FSWrite(F, LoadDurCurve, Separator);
FSWrite(F, IntToStr(Solution.DynaVars.intHour), Separator);
- FSWrite(F, Pad('"' + Uppercase(pElem.Name) + '"', 14));
+ FSWrite(F, Pad('"' + AnsiUpperCase(pElem.Name) + '"', 14));
for j := 1 to NumPVSystem2Registers do
FSWrite(F, Separator, Format('%10.0f', [PElem.Registers[j]]));
FSWriteln(F);
@@ -2548,8 +2368,6 @@ procedure WriteSinglePVSystem2MeterFile(DSS: TDSSContext; FileNm: String);
FreeAndNil(F);
end;
-
-
end;
@@ -2583,12 +2401,12 @@ procedure WriteMultipleStorageMeterFiles(DSS: TDSSContext);
if pElem.Enabled then
begin
try
- FileNm := DSS.OutputDirectory + 'EXP_PV_' + Uppercase(pElem.Name) + '.CSV';
+ FileNm := DSS.OutputDirectory + 'EXP_PV_' + AnsiUpperCase(pElem.Name) + '.csv';
if not FileExists(FileNm) then
begin
- F := TFileStream.Create(FileNm, fmCreate);
- {Write New Header}
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
+ // Write New Header
FSWrite(F, 'Year, LDCurve, Hour, Storage');
for i := 1 to NumStorageRegisters do
FSWrite(F, Separator, '"' + DSS.StorageClass.RegisterNames[i] + '"');
@@ -2596,14 +2414,14 @@ procedure WriteMultipleStorageMeterFiles(DSS: TDSSContext);
FreeAndNil(F);
end;
- F := TFileStream.Create(FileNm, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(FileNm, fmOpenReadWrite);
F.Seek(0, soEnd);
with DSS.ActiveCircuit do
begin
FSWrite(F, IntToStr(Solution.Year), Separator);
FSWrite(F, LoadDurCurve, Separator);
FSWrite(F, IntToStr(Solution.DynaVars.intHour), Separator);
- FSWrite(F, Pad('"' + Uppercase(pElem.Name) + '"', 14));
+ FSWrite(F, Pad('"' + AnsiUpperCase(pElem.Name) + '"', 14));
for j := 1 to NumStorageRegisters do
FSWrite(F, Separator, Format('%10.0f', [PElem.Registers[j]]));
FSWriteln(F);
@@ -2616,7 +2434,6 @@ procedure WriteMultipleStorageMeterFiles(DSS: TDSSContext);
end;
pElem := DSS.ActiveCircuit.StorageElements.Next;
end;
-
end;
procedure WriteMultipleStorage2MeterFiles(DSS: TDSSContext);
@@ -2628,7 +2445,6 @@ procedure WriteMultipleStorage2MeterFiles(DSS: TDSSContext);
Separator: String;
begin
-
if DSS.Storage2Class = NIL then
Exit; // oops somewhere!!
Separator := ', ';
@@ -2639,12 +2455,12 @@ procedure WriteMultipleStorage2MeterFiles(DSS: TDSSContext);
if pElem.Enabled then
begin
try
- FileNm := DSS.OutputDirectory + 'EXP_PV_' + Uppercase(pElem.Name) + '.CSV';
+ FileNm := DSS.OutputDirectory + 'EXP_PV_' + AnsiUpperCase(pElem.Name) + '.csv';
if not FileExists(FileNm) then
begin
- F := TFileStream.Create(FileNm, fmCreate);
- {Write New Header}
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
+ // Write New Header
FSWrite(F, 'Year, LDCurve, Hour, Storage');
for i := 1 to NumStorage2Registers do
FSWrite(F, Separator, '"' + DSS.Storage2Class.RegisterNames[i] + '"');
@@ -2652,14 +2468,14 @@ procedure WriteMultipleStorage2MeterFiles(DSS: TDSSContext);
FreeAndNil(F);
end;
- F := TFileStream.Create(FileNm, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(FileNm, fmOpenReadWrite);
F.Seek(0, soEnd);
with DSS.ActiveCircuit do
begin
FSWrite(F, IntToStr(Solution.Year), Separator);
FSWrite(F, LoadDurCurve, Separator);
FSWrite(F, IntToStr(Solution.DynaVars.intHour), Separator);
- FSWrite(F, Pad('"' + Uppercase(pElem.Name) + '"', 14));
+ FSWrite(F, Pad('"' + AnsiUpperCase(pElem.Name) + '"', 14));
for j := 1 to NumStorage2Registers do
FSWrite(F, Separator, Format('%10.0f', [PElem.Registers[j]]));
FSWriteln(F);
@@ -2672,7 +2488,6 @@ procedure WriteMultipleStorage2MeterFiles(DSS: TDSSContext);
end;
pElem := DSS.ActiveCircuit.StorageElements.Next;
end;
-
end;
@@ -2704,11 +2519,11 @@ procedure WriteSingleStorageMeterFile(DSS: TDSSContext; FileNm: String);
if FileExists(FileNm) then
begin // See if it has already been written on
- F := TFileStream.Create(FileNm, fmOpenRead);
+ F := TBufferedFileStream.Create(FileNm, fmOpenRead or fmShareDenyWrite);
if ((F.Position+1) < F.Size) then
begin
FSReadLn(F, TestStr);
- {See if it likely that the file is OK}
+ // See if it likely that the file is OK
if CompareText(Copy(TestStr, 1, 4), 'Year') = 0 then
RewriteFile := FALSE // Assume the file is OK
else
@@ -2725,12 +2540,12 @@ procedure WriteSingleStorageMeterFile(DSS: TDSSContext; FileNm: String);
ReWriteFile := TRUE;
end;
- {Either open or append the file}
+ // Either open or append the file
if RewriteFile then
begin
FreeAndNil(F);
- F := TFileStream.Create(FileNm, fmCreate);
- {Write New Header}
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
+ // Write New Header
FSWrite(F, 'Year, LDCurve, Hour, Storage');
for i := 1 to NumStorageRegisters do
FSWrite(F, Separator, '"' + DSS.StorageClass.RegisterNames[i] + '"');
@@ -2739,7 +2554,7 @@ procedure WriteSingleStorageMeterFile(DSS: TDSSContext; FileNm: String);
else
begin
FreeAndNil(F);
- F := TFileStream.Create(FileNm, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(FileNm, fmOpenReadWrite);
F.Seek(0, soEnd);
end;
@@ -2752,7 +2567,7 @@ procedure WriteSingleStorageMeterFile(DSS: TDSSContext; FileNm: String);
FSWrite(F, IntToStr(Solution.Year), Separator);
FSWrite(F, LoadDurCurve, Separator);
FSWrite(F, IntToStr(Solution.DynaVars.intHour), Separator);
- FSWrite(F, Pad('"' + Uppercase(pElem.Name) + '"', 14));
+ FSWrite(F, Pad('"' + AnsiUpperCase(pElem.Name) + '"', 14));
for j := 1 to NumStorageRegisters do
FSWrite(F, Separator, Format('%10.0f', [PElem.Registers[j]]));
FSWriteln(F);
@@ -2768,8 +2583,6 @@ procedure WriteSingleStorageMeterFile(DSS: TDSSContext; FileNm: String);
FreeAndNil(F);
end;
-
-
end;
@@ -2782,7 +2595,6 @@ procedure WriteSingleStorage2MeterFile(DSS: TDSSContext; FileNm: String);
ReWriteFile: Boolean;
begin
-
if DSS.StorageClass = NIL then
Exit; // oops somewhere!!
Separator := ', ';
@@ -2791,11 +2603,11 @@ procedure WriteSingleStorage2MeterFile(DSS: TDSSContext; FileNm: String);
if FileExists(FileNm) then
begin // See if it has already been written on
- F := TFileStream.Create(FileNm, fmOpenRead);
+ F := TBufferedFileStream.Create(FileNm, fmOpenRead or fmShareDenyWrite);
if ((F.Position+1) < F.Size) then
begin
FSReadLn(F, TestStr);
- {See if it likely that the file is OK}
+ // See if it likely that the file is OK
if CompareText(Copy(TestStr, 1, 4), 'Year') = 0 then
RewriteFile := FALSE // Assume the file is OK
else
@@ -2812,12 +2624,12 @@ procedure WriteSingleStorage2MeterFile(DSS: TDSSContext; FileNm: String);
ReWriteFile := TRUE;
end;
- {Either open or append the file}
+ // Either open or append the file
if RewriteFile then
begin
FreeAndNil(F);
- F := TFileStream.Create(FileNm, fmCreate);
- {Write New Header}
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
+ // Write New Header
FSWrite(F, 'Year, LDCurve, Hour, Storage');
for i := 1 to NumStorage2Registers do
FSWrite(F, Separator, '"' + DSS.Storage2Class.RegisterNames[i] + '"');
@@ -2826,7 +2638,7 @@ procedure WriteSingleStorage2MeterFile(DSS: TDSSContext; FileNm: String);
else
begin
FreeAndNil(F);
- F := TFileStream.Create(FileNm, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(FileNm, fmOpenReadWrite);
F.Seek(0, soEnd);
end;
@@ -2840,7 +2652,7 @@ procedure WriteSingleStorage2MeterFile(DSS: TDSSContext; FileNm: String);
FSWrite(F, IntToStr(Solution.Year), Separator);
FSWrite(F, LoadDurCurve, Separator);
FSWrite(F, IntToStr(Solution.DynaVars.intHour), Separator);
- FSWrite(F, Pad('"' + Uppercase(pElem.Name) + '"', 14));
+ FSWrite(F, Pad('"' + AnsiUpperCase(pElem.Name) + '"', 14));
for j := 1 to NumStorage2Registers do
FSWrite(F, Separator, Format('%10.0f', [PElem.Registers[j]]));
FSWriteln(F);
@@ -2866,12 +2678,10 @@ procedure ExportGenMeters(DSS: TDSSContext; FileNm: String);
begin
-
- if Lowercase(Copy(FileNm, 1, 2)) = '/m' then
+ if AnsiLowerCase(Copy(FileNm, 1, 2)) = '/m' then
WriteMultipleGenMeterFiles(DSS)
else
WriteSingleGenMeterFile(DSS, FileNM);
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -2882,12 +2692,10 @@ procedure ExportPVSystemMeters(DSS: TDSSContext; FileNm: String);
begin
-
- if Lowercase(Copy(FileNm, 1, 2)) = '/m' then
+ if AnsiLowerCase(Copy(FileNm, 1, 2)) = '/m' then
WriteMultiplePVSystemMeterFiles(DSS)
else
WriteSinglePVSystemMeterFile(DSS, FileNM);
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -2898,12 +2706,10 @@ procedure ExportPVSystem2Meters(DSS: TDSSContext; FileNm: String);
begin
-
- if Lowercase(Copy(FileNm, 1, 2)) = '/m' then
+ if AnsiLowerCase(Copy(FileNm, 1, 2)) = '/m' then
WriteMultiplePVSystem2MeterFiles(DSS)
else
WriteSinglePVSystem2MeterFile(DSS, FileNM);
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -2914,12 +2720,10 @@ procedure ExportStorageMeters(DSS: TDSSContext; FileNm: String);
begin
-
- if Lowercase(Copy(FileNm, 1, 2)) = '/m' then
+ if AnsiLowerCase(Copy(FileNm, 1, 2)) = '/m' then
WriteMultipleStorageMeterFiles(DSS)
else
WriteSingleStorageMeterFile(DSS, FileNM);
-
end;
procedure ExportStorage2Meters(DSS: TDSSContext; FileNm: String);
@@ -2929,34 +2733,26 @@ procedure ExportStorage2Meters(DSS: TDSSContext; FileNm: String);
begin
-
- if Lowercase(Copy(FileNm, 1, 2)) = '/m' then
+ if AnsiLowerCase(Copy(FileNm, 1, 2)) = '/m' then
WriteMultipleStorage2MeterFiles(DSS)
else
WriteSingleStorage2MeterFile(DSS, FileNM);
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure ExportLoads(DSS: TDSSContext; FileNm: String);
-
// Export Loads to view present allocation
-
-
var
F: TFileStream = nil;
pElem: TLoadObj;
Separator: String;
sout: String;
begin
-
Separator := ', ';
-
try
-
- F := TFileStream.Create(FileNm, fmCreate);
- {Write Header}
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
+ // Write Header
FSWriteln(F, 'Load, Connected KVA, Allocation Factor, Phases, kW, kvar, PF, Model');
pElem := DSS.ActiveCircuit.Loads.First;
@@ -2966,7 +2762,7 @@ procedure ExportLoads(DSS: TDSSContext; FileNm: String);
with pElem do
begin
WriteStr(sout,
- Uppercase(Name),
+ AnsiUpperCase(Name),
Separator, ConnectedkVA: 8: 1,
Separator, kVAAllocationFactor: 5: 3,
Separator, NPhases: 0,
@@ -2984,38 +2780,29 @@ procedure ExportLoads(DSS: TDSSContext; FileNm: String);
DSS.GlobalResult := FileNm;
finally
-
FreeAndNil(F);
-
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure ExportCapacity(DSS: TDSSContext; FileNm: String);
-
-{
- Similar to export currents except does only max of the phases and compares that
- to the Normamps and Emergamps rating
-}
-
+// Similar to export currents except does only max of the phases and compares that
+// to the Normamps and Emergamps rating
var
F: TFileStream = nil;
cBuffer: pComplexArray;
pElem: TPDElement;
-
begin
-
cBuffer := NIL;
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
Getmem(cBuffer, sizeof(Complex) * GetMaxCktElementSize(DSS));
FSWriteln(F, 'Name, Imax, %normal, %emergency, kW, kvar, NumCustomers, TotalCustomers, NumPhases, kVBase');
- // PDELEMENTS ONLY
+ // PDELEMENTS ONLY
pElem := DSS.ActiveCircuit.PDElements.First;
while pElem <> NIL do
begin
@@ -3035,8 +2822,6 @@ procedure ExportCapacity(DSS: TDSSContext; FileNm: String);
FreeAndNil(F);
end;
-
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -3058,21 +2843,20 @@ procedure ExportOverloads(DSS: TDSSContext; FileNm: String);
Spower: Double;
sout: String;
begin
-
cBuffer := NIL;
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
- {Allocate cBuffer big enough for largest circuit element}
+ // Allocate cBuffer big enough for largest circuit element
Getmem(cbuffer, sizeof(cBuffer^[1]) * GetMaxCktElementSize(DSS));
- {Sequence Currents}
+ // Sequence Currents
FSWriteln(F, 'Element, Terminal, I1, AmpsOver, kVAOver, %Normal, %Emergency, I2, %I2/I1, I0, %I0/I1');
Separator := ', ';
- // PDELEMENTS Only
+ // PDELEMENTS Only
PDelem := DSS.ActiveCircuit.PDElements.First;
while PDelem <> NIL do
@@ -3110,10 +2894,10 @@ procedure ExportOverloads(DSS: TDSSContext; FileNm: String);
if (PdElem.Normamps > 0.0) or (PdElem.Emergamps > 0.0) then
if (CMax > PDElem.NormAmps) or (Cmax > pdelem.EmergAmps) then
begin
- // Get terminal 1 power
+ // Get terminal 1 power
Spower := Cabs(PDElem.Power[1]) * 0.001; // kW
- FSWrite(F, Format('%s, %d, ', [Pad(('"' + pDelem.DSSClassName + '.' + Uppercase(pDelem.Name) + '"'), 22), j]));
+ FSWrite(F, Format('%s, %d, ', [Pad(('"' + pDelem.DSSClassName + '.' + AnsiUpperCase(pDelem.Name) + '"'), 22), j]));
FSWrite(F, Format('%8.2f, ', [I1]));
if j = 1 then
begin // Only for 1st Terminal
@@ -3185,13 +2969,12 @@ procedure ExportUnserved(DSS: TDSSContext; FileNm: String; UE_Only: Boolean);
DoIt: Boolean;
sout: String;
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Load, Bus, kW, EEN_Factor, UE_Factor');
- // Load
+ // Load
pLoad := DSS.ActiveCircuit.Loads.First;
while pLoad <> NIL do
begin
@@ -3210,7 +2993,7 @@ procedure ExportUnserved(DSS: TDSSContext; FileNm: String; UE_Only: Boolean);
if DoIt then
begin
WriteStr(sout,
- Uppercase(pLoad.Name), ', ',
+ AnsiUpperCase(pLoad.Name), ', ',
pLoad.GetBus(1), ', ',
pLoad.kWBase: 8: 0, ', ',
pLoad.EEN_Factor: 9: 3, ', ',
@@ -3231,25 +3014,20 @@ procedure ExportUnserved(DSS: TDSSContext; FileNm: String; UE_Only: Boolean);
FreeAndNil(F);
end;
-
end;
procedure ExportYprim(DSS: TDSSContext; FileNm: String);
-
-{Exports YPrim matrices for all Circuit Elements}
-
+// Exports YPrim matrices for all Circuit Elements
var
F: TFileStream = nil;
i, j, k: Integer;
cValues: pComplexArray;
-
begin
-
if DSS.ActiveCircuit = NIL then
Exit;
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
with DSS.ActiveCircuit do
begin
@@ -3261,7 +3039,7 @@ procedure ExportYprim(DSS: TDSSContext; FileNm: String);
if (ActiveCktElement is TPDElement) or (ActiveCktElement is TPCElement) then
with ActiveCktElement do
begin
- FSWriteln(F, ParentClass.Name, '.', Uppercase(Name));
+ FSWriteln(F, ParentClass.Name, '.', AnsiUpperCase(Name));
cValues := GetYprimValues(ALL_YPRIM);
for i := 1 to Yorder do
begin
@@ -3282,14 +3060,11 @@ procedure ExportYprim(DSS: TDSSContext; FileNm: String);
FreeAndNil(F);
end;
-
end;
// illustrate retrieval of System Y using compressed column format
procedure ExportY(DSS: TDSSContext; FileNm: String; TripletOpt: Boolean);
-
-{Exports System Y Matrix in Node Order}
-
+// Exports System Y Matrix in Node Order
var
F: TFileStream = nil;
i, j, p: Longword;
@@ -3301,22 +3076,21 @@ procedure ExportY(DSS: TDSSContext; FileNm: String; TripletOpt: Boolean);
re, im: Double;
begin
-
if DSS.ActiveCircuit = NIL then
Exit;
hY := DSS.ActiveCircuit.Solution.hY;
if hY <= 0 then
begin
- DoSimpleMsg(DSS, 'Y Matrix not Built.', 222);
+ DoSimpleMsg(DSS, _('Y Matrix not Built.'), 222);
Exit;
end;
- // this compresses the entries if necessary - no extra work if already solved
+ // this compresses the entries if necessary - no extra work if already solved
FactorSparseMatrix(hY);
GetNNZ(hY, @nNZ);
GetSize(hY, @nBus); // we should already know this
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
if TripletOpt then
begin
@@ -3343,7 +3117,7 @@ procedure ExportY(DSS: TDSSContext; FileNm: String; TripletOpt: Boolean);
SetLength(RowIdx, nNZ);
SetLength(cVals, nNZ);
GetCompressedMatrix(hY, nBus + 1, nNZ, @ColPtr[0], @RowIdx[0], @cVals[0]);
- {Write out fully qualified Bus Names}
+ // Write out fully qualified Bus Names
with DSS.ActiveCircuit do
begin
FSWriteln(F, Format('%d, ', [NumNodes]));
@@ -3356,13 +3130,13 @@ procedure ExportY(DSS: TDSSContext; FileNm: String; TripletOpt: Boolean);
for i := 1 to NumNodes do
begin
j := MapNodeToBus^[i].BusRef;
- FSWrite(F, Format('"%s.%-d", ', [Uppercase(BusList.NameOfIndex(j)), MapNodeToBus^[i].NodeNum]));
+ FSWrite(F, Format('"%s.%-d", ', [AnsiUpperCase(BusList.NameOfIndex(j)), MapNodeToBus^[i].NodeNum]));
for j := 1 to NumNodes do
begin
re := 0.0;
im := 0.0;
- // search for a non-zero element [i,j]
- // DSS indices are 1-based, KLU indices are 0-based
+ // search for a non-zero element [i,j]
+ // DSS indices are 1-based, KLU indices are 0-based
for p := ColPtr[j - 1] to ColPtr[j] - 1 do
begin
if RowIdx[p] + 1 = i then
@@ -3396,16 +3170,14 @@ procedure ExportSeqZ(DSS: TDSSContext; FileNm: String);
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Bus, NumNodes, R1, X1, R0, X0, Z1, Z0, "X1/R1", "X0/R0"');
with DSS.ActiveCircuit do
begin
for i := 1 to NumBuses do
begin
-
Z1 := Buses^[i].Zsc1;
Z0 := Buses^[i].Zsc0;
if Z1.re <> 0.0 then
@@ -3419,7 +3191,7 @@ procedure ExportSeqZ(DSS: TDSSContext; FileNm: String);
FSWriteln(F,
Format('"%s", %d, %10.6g, %10.6g, %10.6g, %10.6g, %10.6g, %10.6g, %8.4g, %8.4g',
- [Uppercase(BusList.NameOfIndex(i)), Buses^[i].NumNodesThisBus,
+ [AnsiUpperCase(BusList.NameOfIndex(i)), Buses^[i].NumNodesThisBus,
Z1.re, Z1.im, Z0.Re, Z0.im, Cabs(Z1), Cabs(Z0), X1R1, X0R0]
));
@@ -3433,7 +3205,6 @@ procedure ExportSeqZ(DSS: TDSSContext; FileNm: String);
FreeAndNil(F);
end;
-
end;
procedure ExportUuids(DSS: TDSSContext; FileNm: String);
@@ -3446,7 +3217,7 @@ procedure ExportUuids(DSS: TDSSContext; FileNm: String);
clsSpac: TLineSpacing;
clsTape: TTSData;
clsConc: TCNData;
- pName: TNamedObject;
+ pName: TDSSObject;
i: integer;
begin
try
@@ -3458,15 +3229,13 @@ procedure ExportUuids(DSS: TDSSContext; FileNm: String);
clsTape := DSS.DSSClassList.Get(DSS.ClassNames.Find('TSData'));
clsConc := DSS.DSSClassList.Get(DSS.ClassNames.Find('CNData'));
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
- pName := DSS.ActiveCircuit;
- FSWriteln(F, Format('%s.%s %s', [pName.DSSClassName, pName.LocalName, pName.ID]));
+ FSWriteln(F, Format('Circuit.%s %s', [DSS.ActiveCircuit.LocalName, DSS.ActiveCircuit.ID]));
for i :=1 to DSS.ActiveCircuit.NumBuses do
begin
- pName := DSS.ActiveCircuit.Buses^[i];
- FSWriteln(F, Format ('%s.%s %s', [pName.DSSClassName, pName.LocalName, pName.ID]));
+ FSWriteln(F, Format ('Bus.%s %s', [DSS.ActiveCircuit.Buses^[i].LocalName, DSS.ActiveCircuit.Buses^[i].ID]));
end;
pName := DSS.ActiveCircuit.CktElements.First;
@@ -3538,7 +3307,7 @@ procedure ExportCounts(DSS: TDSSContext; FileNm: String);
cls: TDSSClass;
begin
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Format: DSS Class Name = Instance Count');
FSWriteln(F);
cls := DSS.DSSClassList.First;
@@ -3562,12 +3331,12 @@ procedure ExportSummary(DSS: TDSSContext; FileNm: String);
if FileExists(FileNm) then
begin
- F := TFileStream.Create(FileNm, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(FileNm, fmOpenReadWrite);
F.Seek(0, soEnd);
end
else
begin // Create and write the header
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWrite(F, 'DateTime, CaseName, ');
FSWrite(F, 'Status, Mode, Number, LoadMult, NumDevices, NumBuses, NumNodes');
FSWrite(F, ', Iterations, ControlMode, ControlIterations');
@@ -3593,14 +3362,14 @@ procedure ExportSummary(DSS: TDSSContext; FileNm: String);
else
FSWrite(F, 'UnSolved');
- FSWrite(F, Format(', %s', [GetSolutionModeID(DSS)]));
+ FSWrite(F, Format(', %s', [DSS.SolveModeEnum.OrdinalToString(ord(DSS.ActiveCircuit.Solution.mode))]));
FSWrite(F, Format(', %d', [DSS.ActiveCircuit.Solution.NumberofTimes]));
FSWrite(F, Format(', %8.3f', [DSS.ActiveCircuit.LoadMultiplier]));
FSWrite(F, Format(', %d', [DSS.ActiveCircuit.NumDevices]));
FSWrite(F, Format(', %d', [DSS.ActiveCircuit.NumBuses]));
FSWrite(F, Format(', %d', [DSS.ActiveCircuit.NumNodes]));
FSWrite(F, Format(', %d', [DSS.ActiveCircuit.Solution.Iteration]));
- FSWrite(F, Format(', %s', [GetControlModeID(DSS)]));
+ FSWrite(F, Format(', %s', [DSS.ControlModeEnum.OrdinalToString(DSS.ActiveCircuit.Solution.Controlmode)]));
FSWrite(F, Format(', %d', [DSS.ActiveCircuit.Solution.ControlIteration]));
FSWrite(F, Format(', %d', [DSS.ActiveCircuit.Solution.MostIterationsDone]));
if DSS.ActiveCircuit <> NIL then
@@ -3610,10 +3379,10 @@ procedure ExportSummary(DSS: TDSSContext; FileNm: String);
FSWrite(F, Format(', %d', [DSS.ActiveCircuit.Solution.DynaVars.intHour]));
FSWrite(F, Format(', %-.5g', [GetMaxPUVoltage(DSS)]));
FSWrite(F, Format(', %-.5g', [GetMinPUVoltage(DSS, TRUE)]));
- cPower := CmulReal(GetTotalPowerFromSources(DSS), 0.000001); // MVA
+ cPower := GetTotalPowerFromSources(DSS) * 0.000001; // MVA
FSWrite(F, Format(', %-.6g', [cPower.re]));
FSWrite(F, Format(', %-.6g', [cPower.im]));
- cLosses := CmulReal(DSS.ActiveCircuit.Losses, 0.000001);
+ cLosses := DSS.ActiveCircuit.Losses * 0.000001;
if cPower.re <> 0.0 then
FSWrite(F, Format(', %-.6g, %-.4g', [cLosses.re, (Closses.re / cPower.re * 100.0)]))
else
@@ -3633,22 +3402,18 @@ procedure ExportSummary(DSS: TDSSContext; FileNm: String);
procedure ExportBusCoords(DSS: TDSSContext; FileNm: String);
// Export bus x, y coordinates
-
var
F: TFileStream = nil;
i: Integer;
-
-
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
with DSS.ActiveCircuit do
for i := 1 to NumBuses do
begin
if Buses^[i].CoordDefined then
- FSWriteln(F, Format('%s, %-13.11g, %-13.11g', [CheckForBlanks(Uppercase(BusList.NameOfIndex(i))), Buses^[i].X, Buses^[i].Y]));
+ FSWriteln(F, Format('%s, %-13.11g, %-13.11g', [CheckForBlanks(AnsiUpperCase(BusList.NameOfIndex(i))), Buses^[i].X, Buses^[i].Y]));
end;
DSS.GlobalResult := FileNm;
@@ -3666,7 +3431,7 @@ procedure WriteNewLine(F: TFileStream;
CenterMarkerCode, NodeMarkerCode, NodeMarkerWidth: Integer);
begin
- FSWrite(F, Format('%s, %.6g, %.6g, %.6g, %.6g,', [Uppercase(CktElementName), DistFromMeter1, puV1, DistFromMeter2, puV2]));
+ FSWrite(F, Format('%s, %.6g, %.6g, %.6g, %.6g,', [AnsiUpperCase(CktElementName), DistFromMeter1, puV1, DistFromMeter2, puV2]));
FSWrite(F, Format('%d, %d, %d, ', [ColorCode, Thickness, LineType]));
FSWrite(F, Format('%d, ', [MarkCenter]));
FSWrite(F, Format('%d, %d, %d', [CenterMarkerCode, NodeMarkerCode, NodeMarkerWidth]));
@@ -3675,7 +3440,6 @@ procedure WriteNewLine(F: TFileStream;
procedure ExportProfile(DSS: TDSSContext; FileNm: String; PhasesToPlot: Integer);
-
var
iEnergyMeter: Integer;
ActiveEnergyMeter: TEnergyMeterObj;
@@ -3687,15 +3451,13 @@ procedure ExportProfile(DSS: TDSSContext; FileNm: String; PhasesToPlot: Integer)
S: String;
F: TFileStream = nil;
Linetype: Integer;
-
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWrite(F, 'Name, Distance1, puV1, Distance2, puV2, Color, Thickness, Linetype, Markcenter, Centercode, NodeCode, NodeWidth,');
- {New graph created before this routine is entered}
+ // New graph created before this routine is entered
case phasesToPlot of
PROFILELL, PROFILELLALL, PROFILELLPRI:
S := 'L-L Voltage Profile';
@@ -3708,9 +3470,8 @@ procedure ExportProfile(DSS: TDSSContext; FileNm: String; PhasesToPlot: Integer)
iEnergyMeter := DSS.EnergyMeterClass.First;
while iEnergyMeter > 0 do
begin
-
ActiveEnergyMeter := DSS.EnergyMeterClass.GetActiveObj;
- {Go down each branch list and draw a line}
+ // Go down each branch list and draw a line
PresentCktElement := ActiveEnergyMeter.BranchList.First;
while PresentCktElement <> NIL do
begin
@@ -3719,10 +3480,10 @@ procedure ExportProfile(DSS: TDSSContext; FileNm: String; PhasesToPlot: Integer)
begin
Bus1 := Buses^[PresentCktElement.Terminals[0].BusRef];
Bus2 := Buses^[PresentCktElement.Terminals[1].BusRef];
- {Now determin which phase to plot}
+ // Now determin which phase to plot
if (Bus1.kVBase > 0.0) and (Bus2.kVBase > 0.0) then
case PhasesToPlot of
- {3ph only}
+ // 3ph only
PROFILE3PH:
if (PresentCktElement.NPhases >= 3) and (Bus1.kVBase > 1.0) then
for iphs := 1 to 3 do
@@ -3732,7 +3493,7 @@ procedure ExportProfile(DSS: TDSSContext; FileNm: String; PhasesToPlot: Integer)
WriteNewLine(F, PresentCktElement.Name, Bus1.DistFromMeter, puV1, Bus2.DistFromMeter, puV2,
iphs, 2, 0, 0, 0, NodeMarkerCode, NodeMarkerWidth);
end;
- {Plot all phases present (between 1 and 3)}
+ // Plot all phases present (between 1 and 3)
PROFILEALL:
begin
for iphs := 1 to 3 do
@@ -3748,7 +3509,7 @@ procedure ExportProfile(DSS: TDSSContext; FileNm: String; PhasesToPlot: Integer)
iphs, 2, Linetype, 0, 0, NodeMarkerCode, NodeMarkerWidth);
end;
end;
- {Plot all phases present (between 1 and 3) for Primary only}
+ // Plot all phases present (between 1 and 3) for Primary only
PROFILEALLPRI:
begin
if Bus1.kVBase > 1.0 then
@@ -3782,8 +3543,8 @@ procedure ExportProfile(DSS: TDSSContext; FileNm: String; PhasesToPlot: Integer)
Linetype := 0;
with Solution do
begin
- puV1 := CABS(CSUB(NodeV^[Bus1.GetRef(Bus1.FindIdx(iphs))], NodeV^[Bus1.GetRef(Bus1.FindIdx(iphs2))])) / Bus1.kVBase / 1732.0;
- puV2 := CABS(CSUB(NodeV^[Bus2.GetRef(Bus2.FindIdx(iphs))], NodeV^[Bus2.GetRef(Bus2.FindIdx(iphs2))])) / Bus2.kVBase / 1732.0;
+ puV1 := CABS(NodeV^[Bus1.GetRef(Bus1.FindIdx(iphs))] - NodeV^[Bus1.GetRef(Bus1.FindIdx(iphs2))]) / Bus1.kVBase / 1732.0;
+ puV2 := CABS(NodeV^[Bus2.GetRef(Bus2.FindIdx(iphs))] - NodeV^[Bus2.GetRef(Bus2.FindIdx(iphs2))]) / Bus2.kVBase / 1732.0;
end;
WriteNewLine(F, PresentCktElement.Name, Bus1.DistFromMeter, puV1, Bus2.DistFromMeter, puV2,
iphs, 2, Linetype, 0, 0, NodeMarkerCode, NodeMarkerWidth);
@@ -3806,8 +3567,8 @@ procedure ExportProfile(DSS: TDSSContext; FileNm: String; PhasesToPlot: Integer)
Linetype := 0;
with Solution do
begin
- puV1 := CABS(CSUB(NodeV^[Bus1.GetRef(Bus1.FindIdx(iphs))], NodeV^[Bus1.GetRef(Bus1.FindIdx(iphs2))])) / Bus1.kVBase / 1732.0;
- puV2 := CABS(CSUB(NodeV^[Bus2.GetRef(Bus2.FindIdx(iphs))], NodeV^[Bus2.GetRef(Bus2.FindIdx(iphs2))])) / Bus2.kVBase / 1732.0;
+ puV1 := CABS(NodeV^[Bus1.GetRef(Bus1.FindIdx(iphs))] - NodeV^[Bus1.GetRef(Bus1.FindIdx(iphs2))]) / Bus1.kVBase / 1732.0;
+ puV2 := CABS(NodeV^[Bus2.GetRef(Bus2.FindIdx(iphs))] - NodeV^[Bus2.GetRef(Bus2.FindIdx(iphs2))]) / Bus2.kVBase / 1732.0;
end;
WriteNewLine(F, PresentCktElement.Name, Bus1.DistFromMeter, puV1, Bus2.DistFromMeter, puV2,
iphs, 2, Linetype, 0, 0, NodeMarkerCode, NodeMarkerWidth);
@@ -3831,8 +3592,8 @@ procedure ExportProfile(DSS: TDSSContext; FileNm: String; PhasesToPlot: Integer)
Linetype := 0;
with Solution do
begin
- puV1 := CABS(CSUB(NodeV^[Bus1.GetRef(Bus1.FindIdx(iphs))], NodeV^[Bus1.GetRef(Bus1.FindIdx(iphs2))])) / Bus1.kVBase / 1732.0;
- puV2 := CABS(CSUB(NodeV^[Bus2.GetRef(Bus2.FindIdx(iphs))], NodeV^[Bus2.GetRef(Bus2.FindIdx(iphs2))])) / Bus2.kVBase / 1732.0;
+ puV1 := CABS(NodeV^[Bus1.GetRef(Bus1.FindIdx(iphs))] - NodeV^[Bus1.GetRef(Bus1.FindIdx(iphs2))]) / Bus1.kVBase / 1732.0;
+ puV2 := CABS(NodeV^[Bus2.GetRef(Bus2.FindIdx(iphs))] - NodeV^[Bus2.GetRef(Bus2.FindIdx(iphs2))]) / Bus2.kVBase / 1732.0;
end;
WriteNewLine(F, PresentCktElement.Name, Bus1.DistFromMeter, puV1, Bus2.DistFromMeter, puV2,
iphs, 2, Linetype, 0, 0, NodeMarkerCode, NodeMarkerWidth);
@@ -3867,11 +3628,8 @@ procedure ExportProfile(DSS: TDSSContext; FileNm: String; PhasesToPlot: Integer)
DSS.GlobalResult := FileNm;
finally
-
FreeAndNil(F);
-
end;
-
end;
procedure ExportEventLog(DSS: TDSSContext; FileNm: String);
@@ -3895,7 +3653,7 @@ procedure ExportIncMatrix(DSS: TDSSContext; FileNm: String);
begin
with DSS.ActiveCircuit.Solution do
begin
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Row,Col,Value');
for i := 0 to (IncMat.NZero - 1) do
begin
@@ -3913,7 +3671,7 @@ procedure ExportIncMatrixRows(DSS: TDSSContext; FileNm: String);
begin
with DSS.ActiveCircuit.Solution do
begin
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'B2N Incidence Matrix Row Names (PDElements)');
for i := 0 to (length(Inc_Mat_Rows) - 1) do
begin
@@ -3931,7 +3689,7 @@ procedure ExportIncMatrixCols(DSS: TDSSContext; FileNm: String);
begin
with DSS.ActiveCircuit.Solution do
begin
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'B2N Incidence Matrix Column Names (Buses)');
for i := 0 to (length(Inc_Mat_Cols) - 1) do
begin
@@ -3949,7 +3707,7 @@ procedure ExportBusLevels(DSS: TDSSContext; FileNm: String);
begin
with DSS.ActiveCircuit.Solution do
begin
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'B2N Incidence Matrix Column Names (Buses) and their level within the matrix');
FSWriteln(F, 'Bus Name,Bus Level');
for i := 0 to (length(Inc_Mat_Cols) - 1) do
@@ -3968,7 +3726,7 @@ procedure ExportLaplacian(DSS: TDSSContext; FileNm: String);
begin
with DSS.ActiveCircuit.Solution do
begin
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Row,Col,Value');
for i := 0 to (Laplacian.NZero - 1) do
begin
@@ -3979,16 +3737,17 @@ procedure ExportLaplacian(DSS: TDSSContext; FileNm: String);
end;
end;
//-------------------------------------------------------------------
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
procedure ExportZLL(DSS: TDSSContext; FileNm: String);
var
F: TFileStream = nil;
i: Integer;
begin
- if DSS.ADiakoptics then
+ if DSS.ActiveCircuit.Solution.ADiakoptics then
begin
with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
begin
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Row,Col,Value(Real), Value(Imag)');
for i := 0 to (ZLL.NZero - 1) do
begin
@@ -4005,11 +3764,11 @@ procedure ExportZCC(DSS: TDSSContext; FileNm: String);
F: TFileStream = nil;
i: Integer;
begin
- if DSS.ADiakoptics then
+ if DSS.ActiveCircuit.Solution.ADiakoptics then
begin
with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
begin
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Row,Col,Value(Real), Value(Imag)');
for i := 0 to (ZCC.NZero - 1) do
begin
@@ -4026,11 +3785,11 @@ procedure ExportY4(DSS: TDSSContext; FileNm: String);
F: TFileStream = nil;
i: Integer;
begin
- if DSS.ADiakoptics then
+ if DSS.ActiveCircuit.Solution.ADiakoptics then
begin
with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
begin
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Row,Col,Value(Real), Value(Imag)');
for i := 0 to (Y4.NZero - 1) do
begin
@@ -4047,11 +3806,11 @@ procedure ExportC(DSS: TDSSContext; FileNm: String);
F: TFileStream = nil;
i: Integer;
begin
- if DSS.ADiakoptics then
+ if DSS.ActiveCircuit.Solution.ADiakoptics then
begin
with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
begin
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Row,Col,Value');
for i := 0 to (Contours.NZero - 1) do
begin
@@ -4062,6 +3821,7 @@ procedure ExportC(DSS: TDSSContext; FileNm: String);
end;
end;
end;
+{$ENDIF}
//-------------------------------------------------------------------
procedure ExportVoltagesElements(DSS: TDSSContext; FileNm: String);
@@ -4075,7 +3835,6 @@ procedure ExportVoltagesElements(DSS: TDSSContext; FileNm: String);
pElem: TDSSCktElement;
begin
-
MaxNumTerminals := 2;
MaxNumNodes := 0;
pElem := DSS.ActiveCircuit.CktElements.First;
@@ -4085,19 +3844,18 @@ procedure ExportVoltagesElements(DSS: TDSSContext; FileNm: String);
MaxNumNodes := max(MaxNumNodes, pElem.NConds);
pElem := DSS.ActiveCircuit.CktElements.Next;
end;
-{
- MaxNumNodes := 0;
- With DSS.ActiveCircuit Do
- For j := 1 to NumBuses Do
- MaxNumNodes := max(MaxNumNodes, Buses^[j].NumNodesThisBus);
-}
+
+// MaxNumNodes := 0;
+// With DSS.ActiveCircuit Do
+// For j := 1 to NumBuses Do
+// MaxNumNodes := max(MaxNumNodes, Buses^[j].NumNodesThisBus);
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWrite(F, 'Element,NumTerminals');
- //Write out the header
+ //Write out the header
for i := 1 to MaxNumTerminals do
begin
FSWrite(F, Format(', Terminal%d', [i]));
@@ -4109,7 +3867,7 @@ procedure ExportVoltagesElements(DSS: TDSSContext; FileNm: String);
FSWriteln(F);
- //Go through all the sources
+ //Go through all the sources
with DSS.ActiveCircuit do
begin
pElem := sources.First;
@@ -4125,7 +3883,7 @@ procedure ExportVoltagesElements(DSS: TDSSContext; FileNm: String);
end;
- //Go through all the PDElements
+ //Go through all the PDElements
pElem := DSS.ActiveCircuit.PDElements.First;
while pElem <> NIL do
@@ -4139,7 +3897,7 @@ procedure ExportVoltagesElements(DSS: TDSSContext; FileNm: String);
end;
- //Go through all the PCElements
+ //Go through all the PCElements
pElem := DSS.ActiveCircuit.PCElements.First;
while pElem <> NIL do
@@ -4160,22 +3918,17 @@ procedure ExportVoltagesElements(DSS: TDSSContext; FileNm: String);
FreeAndNil(F);
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure ExportGICMvar(DSS: TDSSContext; FileNm: String);
-
var
F: TFileStream = nil;
pElem: TGICTransformerObj;
GICClass: TGICTransformer;
-
begin
-
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
GICClass := TGICTransformer(GetDSSClassPtr(DSS, 'GICTransformer'));
@@ -4192,7 +3945,6 @@ procedure ExportGICMvar(DSS: TDSSContext; FileNm: String);
finally
FreeAndNil(F);
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -4200,18 +3952,16 @@ procedure ExportBusReliability(DSS: TDSSContext; FileNm: String);
var
F: TFileStream = nil;
i: Integer;
-
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Bus, Lambda, Num-Interruptions, Num-Customers, Cust-Interruptions, Duration, Total-Miles');
with DSS.ActiveCircuit do
for i := 1 to NumBuses do
with Buses^[i] do
begin
FSWriteln(F, Format('%s, %-.11g, %-.11g, %d, %-.11g, %-.11g, %-.11g',
- [CheckForBlanks(Uppercase(BusList.NameOfIndex(i))), BusFltRate, Bus_Num_Interrupt, BusTotalNumCustomers, BusCustInterrupts, Bus_Int_Duration, BusTotalMiles]));
+ [CheckForBlanks(AnsiUpperCase(BusList.NameOfIndex(i))), BusFltRate, Bus_Num_Interrupt, BusTotalNumCustomers, BusCustInterrupts, Bus_Int_Duration, BusTotalMiles]));
end;
DSS.GlobalResult := FileNm;
@@ -4220,7 +3970,6 @@ procedure ExportBusReliability(DSS: TDSSContext; FileNm: String);
FreeAndNil(F);
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -4233,14 +3982,12 @@ procedure ExportBranchReliability(DSS: TDSSContext; FileNm: String);
MaxCustomers: Integer;
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Element, Lambda, "Accumulated-Lambda", Num-Customers, Total-Customers, Num-Interrupts, Cust-Interruptions, Cust-Durations, Total-Miles, Cust-Miles, SAIFI');
with DSS.ActiveCircuit do
begin
-
- // Find Maxcustomers of any PDElement for Duke Recloser siting algorithm
+ // Find Maxcustomers of any PDElement for Duke Recloser siting algorithm
MaxCustomers := 0;
pElem := DSS.ActiveCircuit.PDElements.First;
while pElem <> NIL do
@@ -4257,7 +4004,7 @@ procedure ExportBranchReliability(DSS: TDSSContext; FileNm: String);
end;
- // write report for PDELEMENTS only
+ // write report for PDELEMENTS only
pElem := DSS.ActiveCircuit.PDElements.First;
while pElem <> NIL do
begin
@@ -4280,14 +4027,11 @@ procedure ExportBranchReliability(DSS: TDSSContext; FileNm: String);
end;
end;
-
DSS.GlobalResult := FileNm;
finally
-
FreeAndNil(F);
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -4297,15 +4041,12 @@ procedure ExportNodeNames(DSS: TDSSContext; FileNm: String);
i: Integer;
j: Integer;
BusName: String;
-
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Node_Name');
with DSS.ActiveCircuit do
begin
-
for i := 1 to NumBuses do
begin
BusName := BusList.NameOfIndex(i);
@@ -4318,27 +4059,18 @@ procedure ExportNodeNames(DSS: TDSSContext; FileNm: String);
end;
-
DSS.GlobalResult := FileNm;
-
finally
-
FreeAndNil(F);
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
-
function TapPosition(const Transformer: TTransfObj; iWind: Integer): Integer;
-
-{Assumes 0 is 1.0 per unit tap}
-
+// Assumes 0 is 1.0 per unit tap
begin
with Transformer do
Result := Round((PresentTap[iWind] - (Maxtap[iWind] + Mintap[iWind]) / 2.0) / TapIncrement[iWind]);
-
end;
-
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure ExportTaps(DSS: TDSSContext; FileNm: String);
var
@@ -4346,10 +4078,8 @@ procedure ExportTaps(DSS: TDSSContext; FileNm: String);
iWind: Integer;
pReg: TRegControlObj;
begin
-
try
-
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Name, Tap, Min, Max, Step, Position');
with DSS.ActiveCircuit do
@@ -4367,47 +4097,33 @@ procedure ExportTaps(DSS: TDSSContext; FileNm: String);
end;
end;
-
DSS.GlobalResult := FileNm;
-
finally
-
FreeAndNil(F);
end;
-
end;
procedure ExportResult(DSS: TDSSContext; FileNm: String);
-
var
F: TFileStream = nil;
-
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
DSS.ParserVars.Lookup('@result');
FSWriteln(F, DSS.Parservars.Value);
-
DSS.GlobalResult := FileNm;
-
finally
-
FreeAndNil(F);
end;
-
-
end;
procedure ExportYNodeList(DSS: TDSSContext; FileNm: String);
var
i: Integer;
F: TFileStream = nil;
-
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
if DSS.ActiveCircuit <> NIL then
with DSS.ActiveCircuit do
@@ -4415,7 +4131,7 @@ procedure ExportYNodeList(DSS: TDSSContext; FileNm: String);
for i := 1 to NumNodes do
begin
with MapNodeToBus^[i] do
- FSWriteln(F, Format('"%s.%-d"', [Uppercase(BusList.NameOfIndex(Busref)), NodeNum]));
+ FSWriteln(F, Format('"%s.%-d"', [AnsiUpperCase(BusList.NameOfIndex(Busref)), NodeNum]));
end;
end;
@@ -4424,18 +4140,15 @@ procedure ExportYNodeList(DSS: TDSSContext; FileNm: String);
finally
FreeAndNil(F);
end;
-
end;
procedure ExportYVoltages(DSS: TDSSContext; FileNm: String);
var
i: Integer;
F: TFileStream = nil;
-
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
if DSS.ActiveCircuit <> NIL then
with DSS.ActiveCircuit do
@@ -4452,19 +4165,15 @@ procedure ExportYVoltages(DSS: TDSSContext; FileNm: String);
finally
FreeAndNil(F);
end;
-
-
end;
procedure ExportYCurrents(DSS: TDSSContext; FileNm: String);
var
i: Integer;
F: TFileStream = nil;
-
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
if DSS.ActiveCircuit <> NIL then
with DSS.ActiveCircuit do
@@ -4481,28 +4190,23 @@ procedure ExportYCurrents(DSS: TDSSContext; FileNm: String);
finally
FreeAndNil(F);
end;
-
end;
procedure ExportSections(DSS: TDSSContext; FileNM: String; pMeter: TEnergyMeterObj);
-
var
MyMeterPtr: TEnergyMeterObj;
iMeter, i: Integer;
F: TFileStream = nil;
-
-
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
- // Write Header
+ // Write Header
FSWriteln(F, 'Meter, SectionID, SeqIndex, DeviceType, NumCustomers, NumBranches, AvgRepairHrs, TotalDownlineCust, SectFaultRate, SumFltRatesXRepairHrs, SumBranchFltRates, HeadBranch ');
if Assigned(pMeter) then
- // If a meter is specified, export that meter only
+ // If a meter is specified, export that meter only
with pMeter do
begin
for i := 1 to SectionCount do
@@ -4511,12 +4215,11 @@ procedure ExportSections(DSS: TDSSContext; FileNM: String; pMeter: TEnergyMeterO
DSS.ActiveCircuit.ActiveCktElement := TDSSCktElement(sequenceList.Get(SeqIndex));
FSWriteln(F, Format('%s, %d, %d, %s, %d, %d, %-.6g, %d, %-.6g, %-.6g, %-.6g, %s',
[Name, i, SeqIndex, GetOCPDeviceTypeString(OCPDeviceType), NCustomers, NBranches, AverageRepairTime, TotalCustomers, SectFaultRate, SumFltRatesXRepairHrs, SumBranchFltRates,
- FullName(DSS.ActiveCircuit.ActiveCktElement)]));
+ EncloseQuotes(DSS.ActiveCircuit.ActiveCktElement.FullName)]));
end;
end
else // export sections for all meters
begin
-
iMeter := DSS.EnergyMeterClass.First;
while iMeter > 0 do
begin
@@ -4529,7 +4232,7 @@ procedure ExportSections(DSS: TDSSContext; FileNM: String; pMeter: TEnergyMeterO
DSS.ActiveCircuit.ActiveCktElement := TDSSCktElement(sequenceList.Get(SeqIndex));
FSWriteln(F, Format('%s, %d, %d, %s, %d, %d, %-.6g, %d, %-.6g, %-.6g, %-.6g, %s',
[Name, i, SeqIndex, GetOCPDeviceTypeString(OCPDeviceType), NCustomers, NBranches, AverageRepairTime, TotalCustomers, SectFaultRate, SumFltRatesXRepairHrs, SumBranchFltRates,
- FullName(DSS.ActiveCircuit.ActiveCktElement)]));
+ EncloseQuotes(DSS.ActiveCircuit.ActiveCktElement.FullName)]));
end;
end;
iMeter := DSS.EnergyMeterClass.Next;
@@ -4542,8 +4245,6 @@ procedure ExportSections(DSS: TDSSContext; FileNM: String; pMeter: TEnergyMeterO
finally
FreeAndNil(F);
end;
-
end;
-
end.
diff --git a/src/Common/KLUSolve.pas b/src/Common/KLUSolve.pas
index 9b8a87a4b..47c802f2b 100644
--- a/src/Common/KLUSolve.pas
+++ b/src/Common/KLUSolve.pas
@@ -1,6 +1,5 @@
unit KLUSolve;
-{$MODE Delphi}
{$MACRO ON}
{$IFDEF MSWINDOWS}
{$DEFINE KLUSOLVEX_CALL:=cdecl;external 'libklusolvex'}
@@ -11,7 +10,7 @@
interface
uses
- uComplex;
+ UComplex, DSSUcomplex;
{$IFDEF DSS_CAPI_MVMULT}
procedure mvmult(N: integer; b, A, x: pComplexArray);KLUSOLVEX_CALL;
diff --git a/src/Common/MeTIS_Exec.pas b/src/Common/MeTIS_Exec.pas
index 4fb8a60ed..a8f7edcf4 100644
--- a/src/Common/MeTIS_Exec.pas
+++ b/src/Common/MeTIS_Exec.pas
@@ -34,12 +34,8 @@ TFileSearchReplace = class(TObject)
implementation
uses
-{$IFNDEF FPC}
- System.IOUtils,
- System.StrUtils,
-{$ELSE}
+ BufStream,
StrUtils,
-{$ENDIF}
Math;
{ TFileSearchReplace }
@@ -48,8 +44,8 @@ constructor TFileSearchReplace.Create(const AFileName: string);
begin
inherited Create;
- FSourceFile := TFileStream.Create(AFileName, fmOpenReadWrite);
- FtmpFile := TFileStream.Create(ChangeFileExt(AFileName, '.tmp'), fmCreate);
+ FSourceFile := TBufferedFileStream.Create(AFileName, fmOpenReadWrite);
+ FtmpFile := TBufferedFileStream.Create(ChangeFileExt(AFileName, '.tmp'), fmCreate);
end;
destructor TFileSearchReplace.Destroy;
@@ -107,8 +103,8 @@ procedure TFileSearchReplace.Replace(const AFrom, ATo: string;
end
else
begin
- tmpStr := UpperCase(Str);
- tmpSubStr := UpperCase(SubStr);
+ tmpStr := AnsiUpperCase(Str);
+ tmpSubStr := AnsiUpperCase(SubStr);
i := Pos(tmpSubStr, tmpStr);
Result := i;
while i > 0 do
@@ -154,9 +150,9 @@ procedure TFileSearchReplace.Replace(const AFrom, ATo: string;
BufStr := FEncoding.GetString(Buf, 0, ReadedBufLen);
if rfIgnoreCase in ReplaceFlags then
- IsReplaced := {$IFDEF FPC}ANSIContainsText{$ELSE}ContainsText{$ENDIF}(BufStr, AFrom)
+ IsReplaced := ANSIContainsText(BufStr, AFrom)
else
- IsReplaced := {$IFDEF FPC}ANSIContainsStr{$ELSE}ContainsStr{$ENDIF}(BufStr, AFrom);
+ IsReplaced := ANSIContainsStr(BufStr, AFrom);
if IsReplaced then
begin
diff --git a/src/Common/ShowResults.pas b/src/Common/ShowResults.pas
index c50db0a5f..fb89ca602 100644
--- a/src/Common/ShowResults.pas
+++ b/src/Common/ShowResults.pas
@@ -6,9 +6,6 @@
All rights reserved.
----------------------------------------------------------
}
-{
- 5-30-00 Added code for handling positive sequence mode
-}
interface
@@ -48,7 +45,8 @@ implementation
uses
Classes,
- uComplex,
+ BufStream,
+ UComplex, DSSUcomplex,
Arraydef,
sysutils,
Circuit,
@@ -120,14 +118,11 @@ procedure WriteSeqVoltages(DSS: TDSSContext; F: TFileStream; i: Integer; LL: Boo
begin
-
with DSS.ActiveCircuit do
begin
-
if Buses^[i].NumNodesThisBus >= 3 then
begin
-
- // compute sequence voltages for Nodes 1, 2, and 3 only
+ // compute sequence voltages for Nodes 1, 2, and 3 only
with Buses^[i] do
for j := 1 to 3 do
@@ -140,7 +135,7 @@ procedure WriteSeqVoltages(DSS: TDSSContext; F: TFileStream; i: Integer; LL: Boo
k := j + 1;
if k > 3 then
k := 1;
- VLL[j] := Csub(Vph[j], Vph[k]);
+ VLL[j] := Vph[j] - Vph[k];
end;
Phase2SymComp(@VLL, @V012);
end
@@ -233,7 +228,7 @@ procedure WriteBusVoltages(DSS: TDSSContext; F: TFileStream; i: Integer; LL: Boo
if kk <= NumNodesThisBus then
begin
nref2 := Buses^[i].GetRef(kk); // reference for next phase in sequence
- VoltsLL := Csub(Volts, DSS.ActiveCircuit.Solution.NodeV^[nref2]);
+ VoltsLL := Volts - DSS.ActiveCircuit.Solution.NodeV^[nref2];
end;
end;
@@ -263,13 +258,13 @@ procedure WriteBusVoltages(DSS: TDSSContext; F: TFileStream; i: Integer; LL: Boo
begin
if kk > 0 then
begin
- FSWriteln(F, Format('%s %s %10.5g /_ %6.1f %9.5g %9.3f', [UpperCase(Bname), NodeNameLL, VmagLL, cdang(VoltsLL), VpuLL, kvbase * SQRT3]));
+ FSWriteln(F, Format('%s %s %10.5g /_ %6.1f %9.5g %9.3f', [AnsiUpperCase(Bname), NodeNameLL, VmagLL, cdang(VoltsLL), VpuLL, kvbase * SQRT3]));
Bname := Pad(' -', MaxBusNameLength);
end;
end
else
begin
- FSWrite(F, Format('%s %s %10.5g /_ %6.1f %9.5g %9.3f', [UpperCase(Bname), NodeName, Vmag, cdang(Volts), Vpu, kvbase * SQRT3]));
+ FSWrite(F, Format('%s %s %10.5g /_ %6.1f %9.5g %9.3f', [AnsiUpperCase(Bname), NodeName, Vmag, cdang(Volts), Vpu, kvbase * SQRT3]));
if (NumNodesThisBus > 1) and (kk > 0) and (jj <= 4) then
FSWrite(F, Format(' %s %10.5g /_ %6.1f %9.5g', [NodeNameLL, VmagLL, cdang(VoltsLL), VpuLL]));
FSWriteln(F);
@@ -293,7 +288,7 @@ procedure WriteElementVoltages(DSS: TDSSContext; F: TFileStream; pElem: TDSSCktE
Nterm := pElem.Nterms;
k := 0;
BusName := Pad(StripExtension(pElem.FirstBus), MaxBusNameLength);
- FSWriteln(F, 'ELEMENT = "' + pElem.dssclassname + '.' + UpperCase(pElem.Name) + '"');
+ FSWriteln(F, 'ELEMENT = "' + pElem.dssclassname + '.' + AnsiUpperCase(pElem.Name) + '"');
for j := 1 to NTerm do
begin
for i := 1 to NCond do
@@ -316,7 +311,7 @@ procedure WriteElementVoltages(DSS: TDSSContext; F: TFileStream; pElem: TDSSCktE
end;
if LL then
Vpu := Vpu / SQRT3;
- FSWriteln(F, Format('%s (%3d) %4d %13.5g (%8.4g) /_ %6.1f', [UpperCase(BusName), nref, MapNodeToBus^[nref].nodenum, Vmag, Vpu, cdang(Volts)]));
+ FSWriteln(F, Format('%s (%3d) %4d %13.5g (%8.4g) /_ %6.1f', [AnsiUpperCase(BusName), nref, MapNodeToBus^[nref].nodenum, Vmag, Vpu, cdang(Volts)]));
end;
end;
if j < Nterm then
@@ -341,7 +336,7 @@ procedure WriteElementDeltaVoltages(DSS: TDSSContext; F: TFileStream; pElem: TDS
begin
NCond := pElem.NConds;
- ElemName := Pad(pElem.dssclassname + '.' + UpperCase(pElem.Name), MaxDeviceNameLength);
+ ElemName := Pad(pElem.dssclassname + '.' + AnsiUpperCase(pElem.Name), MaxDeviceNameLength);
for i := 1 to NCond do
begin
Node1 := pElem.NodeRef^[i];
@@ -358,7 +353,7 @@ procedure WriteElementDeltaVoltages(DSS: TDSSContext; F: TFileStream; pElem: TDS
begin
Volts1 := DSS.ActiveCircuit.Solution.NodeV^[Node1]; // OK if Node1 or Node2 = 0
Volts2 := DSS.ActiveCircuit.Solution.NodeV^[Node2];
- Volts1 := Csub(Volts1, Volts2); // diff voltage
+ Volts1 := Volts1 - Volts2; // diff voltage
with DSS.ActiveCircuit do
begin
if Buses^[Bus1].kVBase <> Buses^[Bus2].kVBase then
@@ -388,11 +383,10 @@ procedure ShowVoltages(DSS: TDSSContext; FileNm: String; LL: Boolean; ShowOption
pElem: TDSSCktElement;
begin
-
try
SetMaxBusNameLength(DSS);
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
case ShowOptionCode of
0:
@@ -412,7 +406,6 @@ procedure ShowVoltages(DSS: TDSSContext; FileNm: String; LL: Boolean; ShowOption
1:
begin
-
FSWriteln(F);
if LL then
FSWriteln(F, 'LINE-LINE VOLTAGES BY BUS & NODE')
@@ -494,7 +487,6 @@ procedure ShowVoltages(DSS: TDSSContext; FileNm: String; LL: Boolean; ShowOption
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -565,8 +557,7 @@ procedure WriteSeqCurrents(F: TFileStream; const PaddedBrName: String; I0, I1, I
FSWriteln(F,
Format('%s %3d %10.5g %10.5g %8.2f %10.5g %8.2f %8.2f %8.2f',
- [UpperCase(Name), j, I1, I2, I2I1, I0, I0I1, Inormal, Iemerg]));
-
+ [AnsiUpperCase(Name), j, I1, I2, I2I1, I0, I0I1, Inormal, Iemerg]));
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -582,7 +573,6 @@ procedure WriteTerminalCurrents(DSS: TDSSContext; F: TFileStream; pElem: TDSSCkt
ResidPolar: Polar;
begin
-
cBuffer := NIL;
NCond := pElem.NConds;
@@ -593,7 +583,7 @@ procedure WriteTerminalCurrents(DSS: TDSSContext; F: TFileStream; pElem: TDSSCkt
pElem.GetCurrents(cBuffer);
k := 0;
FromBus := Pad(StripExtension(pElem.FirstBus), MaxBusNameLength);
- FSWriteln(F, 'ELEMENT = ', FullName(Pelem));
+ FSWriteln(F, 'ELEMENT = ', EncloseQuotes(Pelem.FullName));
for j := 1 to NTerm do
begin
Ctotal := CZERO;
@@ -606,13 +596,13 @@ procedure WriteTerminalCurrents(DSS: TDSSContext; F: TFileStream; pElem: TDSSCkt
begin
Inc(k);
if ShowResidual then
- Caccum(Ctotal, cBuffer^[k]);
- FSWriteln(F, Format('%s %4d %13.5g /_ %6.1f = %9.5g +j %9.5g', [UpperCase(FromBus), GetNodeNum(DSS, pElem.NodeRef^[k]), Cabs(cBuffer^[k]), cdang(cBuffer^[k]), cBuffer^[k].re, cBuffer^[k].im]));
+ Ctotal += cBuffer^[k];
+ FSWriteln(F, Format('%s %4d %13.5g /_ %6.1f = %9.5g +j %9.5g', [AnsiUpperCase(FromBus), GetNodeNum(DSS, pElem.NodeRef^[k]), Cabs(cBuffer^[k]), cdang(cBuffer^[k]), cBuffer^[k].re, cBuffer^[k].im]));
end;
if ShowResidual and (pElem.NPhases > 1) then
begin
- ResidPolar := CtoPolardeg(cnegate(Ctotal));
- FSWriteln(F, Format('%s Resid %13.5g /_ %6.1f = %9.5g +j %9.5g', [UpperCase(FromBus), ResidPolar.mag, ResidPolar.ang, -cTotal.re, -Ctotal.im]));
+ ResidPolar := CtoPolardeg(-Ctotal);
+ FSWriteln(F, Format('%s Resid %13.5g /_ %6.1f = %9.5g +j %9.5g', [AnsiUpperCase(FromBus), ResidPolar.mag, ResidPolar.ang, -cTotal.re, -Ctotal.im]));
end;
if j < Nterm then
FSWriteln(F, '------------');
@@ -628,7 +618,6 @@ procedure WriteTerminalCurrents(DSS: TDSSContext; F: TFileStream; pElem: TDSSCkt
Freemem(cBuffer);
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure ShowCurrents(DSS: TDSSContext; FileNm: String; ShowResidual: Boolean; ShowOptionCode: Integer);
@@ -645,13 +634,12 @@ procedure ShowCurrents(DSS: TDSSContext; FileNm: String; ShowResidual: Boolean;
Cmax: Double;
begin
-
SetMaxDeviceNameLength(DSS);
SetMaxBusNameLength(DSS);
try
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
case ShowOptionCode of
0:
@@ -679,7 +667,7 @@ procedure ShowCurrents(DSS: TDSSContext; FileNm: String; ShowResidual: Boolean;
begin
GetI0I1I2(I0, I1, I2, Cmax, pelem.Nphases, (j - 1) * Ncond, cBuffer);
with PElem do
- WriteSeqCurrents(F, Paddots(FullName(pElem), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, 0.0, 0.0, j, DSSObjType);
+ WriteSeqCurrents(F, Paddots(EncloseQuotes(pElem.FullName), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, 0.0, 0.0, j, DSSObjType);
end;
Freemem(cBuffer);
end;
@@ -703,7 +691,7 @@ procedure ShowCurrents(DSS: TDSSContext; FileNm: String; ShowResidual: Boolean;
begin
GetI0I1I2(I0, I1, I2, Cmax, pDelem.Nphases, (j - 1) * Ncond, cBuffer);
with PDElem do
- WriteSeqCurrents(F, Paddots(FullName(pdElem), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, Normamps, Emergamps, j, DSSObjType);
+ WriteSeqCurrents(F, Paddots(EncloseQuotes(pdElem.FullName), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, Normamps, Emergamps, j, DSSObjType);
end; {For}
Freemem(cBuffer);
end;
@@ -726,7 +714,7 @@ procedure ShowCurrents(DSS: TDSSContext; FileNm: String; ShowResidual: Boolean;
begin
GetI0I1I2(I0, I1, I2, Cmax, pCelem.Nphases, (j - 1) * Ncond, cBuffer);
with PCElem do
- WriteSeqCurrents(F, Paddots(FullName(pcElem), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, 0.0, 0.0, j, DSSObjType);
+ WriteSeqCurrents(F, Paddots(EncloseQuotes(pcElem.FullName), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, 0.0, 0.0, j, DSSObjType);
end;
Freemem(cBuffer);
end;
@@ -740,7 +728,6 @@ procedure ShowCurrents(DSS: TDSSContext; FileNm: String; ShowResidual: Boolean;
begin
if (pelem.Enabled) then
begin
-
NCond := pelem.NConds;
Nterm := pelem.Nterms;
Getmem(cBuffer, Sizeof(cBuffer^[1]) * NCond * Nterm);
@@ -750,7 +737,7 @@ procedure ShowCurrents(DSS: TDSSContext; FileNm: String; ShowResidual: Boolean;
begin
GetI0I1I2(I0, I1, I2, Cmax, pelem.Nphases, (j - 1) * Ncond, cBuffer);
with PElem do
- WriteSeqCurrents(F, Paddots(FullName(pElem), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, 0.0, 0.0, j, DSSObjType);
+ WriteSeqCurrents(F, Paddots(EncloseQuotes(pElem.FullName), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, 0.0, 0.0, j, DSSObjType);
end;
Freemem(cBuffer);
end;
@@ -838,9 +825,8 @@ procedure ShowCurrents(DSS: TDSSContext; FileNm: String; ShowResidual: Boolean;
except
On E: Exception do
- DoSimpleMsg(DSS, 'Exception raised in ShowCurrents: ' + E.Message, 2190);
+ DoSimpleMsg(DSS, 'Exception raised in ShowCurrents: %s', [E.Message], 2190);
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -867,13 +853,12 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
sout: String;
begin
-
c_Buffer := NIL;
SetMaxDeviceNameLength(DSS);
SetMaxBusNameLength(DSS);
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
{Allocate c_Buffer big enough for largest circuit element}
Getmem(c_buffer, sizeof(c_Buffer^[1]) * GetMaxCktElementSize(DSS));
@@ -906,7 +891,7 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
for j := 1 to NTerm do
begin
- FSWrite(F, Pad(FullName(p_Elem), MaxDeviceNameLength + 2) + Format('%3d', [j]));
+ FSWrite(F, Pad(EncloseQuotes(p_Elem.FullName), MaxDeviceNameLength + 2) + Format('%3d', [j]));
for i := 1 to min(3, p_Elem.Nphases) do
begin
k := (j - 1) * Ncond + i;
@@ -938,19 +923,19 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
end;
end;
- S := Cmul(V012[2], conjg(I012[2]));
+ S := V012[2] * cong(I012[2]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%11.1f', [S.re * 0.003]));
FSWrite(F, Format('%11.1f', [S.im * 0.003]));
- S := Cmul(V012[3], conjg(I012[3]));
+ S := V012[3] * cong(I012[3]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%11.1f', [S.re * 0.003]));
FSWrite(F, Format('%11.1f', [S.im * 0.003]));
- S := Cmul(V012[1], conjg(I012[1]));
+ S := V012[1] * cong(I012[1]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%8.1f', [S.re * 0.003]));
FSWrite(F, Format('%8.1f', [S.im * 0.003]));
FSWriteln(F);
@@ -974,7 +959,7 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
for j := 1 to NTerm do
begin
- FSWrite(F, Pad(FullName(pDElem), MaxDeviceNameLength + 2) + Format('%3d', [j]));
+ FSWrite(F, Pad(EncloseQuotes(pDElem.FullName), MaxDeviceNameLength + 2) + Format('%3d', [j]));
for i := 1 to Min(3, pdelem.Nphases) do
begin
k := (j - 1) * Ncond + i;
@@ -1006,19 +991,19 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
end;
end;
- S := Cmul(V012[2], conjg(I012[2]));
+ S := V012[2] * cong(I012[2]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%11.1f', [S.re * 0.003]));
FSWrite(F, Format('%11.1f', [S.im * 0.003]));
- S := Cmul(V012[3], conjg(I012[3]));
+ S := V012[3] * cong(I012[3]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%11.1f', [S.re * 0.003]));
FSWrite(F, Format('%11.1f', [S.im * 0.003]));
- S := Cmul(V012[1], conjg(I012[1]));
+ S := V012[1] * cong(I012[1]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%8.1f', [S.re * 0.003]));
FSWrite(F, Format('%8.1f', [S.im * 0.003]));
@@ -1027,12 +1012,12 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
//----PDelem.ActiveTerminalIdx := 1;
S := PDElem.ExcesskVANorm[1];
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%11.1f', [S.re]));
FSWrite(F, Format('%11.1f', [S.im]));
S := PDElem.ExcesskVAEmerg[1];
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%11.1f', [S.re]));
FSWrite(F, Format('%11.1f', [S.im]));
end;
@@ -1056,7 +1041,7 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
for j := 1 to NTerm do
begin
- FSWrite(F, Pad(FullName(pCElem), MaxDeviceNameLength + 2) + Format('%3d', [j]));
+ FSWrite(F, Pad(EncloseQuotes(pCElem.FullName), MaxDeviceNameLength + 2) + Format('%3d', [j]));
for i := 1 to min(3, pcElem.Nphases) do
begin
k := (j - 1) * Ncond + i;
@@ -1089,19 +1074,19 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
end;
end;
- S := Cmul(V012[2], conjg(I012[2]));
+ S := V012[2] * cong(I012[2]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%11.1f', [S.re * 0.003]));
FSWrite(F, Format('%11.1f', [S.im * 0.003]));
- S := Cmul(V012[3], conjg(I012[3]));
+ S := V012[3] * cong(I012[3]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%11.1f', [S.re * 0.003]));
FSWrite(F, Format('%11.1f', [S.im * 0.003]));
- S := Cmul(V012[1], conjg(I012[1]));
+ S := V012[1] * cong(I012[1]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%8.1f', [S.re * 0.003]));
FSWrite(F, Format('%8.1f', [S.im * 0.003]));
@@ -1116,7 +1101,6 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
1:
begin
-
{Branch Powers}
FSWriteln(F);
FSWriteln(F, 'CIRCUIT ELEMENT POWER FLOW');
@@ -1146,7 +1130,7 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
p_Elem.GetCurrents(c_Buffer);
k := 0;
FromBus := Pad(StripExtension(p_Elem.FirstBus), MaxBusNameLength);
- FSWriteln(F, 'ELEMENT = ', FullName(P_Elem));
+ FSWriteln(F, 'ELEMENT = ', EncloseQuotes(P_Elem.FullName));
for j := 1 to NTerm do
begin
Saccum := CZERO;
@@ -1155,14 +1139,14 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
Inc(k);
nref := p_Elem.NodeRef^[k];
Volts := DSS.ActiveCircuit.Solution.NodeV^[nref];
- S := Cmul(Volts, conjg(c_Buffer^[k]));
+ S := Volts * cong(c_Buffer^[k]);
if { (p_Elem.nphases=1) and } DSS.ActiveCircuit.PositiveSequence then
- S := CmulReal(S, 3.0);
+ S := S * 3;
if Opt = 1 then
- S := CmulReal(S, 0.001);
- Caccum(Saccum, S);
+ S := S * 0.001;
+ Saccum += S;
FSWrite(F, Format('%s %4d %8.1f +j %8.1f',
- [UpperCase(FromBus), GetNodeNum(DSS, p_Elem.NodeRef^[k]), S.re / 1000.0, S.im / 1000.0]
+ [AnsiUpperCase(FromBus), GetNodeNum(DSS, p_Elem.NodeRef^[k]), S.re / 1000.0, S.im / 1000.0]
));
FSWriteln(F, Format(' %8.1f %8.4f', [Cabs(S) / 1000.0, PowerFactor(S)]));
end;
@@ -1187,7 +1171,7 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
p_Elem.GetCurrents(c_Buffer);
k := 0;
FromBus := Pad(StripExtension(p_Elem.FirstBus), MaxBusNameLength);
- FSWriteln(F, 'ELEMENT = ', FullName(p_elem));
+ FSWriteln(F, 'ELEMENT = ', EncloseQuotes(p_elem.FullName));
if (CLASSMASK and P_Elem.DSSObjType) = AUTOTRANS_ELEMENT then
Ntimes := P_Elem.Nphases // Special case for AutoTrans
@@ -1202,15 +1186,15 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
nref1 := p_Elem.NodeRef^[k];
nref2 := p_Elem.NodeRef^[k + 1];
with DSS.ActiveCircuit.Solution do
- Volts := CSub(NodeV^[nref1], NodeV^[nref2]);
- S := Cmul(Volts, conjg(c_Buffer^[k]));
+ Volts := NodeV^[nref1] - NodeV^[nref2];
+ S := Volts * cong(c_Buffer^[k]);
if { (p_Elem.nphases=1) and } DSS.ActiveCircuit.PositiveSequence then
- S := CmulReal(S, 3.0);
+ S := S * 3;
if Opt = 1 then
- S := CmulReal(S, 0.001);
- Caccum(Saccum, S);
+ S := S * 0.001;
+ Saccum += S;
- WriteStr(sout, UpperCase(FromBus), ' ', GetNodeNum(DSS, p_Elem.NodeRef^[k]): 4, ' ', S.re / 1000.0: 8: 1, ' +j ', S.im / 1000.0: 8: 1);
+ WriteStr(sout, AnsiUpperCase(FromBus), ' ', GetNodeNum(DSS, p_Elem.NodeRef^[k]): 4, ' ', S.re / 1000.0: 8: 1, ' +j ', S.im / 1000.0: 8: 1);
FSWrite(F, sout);
WriteStr(sout, ' ', Cabs(S) / 1000.0: 8: 1, ' ', PowerFactor(S): 8: 4);
@@ -1233,13 +1217,13 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
Inc(k);
nref := p_Elem.NodeRef^[k];
Volts := DSS.ActiveCircuit.Solution.NodeV^[nref];
- S := Cmul(Volts, conjg(c_Buffer^[k]));
+ S := Volts * cong(c_Buffer^[k]);
if { (p_Elem.nphases=1) and } DSS.ActiveCircuit.PositiveSequence then
- S := CmulReal(S, 3.0);
+ S := S * 3;
if Opt = 1 then
- S := CmulReal(S, 0.001);
- Caccum(Saccum, S);
- WriteStr(sout, UpperCase(FromBus), ' ', GetNodeNum(DSS, p_Elem.NodeRef^[k]): 4, ' ', S.re / 1000.0: 8: 1, ' +j ', S.im / 1000.0: 8: 1);
+ S := S * 0.001;
+ Saccum += S;
+ WriteStr(sout, AnsiUpperCase(FromBus), ' ', GetNodeNum(DSS, p_Elem.NodeRef^[k]): 4, ' ', S.re / 1000.0: 8: 1, ' +j ', S.im / 1000.0: 8: 1);
FSWrite(F, sout);
WriteStr(sout, ' ', Cabs(S) / 1000.0: 8: 1, ' ', PowerFactor(S): 8: 4);
FSWriteln(F, sout);
@@ -1276,13 +1260,12 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
begin
if p_Elem.Enabled then
begin
-
NCond := p_Elem.NConds;
Nterm := p_Elem.Nterms;
p_Elem.GetCurrents(c_Buffer);
k := 0;
FromBus := Pad(StripExtension(p_Elem.FirstBus), MaxBusNameLength);
- FSWriteln(F, 'ELEMENT = ', Fullname(P_Elem));
+ FSWriteln(F, 'ELEMENT = ', EncloseQuotes(P_Elem.FullName));
for j := 1 to NTerm do
begin
Saccum := CZERO;
@@ -1291,13 +1274,13 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
Inc(k);
nref := p_Elem.NodeRef^[k];
Volts := DSS.ActiveCircuit.Solution.NodeV^[nref];
- S := Cmul(Volts, conjg(c_Buffer^[k]));
+ S := Volts * cong(c_Buffer^[k]);
if { (p_Elem.nphases=1) and } DSS.ActiveCircuit.PositiveSequence then
- S := CmulReal(S, 3.0);
+ S := S * 3;
if Opt = 1 then
- S := CmulReal(S, 0.001);
- Caccum(Saccum, S);
- WriteStr(sout, UpperCase(FromBus), ' ', GetNodeNum(DSS, p_Elem.NodeRef^[k]): 4, ' ', S.re / 1000.0: 6: 1, ' +j ', S.im / 1000.0: 6: 1);
+ S := S * 0.001;
+ Saccum += S;
+ WriteStr(sout, AnsiUpperCase(FromBus), ' ', GetNodeNum(DSS, p_Elem.NodeRef^[k]): 4, ' ', S.re / 1000.0: 6: 1, ' +j ', S.im / 1000.0: 6: 1);
FSWrite(F, sout);
WriteStr(sout, ' ', Cabs(S) / 1000.0: 8: 1, ' ', PowerFactor(S): 8: 4);
FSWriteln(F, sout);
@@ -1314,15 +1297,12 @@ procedure ShowPowers(DSS: TDSSContext; FileNm: String; opt, ShowOptionCode: Inte
end;
end; {ShowOptionCode=1}
-
- else
-
end; {CASE}
FSWriteln(F);
- S := CmulReal(DSS.ActiveCircuit.Losses, 0.001);
+ S := DSS.ActiveCircuit.Losses * 0.001;
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
WriteStr(sout, 'Total Circuit Losses = ', S.re: 6: 1, ' +j ', S.im: 6: 1);
FSWriteln(F, sout);
@@ -1369,7 +1349,6 @@ procedure WriteTerminalPowerSeq(DSS: TDSSContext; F: TFileStream; cktElem: TDSSC
c_Buffer: pComplexArray; // Allocate to max total conductors
begin
-
c_Buffer := NIL;
@@ -1379,7 +1358,7 @@ procedure WriteTerminalPowerSeq(DSS: TDSSContext; F: TFileStream; cktElem: TDSSC
NCond := cktElem.NConds;
cktElem.GetCurrents(c_Buffer);
- FSWrite(F, Pad(FullName(cktElem), MaxDeviceNameLength + 2) + IntToStr(j));
+ FSWrite(F, Pad(EncloseQuotes(cktElem.FullName), MaxDeviceNameLength + 2) + IntToStr(j));
for i := 1 to Min(cktElem.Nphases, 3) do
begin
k := (j - 1) * Ncond + i;
@@ -1416,25 +1395,25 @@ procedure WriteTerminalPowerSeq(DSS: TDSSContext; F: TFileStream; cktElem: TDSSC
// Pos Seq or Single Phase
case cktElem.Nphases of
1:
- S := Cmul(Vph[1], conjg(Iph[1]));
+ S := Vph[1] * cong(Iph[1]);
2:
- S := Cadd(Cmul(Vph[1], conjg(Iph[1])), Cmul(Vph[2], conjg(Iph[3])));
+ S := Vph[1] * cong(Iph[1]) + Vph[2] * cong(Iph[3]);
else
- S := Cmul(V012[2], conjg(I012[2]));
+ S := V012[2] * cong(I012[2]);
end;
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%11.1f', [S.re * 0.003]));
FSWrite(F, Format('%11.1f', [S.im * 0.003]));
- S := Cmul(V012[3], conjg(I012[3]));
+ S := V012[3] * cong(I012[3]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%11.1f', [S.re * 0.003]));
FSWrite(F, Format('%11.1f', [S.im * 0.003]));
- S := Cmul(V012[1], conjg(I012[1]));
+ S := V012[1] * cong(I012[1]);
if Opt = 1 then
- S := CmulReal(S, 0.001);
+ S := S * 0.001;
FSWrite(F, Format('%8.1f', [S.re * 0.003]));
FSWrite(F, Format('%8.1f', [S.im * 0.003]));
@@ -1444,7 +1423,6 @@ procedure WriteTerminalPowerSeq(DSS: TDSSContext; F: TFileStream; cktElem: TDSSC
if Assigned(C_buffer) then
Freemem(c_Buffer);
end;
-
end;
@@ -1459,7 +1437,6 @@ procedure WriteTerminalPower(DSS: TDSSContext; F: TFileStream; CktElem: TDSSCktE
FromBus: String;
begin
-
c_Buffer := NIL;
try
@@ -1469,7 +1446,7 @@ procedure WriteTerminalPower(DSS: TDSSContext; F: TFileStream; CktElem: TDSSCktE
NCond := CktElem.NConds;
CktElem.GetCurrents(c_Buffer);
FromBus := Pad(StripExtension(CktElem.GetBus(jTerm)), 12);
- FSWriteln(F, 'ELEMENT = ', Pad(FullName(cktElem), MaxDeviceNameLength + 2));
+ FSWriteln(F, 'ELEMENT = ', Pad(EncloseQuotes(cktElem.FullName), MaxDeviceNameLength + 2));
Saccum := CZERO;
for i := 1 to NCond do
@@ -1477,14 +1454,14 @@ procedure WriteTerminalPower(DSS: TDSSContext; F: TFileStream; CktElem: TDSSCktE
k := (jTerm - 1) * Ncond + i;
nref := CktElem.NodeRef^[k];
Volts := DSS.ActiveCircuit.Solution.NodeV^[nref];
- S := Cmul(Volts, conjg(c_Buffer^[k]));
+ S := Volts * cong(c_Buffer^[k]);
if { (CktElem.nphases=1) and } DSS.ActiveCircuit.PositiveSequence then
- S := CmulReal(S, 3.0);
+ S := S * 3;
if Opt = 1 then
- S := CmulReal(S, 0.001);
- Caccum(Saccum, S);
+ S := S * 0.001;
+ Saccum += S;
FSWriteln(F, Format('%s %4d %10.5g +j %10.5g %10.5g %8.4f',
- [UpperCase(FromBus), GetNodeNum(DSS, CktElem.NodeRef^[k]), S.re / 1000.0, S.im / 1000.0,
+ [AnsiUpperCase(FromBus), GetNodeNum(DSS, CktElem.NodeRef^[k]), S.re / 1000.0, S.im / 1000.0,
Cabs(S) / 1000.0, PowerFactor(S)]));
end;
FSWriteln(F, Format(' TERMINAL TOTAL %10.5g +j %10.5g %10.5g %8.4f',
@@ -1495,7 +1472,6 @@ procedure WriteTerminalPower(DSS: TDSSContext; F: TFileStream; CktElem: TDSSCktE
if Assigned(C_buffer) then
Freemem(c_Buffer);
end;
-
end;
@@ -1523,7 +1499,6 @@ procedure ShowBusPowers(DSS: TDSSContext; FileNm, BusName: String; opt, ShowOpti
begin
-
SetMaxDeviceNameLength(DSS);
c_Buffer := NIL;
@@ -1531,11 +1506,11 @@ procedure ShowBusPowers(DSS: TDSSContext; FileNm, BusName: String; opt, ShowOpti
BusReference := DSS.ActiveCircuit.BusList.Find(BusName);
if BusReference = 0 then
begin
- DoSimpleMsg(DSS, 'Bus "' + UpperCase(BusName) + '" not found.', 219);
+ DoSimpleMsg(DSS, 'Bus "%s" not found.', [AnsiUpperCase(BusName)], 219);
Exit;
end;
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
{Allocate c_Buffer big enough for largest circuit element}
Getmem(c_buffer, sizeof(c_Buffer^[1]) * GetMaxCktElementSize(DSS));
@@ -1543,7 +1518,6 @@ procedure ShowBusPowers(DSS: TDSSContext; FileNm, BusName: String; opt, ShowOpti
case ShowOptionCode of
0:
begin
-
{Write Bus Voltage}
FSWriteln(F);
@@ -1567,7 +1541,6 @@ procedure ShowBusPowers(DSS: TDSSContext; FileNm, BusName: String; opt, ShowOpti
if (p_Elem.Enabled) then
if CheckBusReference(p_Elem, BusReference, j) then
begin
-
NCond := p_elem.NConds;
Nterm := p_elem.Nterms;
p_elem.GetCurrents(c_Buffer);
@@ -1576,7 +1549,7 @@ procedure ShowBusPowers(DSS: TDSSContext; FileNm, BusName: String; opt, ShowOpti
begin
GetI0I1I2(I0, I1, I2, Cmax, p_elem.Nphases, (j - 1) * Ncond, c_Buffer);
with p_elem do
- WriteSeqCurrents(F, Paddots(FullName(p_Elem), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, 0.0, 0.0, j, DSSObjType);
+ WriteSeqCurrents(F, Paddots(EncloseQuotes(p_Elem.FullName), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, 0.0, 0.0, j, DSSObjType);
end;
end;
@@ -1599,7 +1572,7 @@ procedure ShowBusPowers(DSS: TDSSContext; FileNm, BusName: String; opt, ShowOpti
begin
GetI0I1I2(I0, I1, I2, Cmax, PDElem.Nphases, (j - 1) * Ncond, c_Buffer);
with PDElem do
- WriteSeqCurrents(F, Paddots(FullName(PDElem), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, 0.0, 0.0, j, DSSObjType);
+ WriteSeqCurrents(F, Paddots(EncloseQuotes(PDElem.FullName), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, 0.0, 0.0, j, DSSObjType);
end;
end;
PDElem := DSS.ActiveCircuit.PDElements.Next;
@@ -1620,7 +1593,7 @@ procedure ShowBusPowers(DSS: TDSSContext; FileNm, BusName: String; opt, ShowOpti
begin
GetI0I1I2(I0, I1, I2, Cmax, PCElem.Nphases, (j - 1) * Ncond, c_Buffer);
with PCElem do
- WriteSeqCurrents(F, Paddots(FullName(PCelem), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, 0.0, 0.0, j, DSSObjType);
+ WriteSeqCurrents(F, Paddots(EncloseQuotes(PCelem.FullName), MaxDeviceNameLength + 2), I0, I1, I2, Cmax, 0.0, 0.0, j, DSSObjType);
end;
end;
PCElem := DSS.ActiveCircuit.PCElements.Next;
@@ -1680,7 +1653,6 @@ procedure ShowBusPowers(DSS: TDSSContext; FileNm, BusName: String; opt, ShowOpti
1:
begin
-
{Write Bus Voltage}
FSWriteln(F);
@@ -1873,19 +1845,17 @@ procedure ShowFaultStudy(DSS: TDSSContext; FileNm: String);
sout: String;
begin
-
SetMaxBusNameLength(DSS);
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
{ Set source voltage injection currents }
with DSS.ActiveCircuit do
begin
with Solution do
begin
-
{All Phase Faults}
FSWriteln(F, 'FAULT STUDY REPORT');
FSWriteln(F);
@@ -1897,7 +1867,7 @@ procedure ShowFaultStudy(DSS: TDSSContext; FileNm: String);
{Bus Norton Equivalent Current, Isc has been previously computed}
with Buses^[iBus] do
begin
- WriteStr(sout, Pad(EncloseQuotes(UpperCase(BusList.NameOfIndex(iBus))) + ',', MaxBusNameLength + 2));
+ WriteStr(sout, Pad(EncloseQuotes(AnsiUpperCase(BusList.NameOfIndex(iBus))) + ',', MaxBusNameLength + 2));
FSWrite(F, sout);
for i := 1 to NumNodesThisBus do
begin
@@ -1911,7 +1881,7 @@ procedure ShowFaultStudy(DSS: TDSSContext; FileNm: String);
if Currmag > 0.0 then
begin
- WriteStr(sout, ', ', GetXR(Cdiv(VBus^[i], BusCurrent^[i])): 5: 1);
+ WriteStr(sout, ', ', GetXR(VBus^[i] / BusCurrent^[i]): 5: 1);
FSWrite(F, sout)
end
else
@@ -1940,13 +1910,13 @@ procedure ShowFaultStudy(DSS: TDSSContext; FileNm: String);
for iphs := 1 to NumNodesThisBus do
begin
- IFault := Cdiv(VBus[iphs], Zsc.GetElement(iphs, iphs));
+ IFault := VBus[iphs] / Zsc.GetElement(iphs, iphs);
- S := Format('%s %4u %12.0f ', [Pad(EncloseQuotes(UpperCase(BusList.NameOfIndex(iBus))), MaxBusNameLength + 2), GetNum(iphs), Cabs(Ifault)]);
+ S := Format('%s %4u %12.0f ', [Pad(EncloseQuotes(AnsiUpperCase(BusList.NameOfIndex(iBus))), MaxBusNameLength + 2), GetNum(iphs), Cabs(Ifault)]);
FSWrite(F, S, ' ');
for i := 1 to NumNodesThisBus do
begin
- Vphs := Cabs(Csub(VBus[i], Cmul(Zsc.GetElement(i, iphs), IFault)));
+ Vphs := Cabs(VBus[i] - (Zsc.GetElement(i, iphs) * IFault));
if kVbase > 0.0 then
begin
VPhs := 0.001 * Vphs / kVBase;
@@ -1975,9 +1945,9 @@ procedure ShowFaultStudy(DSS: TDSSContext; FileNm: String);
FSWriteln(F, 'Bus Node-Node Amps Node 1 Node 2 Node 3 ...');
FSWriteln(F);
- { Solve for Fault Injection Currents}
+ // Solve for Fault Injection Currents
for iBus := 1 to NumBuses do
- {Bus Norton Equivalent Current, Isc has been previously computed}
+ // Bus Norton Equivalent Current, Isc has been previously computed
with Buses^[iBus] do
begin
YFault := TcMatrix.CreateMatrix(NumNodesThisBus);
@@ -1990,13 +1960,13 @@ procedure ShowFaultStudy(DSS: TDSSContext; FileNm: String);
YFault.CopyFrom(Ysc);
YFault.AddElement(iphs, iphs, GFault);
YFault.AddElement(iphs + 1, iphs + 1, GFault);
- YFault.AddElemSym(iphs, iphs + 1, Cnegate(GFault));
+ YFault.AddElemSym(iphs, iphs + 1, -GFault);
- { Solve for Injection Currents}
+ // Solve for Injection Currents
YFault.Invert;
- YFault.MvMult(VFault, BusCurrent); {Gets voltage appearing at fault}
+ YFault.MvMult(VFault, BusCurrent); // Gets voltage appearing at fault
- WriteStr(sout, Pad(EncloseQuotes(UpperCase(BusList.NameOfIndex(iBus))), MaxBusNameLength + 2), GetNum(Iphs): 4, GetNum(Iphs + 1): 4, Cabs(Cmul(Csub(VFault^[iphs], VFault^[iphs + 1]), GFault)): 12: 0, ' ');
+ WriteStr(sout, Pad(EncloseQuotes(AnsiUpperCase(BusList.NameOfIndex(iBus))), MaxBusNameLength + 2), GetNum(Iphs): 4, GetNum(Iphs + 1): 4, Cabs((VFault^[iphs] - VFault^[iphs + 1]) * GFault): 12: 0, ' ');
FSWrite(F, sout);
for i := 1 to NumNodesThisBus do
begin
@@ -2015,16 +1985,16 @@ procedure ShowFaultStudy(DSS: TDSSContext; FileNm: String);
end;
FSWriteln(F);
- end; {For iphase}
- {Now, Stuff it in the Css Array where it belongs}
+ end; // For iphase
+ // Now, Stuff it in the Css Array where it belongs
Freemem(VFault);
YFault.Free;
- end; {With bus}
+ end; //With bus
- end; {With Solution}
- end; {With ActiveCircuit}
+ end; // With Solution
+ end; // With ActiveCircuit
finally
@@ -2041,10 +2011,10 @@ procedure WriteElementRecord(F: TFileStream; pElem: TDSSCktElement);
begin
Nterm := pElem.Nterms;
BusName := Pad(StripExtension(pElem.FirstBus), MaxBusNameLength);
- FSWrite(F, Pad(FullName(PElem), MaxDeviceNameLength + 2), ' ');
+ FSWrite(F, Pad(EncloseQuotes(PElem.FullName), MaxDeviceNameLength + 2), ' ');
for j := 1 to NTerm do
begin
- FSWrite(F, UpperCase(Busname), ' ');
+ FSWrite(F, AnsiUpperCase(Busname), ' ');
BusName := Pad(StripExtension(pElem.Nextbus), MaxBusNameLength);
end;
FSWriteln(F);
@@ -2068,18 +2038,18 @@ procedure ShowElements(DSS: TDSSContext; FileNm: String; ClassName: String);
try
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
except
On E: Exception do
- DoSimpleMsg(DSS, 'Error Trying to open element file "' + FileNm + '" file:' + E.message, 219000);
+ DoSimpleMsg(DSS, 'Error trying to open element file "%s" file: %s', [FileNm, E.message], 219000);
end;
try
DisabledFileNm := StripExtension(FileNm) + '_Disabled.txt';
- FDisabled := TFileStream.Create(DisabledFileNm, fmCreate);
+ FDisabled := TBufferedFileStream.Create(DisabledFileNm, fmCreate);
except
On E: Exception do
- DoSimpleMsg(DSS, 'Error Trying to open disabled element file "' + DisabledFilenm + '" file:' + E.message, 219000);
+ DoSimpleMsg(DSS, 'Error trying to open disabled element file "%s" file: %s', [DisabledFilenm, E.message], 219000);
end;
if Length(ClassName) > 0 then
@@ -2097,12 +2067,12 @@ procedure ShowElements(DSS: TDSSContext; FileNm: String; ClassName: String);
if (DSS.ActiveDSSClass.DSSClassType and BASECLASSMASK) > 0 then
begin
if TDSSCktElement(DSS.ActiveDSSObject).Enabled then
- FSWriteln(F, UpperCase(DSS.ActiveDssObject.Name))
+ FSWriteln(F, AnsiUpperCase(DSS.ActiveDssObject.Name))
else
- FSWriteln(Fdisabled, UpperCase(DSS.ActiveDssObject.Name));
+ FSWriteln(Fdisabled, AnsiUpperCase(DSS.ActiveDssObject.Name));
end
else
- FSWriteln(F, UpperCase(DSS.ActiveDssObject.Name)); // non cktelements
+ FSWriteln(F, AnsiUpperCase(DSS.ActiveDssObject.Name)); // non cktelements
end;
end;
end
@@ -2183,7 +2153,6 @@ procedure ShowElements(DSS: TDSSContext; FileNm: String; ClassName: String);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
@@ -2198,11 +2167,10 @@ procedure ShowBuses(DSS: TDSSContext; FileNm: String);
pBus: TDSSBus;
begin
-
try
SetMaxBusNameLength(DSS);
Inc(MaxBusNameLength, 2);
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F);
FSWriteln(F, 'BUSES AND NODES IN ACTIVE CIRCUIT: ' + DSS.ActiveCircuit.name);
@@ -2247,7 +2215,6 @@ procedure ShowBuses(DSS: TDSSContext; FileNm: String);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
@@ -2263,9 +2230,8 @@ procedure ShowMeters(DSS: TDSSContext; FileNm: String);
MeterClass: TEnergyMeter;
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F);
FSWriteln(F, 'ENERGY METER VALUES');
@@ -2319,7 +2285,6 @@ procedure ShowMeters(DSS: TDSSContext; FileNm: String);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -2334,9 +2299,8 @@ procedure ShowGenMeters(DSS: TDSSContext; FileNm: String);
GeneratorClass: TGenerator;
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F);
FSWriteln(F, 'GENERATOR ENERGY METER VALUES');
@@ -2372,7 +2336,6 @@ procedure ShowGenMeters(DSS: TDSSContext; FileNm: String);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
function TapPosition(const Transformer: TTransfObj; iWind: Integer): Integer;
@@ -2382,7 +2345,6 @@ function TapPosition(const Transformer: TTransfObj; iWind: Integer): Integer;
begin
with Transformer do
Result := Round((PresentTap[iWind] - (Maxtap[iWind] + Mintap[iWind]) / 2.0) / TapIncrement[iWind]);
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -2395,9 +2357,8 @@ procedure ShowRegulatorTaps(DSS: TDSSContext; FileNm: String);
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F);
FSWriteln(F, 'CONTROLLED TRANSFORMER TAP SETTINGS');
@@ -2424,7 +2385,6 @@ procedure ShowRegulatorTaps(DSS: TDSSContext; FileNm: String);
FireOffEditor(DSS, FileNm);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -2441,7 +2401,6 @@ procedure ShowMeterZone(DSS: TDSSContext; FileNm: String);
Param: String;
begin
-
try
FileNm := StripExtension(FileNm);
{ParamName :=} DSS.Parser.NextParam;
@@ -2449,7 +2408,7 @@ procedure ShowMeterZone(DSS: TDSSContext; FileNm: String);
FileNm := FileNm + '_' + Param + '.txt';
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
DSS.GlobalResult := FileNm;
@@ -2459,7 +2418,7 @@ procedure ShowMeterZone(DSS: TDSSContext; FileNm: String);
begin
pMtr := pMtrClass.Find(Param);
if pMtr = NIL then
- DoSimpleMsg(DSS, 'EnergyMeter "' + Param + '" not found.', 220)
+ DoSimpleMsg(DSS, 'EnergyMeter "%s" not found.', [Param], 220)
else
if pMtr.BranchList <> NIL then
begin
@@ -2477,10 +2436,10 @@ procedure ShowMeterZone(DSS: TDSSContext; FileNm: String);
if IsParallel then
FSWrite(F, '(PARALLEL:' + TDSSCktElement(LoopLineObj).Name + ')');
if IsLoopedHere then
- FSWrite(F, '(LOOP:' + TDSSCktElement(LoopLineObj).ParentClass.Name + '.' + TDSSCktElement(LoopLineObj).Name + ')');
+ FSWrite(F, '(LOOP:' + TDSSCktElement(LoopLineObj).FullName + ')');
end;
if Assigned(PDElem.SensorObj) then
- FSWrite(F, Format(' (Sensor: %s.%s) ', [PDElem.SensorObj.ParentClass.Name, PDElem.SensorObj.Name]))
+ FSWrite(F, Format(' (Sensor: %s) ', [PDElem.SensorObj.FullName]))
else
FSWrite(F, ' (Sensor: NIL)');
FSWriteln(F);
@@ -2491,7 +2450,7 @@ procedure ShowMeterZone(DSS: TDSSContext; FileNm: String);
FSWrite(F, TABCHAR);
FSWrite(F, LoadElem.ParentClass.Name, '.', LoadElem.Name);
if Assigned(LoadElem.SensorObj) then
- FSWrite(F, Format(' (Sensor: %s.%s) ', [LoadElem.SensorObj.ParentClass.Name, LoadElem.SensorObj.Name]))
+ FSWrite(F, Format(' (Sensor: %s) ', [LoadElem.SensorObj.FullName]))
else
FSWrite(F, ' (Sensor: NIL)');
FSWriteln(F);
@@ -2502,13 +2461,13 @@ procedure ShowMeterZone(DSS: TDSSContext; FileNm: String);
end;
end
else
- DoSimpleMsg(DSS, 'Meter Name Not Specified.' + CRLF + DSS.Parser.CmdString, 221);
+ DoSimpleMsg(DSS, 'Meter Name Not Specified. %s', [CRLF + DSS.Parser.CmdString], 221);
finally
FreeAndNil(F);
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Param := DSS.Parser.strvalue;
case length(Param) of
@@ -2520,7 +2479,6 @@ procedure ShowMeterZone(DSS: TDSSContext; FileNm: String);
DSS.ParserVars.Add('@lastshowfile', FileNm);
//
end;
-
end;
procedure ShowOverloads(DSS: TDSSContext; FileNm: String);
@@ -2535,13 +2493,12 @@ procedure ShowOverloads(DSS: TDSSContext; FileNm: String);
Cmag, Cmax: Double;
begin
-
c_Buffer := NIL;
SetMaxDeviceNameLength(DSS);
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
{Allocate c_Buffer big enough for largest circuit element}
Getmem(c_buffer, sizeof(c_Buffer^[1]) * GetMaxCktElementSize(DSS));
@@ -2596,7 +2553,7 @@ procedure ShowOverloads(DSS: TDSSContext; FileNm: String);
if (PdElem.Normamps > 0.0) or (PdElem.Emergamps > 0.0) then
if (CMax > PDElem.NormAmps) or (Cmax > pdelem.EmergAmps) then
begin
- FSWrite(F, Pad(FullName(PDelem), MaxDeviceNameLength + 2));
+ FSWrite(F, Pad(EncloseQuotes(PDelem.FullName), MaxDeviceNameLength + 2));
FSWrite(F, Format('%3d%8.1f', [j, I1]));
if PDElem.Normamps > 0.0 then
FSWrite(F, Format('%8.2f', [Cmax - PDElem.Normamps]))
@@ -2635,7 +2592,6 @@ procedure ShowOverloads(DSS: TDSSContext; FileNm: String);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
{ - -- - - - - ------------------------------}
@@ -2648,10 +2604,9 @@ procedure ShowUnserved(DSS: TDSSContext; FileNm: String; UE_Only: Boolean);
DoIt: Boolean;
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F);
FSWriteln(F, 'UNSERVED LOAD REPORT');
@@ -2696,7 +2651,6 @@ procedure ShowUnserved(DSS: TDSSContext; FileNm: String; UE_Only: Boolean);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
@@ -2716,12 +2670,11 @@ procedure ShowLosses(DSS: TDSSContext; FileNm: String);
sout: String;
begin
-
setMaxDeviceNameLength(DSS);
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
{Sequence Currents}
FSWriteln(F);
@@ -2746,18 +2699,18 @@ procedure ShowLosses(DSS: TDSSContext; FileNm: String);
then
begin
//----PDelem.ActiveTerminalIdx := 1; // activate 1st terminal for Power call
- kLosses := CmulReal(PDelem.Losses, 0.001); // kW Losses in element
- Caccum(TotalLosses, kLosses);
- TermPower := CmulReal(PDelem.power[1], 0.001); // Terminal 1 power
+ kLosses := PDelem.Losses * 0.001; // kW Losses in element
+ TotalLosses += kLosses;
+ TermPower := PDelem.power[1] * 0.001; // Terminal 1 power
if (CLASSMASK and PDElem.DSSObjType) = XFMR_ELEMENT then
- Caccum(TransLosses, kLosses);
+ TransLosses += kLosses;
if (CLASSMASK and PDElem.DSSObjType) = AUTOTRANS_ELEMENT then
- Caccum(TransLosses, kLosses);
+ TransLosses += kLosses;
if (CLASSMASK and PDElem.DSSObjType) = LINE_ELEMENT then
- Caccum(LineLosses, kLosses);
+ LineLosses += kLosses;
- FSWrite(F, Pad(FullName(PDelem), MaxDeviceNameLength + 2));
+ FSWrite(F, Pad(EncloseQuotes(PDelem.FullName), MaxDeviceNameLength + 2));
FSWrite(F, Format('%10.5f, ', [kLosses.re]));
if (TermPower.re <> 0.0) and (kLosses.re > 0.0009) then
FSWrite(F, Format('%8.2f', [(kLosses.re / Abs(TermPower.re) * 100.0)]))
@@ -2788,11 +2741,11 @@ procedure ShowLosses(DSS: TDSSContext; FileNm: String);
begin
if PcElem.Enabled then
begin
- Caccum(LoadPower, PCelem.Power[1]);
+ LoadPower += PCelem.Power[1];
end;
PCelem := DSS.ActiveCircuit.Loads.Next;
end;
- LoadPower := CmulReal(LoadPower, 0.001);
+ LoadPower := LoadPower * 0.001;
FSWriteln(F);
WriteStr(sout, Pad('TOTAL LOAD POWER = ', 30), Abs(LoadPower.re): 10: 1, ' kW');
@@ -2812,7 +2765,6 @@ procedure ShowLosses(DSS: TDSSContext; FileNm: String);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
procedure ShowVariables(DSS: TDSSContext; FileNm: String);
@@ -2824,10 +2776,9 @@ procedure ShowVariables(DSS: TDSSContext; FileNm: String);
i: Integer;
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
{Sequence Currents}
FSWriteln(F);
@@ -2860,15 +2811,11 @@ procedure ShowVariables(DSS: TDSSContext; FileNm: String);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
procedure ShowIsolated(DSS: TDSSContext; FileNm: String);
-
-{Show isolated buses/branches in present circuit}
-
+// Show isolated buses/branches in present circuit
var
-
Branch_List,
SubArea: TCktTree; // Pointers to all circuit elements
@@ -2878,21 +2825,19 @@ procedure ShowIsolated(DSS: TDSSContext; FileNm: String);
i, j: Integer;
sout: String;
begin
-
// Make sure bus list is built
if DSS.ActiveCircuit.BusNameRedefined then
DSS.ActiveCircuit.ReProcessBusDefs;
with DSS.ActiveCircuit do
begin
-
- {Initialize all Circuit Elements to not checked}
+ // Initialize all Circuit Elements to not checked
TestElement := CktElements.First;
while (TestElement <> NIL) do
begin
with TestElement do
begin
- Checked := FALSE;
+ Exclude(Flags, Flg.Checked);
for i := 1 to Nterms do
TerminalsChecked[i - 1] := FALSE;
end;
@@ -2909,10 +2854,9 @@ procedure ShowIsolated(DSS: TDSSContext; FileNm: String);
TestElement := DSS.ActiveCircuit.Sources.First;
Branch_List := GetIsolatedSubArea(DSS.ActiveCircuit, TestElement);
- {Show Report of Elements connected and not connected}
+ // Show Report of Elements connected and not connected
try
-
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F);
FSWriteln(F, 'ISOLATED CIRCUIT ELEMENT REPORT');
@@ -2928,7 +2872,6 @@ procedure ShowIsolated(DSS: TDSSContext; FileNm: String);
FSWriteln(F, EncloseQuotes(BusList.NameOfIndex(j)));
end;
-
FSWriteln(F);
FSWriteln(F, '*********** THE FOLLOWING SUB NETWORKS ARE ISOLATED ************');
FSWriteln(F);
@@ -2940,10 +2883,9 @@ procedure ShowIsolated(DSS: TDSSContext; FileNm: String);
while TestElement <> NIL do
begin
if TestElement.Enabled then
- if not TestElement.Checked then
+ if not (Flg.Checked in TestElement.Flags) then
if (TestElement.DSSObjType and BASECLASSMASK) = PD_ELEMENT then
begin
-
SubArea := GetIsolatedSubArea(DSS.ActiveCircuit, TestElement);
FSWriteln(F, '*** START SUBAREA ***');
TestBranch := SubArea.First;
@@ -2972,22 +2914,21 @@ procedure ShowIsolated(DSS: TDSSContext; FileNm: String);
with DSS.ActiveCircuit do
begin
-
- {Mark all controls, energy meters and monitors as checked so they don't show up}
+ // Mark all controls, energy meters and monitors as checked so they don't show up
for i := 1 to DSSControls.Count do
- TDSSCktElement(DSSControls.Get(i)).Checked := TRUE;
+ Include(TDSSCktElement(DSSControls.Get(i)).Flags, Flg.Checked);
for i := 1 to MeterElements.Count do
- TDSSCktElement(MeterElements.Get(i)).Checked := TRUE;
+ Include(TDSSCktElement(MeterElements.Get(i)).Flags, Flg.Checked);
TestElement := CktElements.First;
while TestElement <> NIL do
begin
if TestElement.Enabled then
- if not TestElement.Checked then
+ if not (Flg.Checked in TestElement.Flags) then
begin
- FSWrite(F, '"' + TestElement.ParentClass.Name + '.' + TestElement.Name + '"');
+ FSWrite(F, '"' + TestElement.FullName + '"');
FSWrite(F, ' Buses:');
for j := 1 to TestElement.nterms do
FSWrite(F, ' "', TestElement.GetBus(j), '"');
@@ -3036,7 +2977,6 @@ procedure ShowIsolated(DSS: TDSSContext; FileNm: String);
FireOffEditor(DSS, FileNm);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
procedure ShowRatings(DSS: TDSSContext; FileNm: String);
@@ -3048,7 +2988,7 @@ procedure ShowRatings(DSS: TDSSContext; FileNm: String);
begin
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Power Delivery Elements Normal and Emergency (max) Ratings');
FSWriteln(F);
@@ -3056,8 +2996,7 @@ procedure ShowRatings(DSS: TDSSContext; FileNm: String);
pdElem := DSS.ActiveCircuit.PDElements.First;
while pdElem <> NIL do
begin
-
- FSWrite(F, '"' + pdElem.ParentClass.Name + '.' + pdElem.Name + '", normamps=');
+ FSWrite(F, '"' + pdElem.FullName + '", normamps=');
FSWrite(F, Format('%-.4g, %-.4g !Amps', [pdElem.Normamps, pdElem.EmergAmps]));
FSWriteln(F);
@@ -3069,22 +3008,18 @@ procedure ShowRatings(DSS: TDSSContext; FileNm: String);
FireOffEditor(DSS, FileNm);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
procedure ShowLoops(DSS: TDSSContext; FileNm: String);
-{Show loops and paralleled branches in Meter zones}
-
+// Show loops and paralleled branches in Meter zones
var
F: TFileStream = nil;
pdElem: TPDElement;
hMeter: Integer;
pMtr: TEnergyMeterObj;
-
begin
try
-
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Loops and Paralleled Lines in all EnergyMeter Zones');
FSWriteln(F);
@@ -3093,22 +3028,19 @@ procedure ShowLoops(DSS: TDSSContext; FileNm: String);
while hMeter > 0 do
begin
-
pMtr := TEnergyMeterObj(DSS.ActiveDSSObject);
if pMtr.BranchList <> NIL then
begin
-
PDElem := pMtr.BranchList.First;
while PDElem <> NIL do
begin
-
with pMtr.BranchList.PresentBranch do
begin
if IsParallel then
- FSWriteln(F, ['(', pMtr.Name, ') ', PDElem.ParentClass.Name, '.', UpperCase(PDelem.Name), ': PARALLEL WITH ', TDSSCktElement(LoopLineObj).Parentclass.Name, '.', TDSSCktElement(LoopLineObj).Name]);
+ FSWriteln(F, ['(', pMtr.Name, ') ', PDElem.ParentClass.Name, '.', AnsiUpperCase(PDelem.Name), ': PARALLEL WITH ', TDSSCktElement(LoopLineObj).Parentclass.Name, '.', TDSSCktElement(LoopLineObj).Name]);
if IsLoopedHere then
- FSWriteln(F, ['(', pMtr.Name, ') ', PDElem.ParentClass.Name, '.', UpperCase(PDelem.Name), ': LOOPED TO ', TDSSCktElement(LoopLineObj).parentclass.Name, '.', TDSSCktElement(LoopLineObj).Name]);
+ FSWriteln(F, ['(', pMtr.Name, ') ', PDElem.ParentClass.Name, '.', AnsiUpperCase(PDelem.Name), ': LOOPED TO ', TDSSCktElement(LoopLineObj).parentclass.Name, '.', TDSSCktElement(LoopLineObj).Name]);
end;
PDElem := pMtr.BranchList.GoForward;
end;
@@ -3150,14 +3082,14 @@ procedure ShowTopology(DSS: TDSSContext; FileRoot: String);
nLoops, nParallel, nLevels, nIsolated, nSwitches: Integer;
begin
try
- FileNm := FileRoot + 'TopoSumm.Txt';
- TreeNm := FileRoot + 'TopoTree.Txt';
+ FileNm := FileRoot + 'TopoSumm.txt';
+ TreeNm := FileRoot + 'TopoTree.txt';
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'Topology analysis for switch control algorithms');
FSWriteln(F);
- Ftree := TFileStream.Create(TreeNm, fmCreate);
+ Ftree := TBufferedFileStream.Create(TreeNm, fmCreate);
FSWriteln(Ftree, 'Branches and Loads in Circuit ' + DSS.ActiveCircuit.Name);
FSWriteln(Ftree);
@@ -3182,29 +3114,27 @@ procedure ShowTopology(DSS: TDSSContext; FileRoot: String);
if IsParallel then
begin
Inc(nParallel);
- FSWrite(Ftree, '(PARALLEL:' + TDSSCktElement(LoopLineObj).Name + ')');
+ FSWrite(Ftree, '(PARALLEL:' + TDSSCktElement(LoopLineObj).FullName + ')');
end;
if IsLoopedHere then
begin
Inc(nLoops);
- FSWrite(Ftree, '(LOOP:' + TDSSCktElement(LoopLineObj).ParentClass.Name + '.' + TDSSCktElement(LoopLineObj).Name + ')');
+ FSWrite(Ftree, '(LOOP:' + TDSSCktElement(LoopLineObj).FullName + ')');
end;
- if PDElem.HasSensorObj then
- FSWrite(Ftree, Format(' (Sensor: %s.%s) ',
- [PDElem.SensorObj.ParentClass.Name, PDElem.SensorObj.Name]));
- if PDElem.HasControl then
+ if Flg.HasSensorObj in PDElem.Flags then
+ FSWrite(Ftree, Format(' (Sensor: %s) ', [PDElem.SensorObj.FullName]));
+ if Flg.HasControl in PDElem.Flags then
begin
pControlElem := PDElem.ControlElementList.First;
while pControlElem <> NIL do
begin // accommodate multiple controls on same branch
- FSWrite(Ftree, Format(' (Control: %s.%s) ',
- [pControlElem.ParentClass.Name, pControlElem.Name]));
+ FSWrite(Ftree, Format(' (Control: %s) ', [pControlElem.FullName]));
if ((pControlElem.DSSObjType and CLASSMASK) = SWT_CONTROL) then
Inc(nSwitches);
pControlElem := PDElem.ControlElementList.Next;
end;
end;
- if PDElem.HasEnergyMeter then
+ if Flg.HasEnergyMeter in PDElem.Flags then
FSWrite(Ftree, Format(' (Meter: %s) ', [PDElem.MeterObj.Name]));
end;
FSWriteln(Ftree);
@@ -3214,23 +3144,20 @@ procedure ShowTopology(DSS: TDSSContext; FileRoot: String);
begin
TopoLevelTabs(Ftree, topo.Level + 1);
FSWrite(Ftree, LoadElem.ParentClass.Name, '.', LoadElem.Name);
- if LoadElem.HasSensorObj then
- FSWrite(Ftree, Format(' (Sensor: %s.%s) ',
- [LoadElem.SensorObj.ParentClass.Name, LoadElem.SensorObj.Name]));
- if LoadElem.HasControl then
+ if Flg.HasSensorObj in LoadElem.Flags then
+ FSWrite(Ftree, Format(' (Sensor: %s) ',[LoadElem.SensorObj.FullName]));
+ if Flg.HasControl in LoadElem.Flags then
begin
-
pControlElem := LoadElem.ControlElementList.First;
while pControlElem <> NIL do
begin // accommodate multiple controls on same branch
- FSWrite(Ftree, Format(' (Control: %s.%s) ',
- [pControlElem.ParentClass.Name, pControlElem.Name]));
+ FSWrite(Ftree, Format(' (Control: %s) ', [pControlElem.FullName]));
if ((pControlElem.DSSObjType and CLASSMASK) = SWT_CONTROL) then
Inc(nSwitches);
pControlElem := LoadElem.ControlElementList.Next;
end;
end;
- if LoadElem.HasEnergyMeter then
+ if Flg.HasEnergyMeter in LoadElem.Flags then
FSWrite(Ftree, Format(' (Meter: %s) ', [LoadElem.MeterObj.Name]));
FSWriteln(Ftree);
LoadElem := topo.NextObject
@@ -3243,26 +3170,24 @@ procedure ShowTopology(DSS: TDSSContext; FileRoot: String);
pdElem := DSS.ActiveCircuit.PDElements.First;
while assigned(pdElem) do
begin
- if pdElem.IsIsolated then
+ if Flg.IsIsolated in pdElem.Flags then
begin
- FSWrite(Ftree, Format('Isolated: %s.%s', [PDElem.ParentClass.Name, PDElem.Name]));
- if PDElem.HasSensorObj then
- FSWrite(Ftree, Format(' (Sensor: %s.%s) ',
- [PDElem.SensorObj.ParentClass.Name, PDElem.SensorObj.Name]));
- if PDElem.HasControl then
+ FSWrite(Ftree, Format('Isolated: %s', [PDElem.FullName]));
+ if Flg.HasSensorObj in PDElem.Flags then
+ FSWrite(Ftree, Format(' (Sensor: %s) ', [PDElem.SensorObj.FullName]));
+ if Flg.HasControl in PDElem.Flags then
begin
pControlElem := PDElem.ControlElementList.First;
while pControlElem <> NIL do
begin // accommodate multiple controls on same branch
- FSWrite(Ftree, Format(' (Control: %s.%s) ',
- [pControlElem.ParentClass.Name, pControlElem.Name]));
+ FSWrite(Ftree, Format(' (Control: %s) ', [pControlElem.FullName]));
if ((pControlElem.DSSObjType and CLASSMASK) = SWT_CONTROL) then
Inc(nSwitches);
pControlElem := PDElem.ControlElementList.Next;
end;
end;
- if PDElem.HasEnergyMeter then
+ if Flg.HasEnergyMeter in PDElem.Flags then
FSWrite(Ftree, Format(' (Meter: %s) ', [PDElem.MeterObj.Name]));
FSWriteln(Ftree);
Inc(nIsolated);
@@ -3303,25 +3228,22 @@ procedure ShowLineConstants(DSS: TDSSContext; FileNm: String; Freq: Double; Unit
XCM: Double;
CCM: Double; // Common mode capacitance
LineCodesFileNm: String;
-
begin
-
try
-
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'LINE CONSTANTS');
FSWriteln(F, Format('Frequency = %.6g Hz, Earth resistivity = %.6g ohm-m', [Freq, Rho]));
- FSWriteln(F, 'Earth Model = ', GetEarthModel(DSS.DefaultEarthModel));
+ FSWriteln(F, 'Earth Model = ', DSS.EarthModelEnum.OrdinalToString(DSS.DefaultEarthModel));
FSWriteln(F);
// DSS C-API change: saves the file in the same folder as FileNm
- LineCodesFileNm := ExtractFilePath(FileNm) + 'LineConstantsCode.DSS';
- F2 := TFileStream.Create(LineCodesFileNm, fmCreate);
+ LineCodesFileNm := ExtractFilePath(FileNm) + 'LineConstantsCode.dss';
+ F2 := TBufferedFileStream.Create(LineCodesFileNm, fmCreate);
FSWriteln(F2, '!--- OpenDSS Linecodes file generated from Show LINECONSTANTS command');
FSWriteln(F2, Format('!--- Frequency = %.6g Hz, Earth resistivity = %.6g ohm-m', [Freq, Rho]));
- FSWriteln(F2, '!--- Earth Model = ', GetEarthModel(DSS.DefaultEarthModel));
+ FSWriteln(F2, '!--- Earth Model = ', DSS.EarthModelEnum.OrdinalToString(DSS.DefaultEarthModel));
Z := NIL;
YC := NIL;
@@ -3342,8 +3264,7 @@ procedure ShowLineConstants(DSS: TDSSContext; FileNm: String; Freq: Double; Unit
YC := pelem.YCmatrix[freq, 1.0, Units];
except
on E: Exception do
- DoSimpleMsg(DSS, 'Error computing line constants for LineGeometry.' + pelem.Name +
- '; Error message: ' + E.Message, 9934);
+ DoSimpleMsg(DSS, 'Error computing line constants for %s; Error message: %s', [pelem.FullName, E.Message], 9934);
end;
FSWriteln(F);
@@ -3406,7 +3327,7 @@ procedure ShowLineConstants(DSS: TDSSContext; FileNm: String; Freq: Double; Unit
FSWriteln(F);
end;
- {Write DSS LineCode record}
+ //Write DSS LineCode record
//Writeln(F);
//Writeln(F,'-------------------------------------------------------------------');
//Writeln(F,'-------------------DSS Linecode Definition-------------------------');
@@ -3463,13 +3384,13 @@ procedure ShowLineConstants(DSS: TDSSContext; FileNm: String; Freq: Double; Unit
FSWriteln(F, '-------------------------------------------------------------------');
FSWriteln(F);
for i := 1 to 3 do
- Caccum(Zs, Z.GetElement(i, i));
+ Zs += Z.GetElement(i, i);
for i := 1 to 3 do
for j := 1 to i - 1 do
- Caccum(Zm, Z.GetElement(i, j));
+ Zm += Z.GetElement(i, j);
- Z1 := CDivReal(Csub(Zs, Zm), 3.0);
- Z0 := CDivReal(Cadd(CMulReal(Zm, 2.0), Zs), 3.0);
+ Z1 := (Zs - Zm) / 3;
+ Z0 := (Zm * 2 + Zs) / 3;
w := freq * twopi / 1000.0;
FSWriteln(F);
FSWriteln(F, 'Z1, ohms per ', LineUnitsStr(Units), Format(' = %.6g + j %.6g (L1 = %.6g mH) ', [Z1.re, Z1.im, Z1.im / w]));
@@ -3481,7 +3402,7 @@ procedure ShowLineConstants(DSS: TDSSContext; FileNm: String; Freq: Double; Unit
YCM := CZERO;
for i := 1 to 3 do // Add up all elements of Z inverse
for j := 1 to 3 do
- Caccum(YCM, Z.GetElement(i, j));
+ YCM += Z.GetElement(i, j);
XCM := Cinv(YCM).im;
w := freq * twopi / 1.0E9;
@@ -3499,7 +3420,7 @@ procedure ShowLineConstants(DSS: TDSSContext; FileNm: String; Freq: Double; Unit
YCM := CZERO;
for i := 1 to 3 do // Add up all elements of Z inverse
for j := 1 to 3 do
- Caccum(YCM, YC.GetElement(i, j));
+ YCM += YC.GetElement(i, j);
CCM := YCM.im / w;
FSWriteln(F, 'C1, nF per ', LineUnitsStr(Units), Format(' = %.6g', [C1]));
@@ -3540,63 +3461,46 @@ procedure ShowYPrim(DSS: TDSSContext; FileNm: String);
i, j: Integer;
begin
+ if (DSS.ActiveCircuit = NIL) or (DSS.ActiveCircuit.ActiveCktElement = NIL) then
+ Exit;
+ with DSS.ActiveCircuit, ActiveCktElement do
+ try
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
+ FSWriteln(F, 'Yprim of active circuit element: ', FullName);
+ FSWriteln(F);
- if DSS.ActiveCircuit <> NIL then
- with DSS.ActiveCircuit do
+ cValues := GetYprimValues(ALL_YPRIM);
+ if cValues <> NIL then
begin
- if ActiveCktElement <> NIL then
- begin
- try
- F := TFileStream.Create(FileNm, fmCreate);
-
- with ActiveCktElement do
- begin
-
- FSWriteln(F, 'Yprim of active circuit element: ', ParentClass.Name + '.' + Name);
- FSWriteln(F);
-
- cValues := GetYprimValues(ALL_YPRIM);
- if cValues <> NIL then
- begin
-
- FSWriteln(F);
- FSWriteln(F, 'G matrix (conductance), S');
- FSWriteln(F);
-
- for i := 1 to Yorder do
- begin
- for j := 1 to i do
- FSWrite(F, Format('%13.10g ', [cValues^[i + (j - 1) * Yorder].re]));
- FSWriteln(F);
- end;
-
- FSWriteln(F);
- FSWriteln(F, 'jB matrix (Susceptance), S');
- FSWriteln(F);
-
- for i := 1 to Yorder do
- begin
- for j := 1 to i do
- FSWrite(F, Format('%13.10g ', [cValues^[i + (j - 1) * Yorder].im]));
- FSWriteln(F);
- end;
- end
- else
- FSWriteln(F, 'Yprim matrix is Nil');
-
- end;
-
- finally
+ FSWriteln(F);
+ FSWriteln(F, 'G matrix (conductance), S');
+ FSWriteln(F);
- FreeAndNil(F);
- FireOffEditor(DSS, FileNm);
- DSS.ParserVars.Add('@lastshowfile', FileNm);
- end;
+ for i := 1 to Yorder do
+ begin
+ for j := 1 to i do
+ FSWrite(F, Format('%13.10g ', [cValues^[i + (j - 1) * Yorder].re]));
+ FSWriteln(F);
+ end;
+ FSWriteln(F);
+ FSWriteln(F, 'jB matrix (Susceptance), S');
+ FSWriteln(F);
+ for i := 1 to Yorder do
+ begin
+ for j := 1 to i do
+ FSWrite(F, Format('%13.10g ', [cValues^[i + (j - 1) * Yorder].im]));
+ FSWriteln(F);
end;
- end;
-
+ end
+ else
+ FSWriteln(F, 'Yprim matrix is Nil');
+ finally
+ FreeAndNil(F);
+ FireOffEditor(DSS, FileNm);
+ DSS.ParserVars.Add('@lastshowfile', FileNm);
+ end;
end;
// shows how to retrieve the System Y in Triplet form
@@ -3612,13 +3516,12 @@ procedure ShowY(DSS: TDSSContext; FileNm: String);
cVals: array of Complex;
begin
-
if DSS.ActiveCircuit = NIL then
Exit;
hY := DSS.ActiveCircuit.Solution.hY;
if hY <= 0 then
begin
- DoSimpleMsg(DSS, 'Y Matrix not Built.', 222);
+ DoSimpleMsg(DSS, _('Y Matrix not Built.'), 222);
Exit;
end;
// print lower triangle of G and B using new functions
@@ -3633,7 +3536,7 @@ procedure ShowY(DSS: TDSSContext; FileNm: String);
SetLength(cVals, nNZ);
GetTripletMatrix(hY, nNZ, @RowIdx[0], @ColIdx[0], @cVals[0]);
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
FSWriteln(F, 'System Y Matrix (Lower Triangle by Columns)');
FSWriteln(F);
@@ -3658,7 +3561,6 @@ procedure ShowY(DSS: TDSSContext; FileNm: String);
FireOffEditor(DSS, FileNm);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
procedure ShowNodeCurrentSum(DSS: TDSSContext; FileNm: String);
@@ -3682,7 +3584,7 @@ procedure ShowNodeCurrentSum(DSS: TDSSContext; FileNm: String);
begin
MaxNodeCurrent := NIL;
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
with DSS.ActiveCircuit, DSS.ActiveCircuit.solution do
begin
@@ -3711,7 +3613,7 @@ procedure ShowNodeCurrentSum(DSS: TDSSContext; FileNm: String);
Inc(k);
Ctemp := Iterminal^[k];
nRef := NodeRef^[k];
- Caccum(Currents^[nRef], Ctemp); // Noderef=0 is OK
+ Currents^[nRef] += Ctemp; // Noderef=0 is OK
if Cabs(Ctemp) > MaxNodeCurrent^[nRef] then
MaxNodeCurrent^[nRef] := Cabs(Ctemp);
end;
@@ -3724,7 +3626,7 @@ procedure ShowNodeCurrentSum(DSS: TDSSContext; FileNm: String);
begin
Ctemp := Iterminal^[i];
nRef := NodeRef^[i];
- Caccum(Currents^[nRef], Ctemp); // Noderef=0 is OK
+ Currents^[nRef] += Ctemp; // Noderef=0 is OK
if Cabs(Ctemp) > MaxNodeCurrent^[nRef] then
MaxNodeCurrent^[nRef] := Cabs(Ctemp);
end;
@@ -3794,9 +3696,8 @@ procedure ShowkVBaseMismatch(DSS: TDSSContext; FileNm: String);
BusName: String;
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
{Check Loads}
if DSS.ActiveCircuit.Loads.Count > 0 then
@@ -3819,9 +3720,9 @@ procedure ShowkVBaseMismatch(DSS: TDSSContext; FileNm: String);
begin
if abs(pLoad.kVLoadBase - pBus.kVBase) > 0.10 * pBus.kVBase then
begin
- FSWriteln(F, Format('!!!!! Voltage Base Mismatch, Load.%s.kV=%.6g, Bus %s LN kvBase = %.6g', [pLoad.Name, pLoad.kVLoadBase, pLoad.GetBus(1), pBus.kVBase]));
+ FSWriteln(F, Format('!!!!! Voltage Base Mismatch, %s.kV=%.6g, Bus %s LN kvBase = %.6g', [pLoad.FullName, pLoad.kVLoadBase, pLoad.GetBus(1), pBus.kVBase]));
FSWriteln(F, Format('!setkvbase %s kVLN=%.6g', [Busname, pLoad.kVLoadBase]));
- FSWriteln(F, Format('!Load.%s.kV=%.6g', [pLoad.Name, pBus.kVBase]));
+ FSWriteln(F, Format('!%s.kV=%.6g', [pLoad.FullName, pBus.kVBase]));
end;
end
else
@@ -3829,9 +3730,9 @@ procedure ShowkVBaseMismatch(DSS: TDSSContext; FileNm: String);
BuskV := pBus.kVBase * SQRT3;
if abs(pLoad.kVLoadBase - BuskV) > 0.10 * BuskV then
begin
- FSWriteln(F, Format('!!!!! Voltage Base Mismatch, Load.%s.kV=%.6g, Bus %s kvBase = %.6g', [pLoad.Name, pLoad.kVLoadBase, pLoad.GetBus(1), BuskV]));
+ FSWriteln(F, Format('!!!!! Voltage Base Mismatch, %s.kV=%.6g, Bus %s kvBase = %.6g', [pLoad.FullName, pLoad.kVLoadBase, pLoad.GetBus(1), BuskV]));
FSWriteln(F, Format('!setkvbase %s kVLL=%.6g', [Busname, pLoad.kVLoadBase]));
- FSWriteln(F, Format('!Load.%s.kV=%.6g', [pLoad.Name, BuskV]));
+ FSWriteln(F, Format('!%s.kV=%.6g', [pLoad.FullName, BuskV]));
end;
end;
end;
@@ -3861,9 +3762,9 @@ procedure ShowkVBaseMismatch(DSS: TDSSContext; FileNm: String);
begin
if abs(pGen.Genvars.kVGeneratorBase - pBus.kVBase) > 0.10 * pBus.kVBase then
begin
- FSWriteln(F, Format('!!! Voltage Base Mismatch, Generator.%s.kV=%.6g, Bus %s LN kvBase = %.6g', [pGen.Name, pGen.Genvars.kVGeneratorBase, pGen.GetBus(1), pBus.kVBase]));
+ FSWriteln(F, Format('!!! Voltage Base Mismatch, %s.kV=%.6g, Bus %s LN kvBase = %.6g', [pGen.FullName, pGen.Genvars.kVGeneratorBase, pGen.GetBus(1), pBus.kVBase]));
FSWriteln(F, Format('!setkvbase %s kVLN=%.6g', [Busname, pGen.Genvars.kVGeneratorBase]));
- FSWriteln(F, Format('!Generator.%s.kV=%.6g', [pGen.Name, pBus.kVBase]));
+ FSWriteln(F, Format('!%s.kV=%.6g', [pGen.FullName, pBus.kVBase]));
end;
end
else
@@ -3871,9 +3772,9 @@ procedure ShowkVBaseMismatch(DSS: TDSSContext; FileNm: String);
BuskV := pBus.kVBase * SQRT3;
if abs(pGen.Genvars.kVGeneratorBase - BuskV) > 0.10 * BuskV then
begin
- FSWriteln(F, Format('!!! Voltage Base Mismatch, Generator.%s.kV=%.6g, Bus %s kvBase = %.6g', [pGen.Name, pGen.Genvars.kVGeneratorBase, pGen.GetBus(1), BuskV]));
+ FSWriteln(F, Format('!!! Voltage Base Mismatch, %s.kV=%.6g, Bus %s kvBase = %.6g', [pGen.FullName, pGen.Genvars.kVGeneratorBase, pGen.GetBus(1), BuskV]));
FSWriteln(F, Format('!setkvbase %s kVLL=%.6g', [Busname, pGen.Genvars.kVGeneratorBase]));
- FSWriteln(F, Format('!Generator.%s.kV=%.6g', [pGen.Name, BuskV]));
+ FSWriteln(F, Format('!%s.kV=%.6g', [pGen.FullName, BuskV]));
end;
end;
end;
@@ -3896,9 +3797,8 @@ procedure ShowDeltaV(DSS: TDSSContext; FileNm: String);
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
SetMaxDeviceNameLength(DSS);
@@ -3970,34 +3870,30 @@ procedure ShowDeltaV(DSS: TDSSContext; FileNm: String);
FireOffEditor(DSS, FileNm);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
procedure ShowControlledElements(DSS: TDSSContext; FileNm: String);
-
var
F: TFileStream = nil;
pdelem: TPDElement;
pctrlelem: TDSSCktElement;
i: Integer;
-
begin
try
-
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
pdelem := DSS.ActiveCircuit.PDElements.First;
while pdelem <> NIL do
begin
- if pdelem.HasControl then
+ if Flg.HasControl in pdelem.Flags then
begin
with pdelem do
- FSWrite(F, Format('%s.%s', [ParentClass.Name, Name]));
+ FSWrite(F, FullName);
for i := 1 to pdelem.ControlElementList.Count do
begin
pctrlelem := pdelem.ControlElementList.Get(i);
with pctrlelem do
- FSWrite(F, Format(', %s.%s ', [ParentClass.Name, Name]));
+ FSWrite(F, Format(', %s ', [FullName]));
end;
FSWriteln(F);
end;
@@ -4005,24 +3901,18 @@ procedure ShowControlledElements(DSS: TDSSContext; FileNm: String);
end;
finally
-
FreeAndNil(F);
FireOffEditor(DSS, FileNm);
DSS.ParserVars.Add('@lastshowfile', FileNm);
-
end;
-
end;
procedure ShowResult(DSS: TDSSContext; FileNm: String);
-
var
F: TFileStream = nil;
-
begin
-
try
- F := TFileStream.Create(FileNm, fmCreate);
+ F := TBufferedFileStream.Create(FileNm, fmCreate);
DSS.ParserVars.Lookup('@result');
FSWriteln(F, DSS.Parservars.Value);
@@ -4035,8 +3925,6 @@ procedure ShowResult(DSS: TDSSContext; FileNm: String);
FireOffEditor(DSS, FileNm);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
-
end;
procedure ShowEventLog(DSS: TDSSContext; FileNm: String);
@@ -4052,7 +3940,6 @@ procedure ShowEventLog(DSS: TDSSContext; FileNm: String);
FireOffEditor(DSS, FileNm);
DSS.ParserVars.Add('@lastshowfile', FileNm);
end;
-
end;
diff --git a/src/Common/Solution.pas b/src/Common/Solution.pas
index 569454bb3..5c80193a1 100644
--- a/src/Common/Solution.pas
+++ b/src/Common/Solution.pas
@@ -2,48 +2,16 @@
{
----------------------------------------------------------
- Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
+ Copyright (c) 2008-2021, Electric Power Research Institute, Inc.
All rights reserved.
----------------------------------------------------------
}
-{ Change Log
- 8-14-99 Added progress display and abort on longer solution types
- 11-3-99 added calc voltage base
- 11-21-99 modified to calc the voltage bases at the current load level set by the user.
- 12-1-99 Added code to estimate starting point for P-V Generators
- 12-2-99 Made more properties visible
- 12-6-99 Merged properties with Set Command and removed from here
- 12-15-99 Added global generatordispatchreference
- 1-8-00 Fixed bug in autoadd generators to work with new generator model
- set vminpu=0 and vmaxpu=1000000
- 1-30-00 to 2-1-00 Implemented control action check in solution
- 2-19-00 Frequency changed reset to FALSE after being used (was causing all YPrims to be recomputed)
- 2-23-00 Modified so that reset of meters and monitors is done upon setting the solution mode property.
- After that the user must reset else the monitors just accumulate.
- 3-20-00 Fixed bug with setting generator disp reference - made uniform for all types
- 6-11-00 Split into two modules + moved auto add stuff to AutoAdd
- 9-20-00 Added Dynamic Mode
- 10-25-00 Added Fundamental Freq and other stuff for Harmonics Solution
- 5-30-01 Added control iterations check, mostIterationsdone.
- Fixed bug with controls off doing the solution too many times.
-
- 8-14-01 Reset IntervalHrs on Mode change
- 7-11-02 Added check for system Y change after computing currents
-
- 9-28-03 Redefined V to NodeV and changed from an array from 1..n to 0..n where
- 0-th element is alway ground(complex zero volts).
- 8-14-06 Revised power flow initialization; removed forward/backward sweep
-
- 9-14-16 Added SampleTheMeters Flag to allow sampling energy meters in Time and DutyCycle mode
-
-}
-
interface
uses
Classes,
- uCOMPLEX,
+ UComplex, DSSUcomplex,
Arraydef,
Command,
Monitor,
@@ -53,10 +21,9 @@ interface
EnergyMeter,
Sparse_Math,
VSource,
+ ISource,
SysUtils,
-{$IFDEF DSS_CAPI_PM}
- Parallel_Lib,
-{$ENDIF}
+ generics.collections,
{$IFDEF MSWINDOWS}
Windows,
{$ELSE}
@@ -64,25 +31,44 @@ interface
Unix,
{$ENDIF}
Strings,
+{$IFDEF DSS_CAPI_PM}
SyncObjs,
ExecHelper,
- CktElement;
+{$ENDIF}
+ CktElement,
+ DSSPointerList;
const
-
NORMALSOLVE = 0;
NEWTONSOLVE = 1;
-// Constants for the actor's messaging
- SIMULATE = 0;
- EXIT_ACTOR = 1;
- ALL_ACTORS = 0; // Wait flag for all the actors
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
AD_ACTORS = 1; // Wait flag to wait only for the A-Diakoptics actors
-
-
+{$ENDIF}
+ ALL_ACTORS = 0; // Wait flag for all the actors
type
+{$SCOPEDENUMS ON}
+ TActorMessage = (
+ // Constants for the actor's messaging
+ SIMULATE = 0,
+ EXIT_ACTOR,
+ CALC_INJ_CURR, // Uses the total solution to estiamte the injection currents
+ DO_CTRL_ACTIONS, // Does the control actions distributedly
+ ZEROIVECTOR, // Zeroes the actor's I vector
+ GETCURRINJ, // Gets the current injections for the actor and uploades them in the local I vector
+ CHECKYBUS, // Rebuilds the YBus if needed at local level
+ CHECK_FAULT // Checks the fault status at local level
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ ,
+ INIT_ADIAKOPTICS, // Initializes the environment for the children actors
+ SOLVE_AD1, // solves for the actors with Power Injections -> E(0)
+ SOLVE_AD2, // Solves the sub-system and adds to the partial solution
+ GETCTRLMODE // Sync the local control mode with actor 1
+{$ENDIF}
+ );
+{$SCOPEDENUMS OFF}
{$IFDEF DSS_CAPI_INCREMENTAL_Y}
{$SCOPEDENUMS ON}
TSolverOptions = (
@@ -93,7 +79,7 @@ interface
ReuseSymbolicFactorization = 2, // Reuse the symbolic factorization, implies ReuseCompressedMatrix
ReuseNumericFactorization = 3, // Reuse the numeric factorization, implies ReuseSymbolicFactorization
- AlwaysResetYPrimInvalid = $100000000 // Bit flag, see CktElement.pas
+ AlwaysResetYPrimInvalid = $10000000 // Bit flag, see CktElement.pas
);
{$SCOPEDENUMS OFF}
{$ENDIF}
@@ -104,68 +90,43 @@ ESolveError = class(Exception); // Raised when solution aborted
TNodeVarray = array[0..1000] of Complex;
pNodeVarray = ^TNodeVarray;
-
- TDSSSolution = class(TDSSClass)
-
-
- PRIVATE
-// CommandList:TCommandlist;
- PROTECTED
- procedure DefineProperties;
- PUBLIC
- constructor Create(dssContext: TDSSContext);
- destructor Destroy; OVERRIDE;
-
- function Edit: Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
- end;
-
{$IFDEF DSS_CAPI_PM}
- TInfoMessageCall = procedure(const info: String) of object; // Creates the procedure for sending a message
-
- TSolver = class(TThread)
- constructor Create(dssContext: TDSSContext; Susp: Boolean; local_CPU: Integer; CallBack: TInfoMessageCall; AEvent: TEvent); OVERLOAD;
+ TSolver = class(TThread)
+ constructor Create(dssContext: TDSSContext; Susp: Boolean; local_CPU: Integer; AEvent: TEvent); OVERLOAD;
procedure Execute; OVERRIDE;
procedure Doterminate; OVERRIDE;
destructor Destroy; OVERRIDE;
-//*******************************Private components*****************************
PROTECTED
DSS: TDSSContext;
- FMessage,
- Msg_Cmd: String;
+ FMessage: String;
UINotifier,
- FInfoProc: TInfoMessageCall;
- MsgType: Integer;
- UIEvent,
- ActorMsg: TEvent;
- AD_Init, // used to know if the actors require a partial solution
- ActorActive,
+ // ActorID,
+ StatusEvent,
+ MessageQueueEvent: TEvent;
+ ActorIsActive,
Processing: Boolean;
+
+ actorMessages: TQueue; // A queue for messaging to actors, the aim is to reduce inconsistency
+ actorMessagesLock: TCriticalSection;
+
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ AD_Init: Boolean; // used to know if the actors require a partial solution
-{$IFDEF DSS_CAPI_PM}
procedure Start_Diakoptics();
+ procedure IndexBuses(); // Locates the actor buses within the bus array in Actor 1 (interconnected)
{$ENDIF}
- procedure Notify_Main;
- function Get_Processing(): Boolean;
- procedure Set_Processing(Nval: Boolean);
function Get_CPU(): Integer;
procedure Set_CPU(CPU: Integer);
-//*******************************Public components******************************
- PUBLIC
- procedure Send_Message(Msg: Integer);
- procedure CallCallBack;
- property Event: TEvent READ UIEvent;
- property Is_Busy: Boolean READ Get_Processing WRITE Set_Processing;
+ PUBLIC
+ procedure Send_Message(Msg: TActorMessage);
property CPU: Integer READ Get_CPU WRITE Set_CPU;
-
end;
{$ENDIF}
- TSolutionObj = class(TDSSObject)
+ TSolutionObj = class(TObject)
PRIVATE
dV: pNodeVArray; // Array of delta V for Newton iteration
FFrequency: Double;
@@ -176,7 +137,6 @@ TSolutionObj = class(TDSSObject)
procedure DoNewtonSolution;
procedure DoNormalSolution;
-// PROCEDURE GetMachineInjCurrents;
procedure SumAllCurrents;
procedure Set_Frequency(const Value: Double);
procedure Set_Mode(const Value: TSolveMode);
@@ -184,9 +144,9 @@ TSolutionObj = class(TDSSObject)
procedure Set_Total_Time(const Value: Double);
PUBLIC
-
+ DSS: TDSSContext;
Algorithm: Integer; // NORMALSOLVE or NEWTONSOLVE
- AuxCurrents: pComplexArray; // For injections like AutoAdd
+ AuxCurrents: pComplexArray; // For injections like AutoAdd
ControlActionsDone: Boolean;
ControlIteration: Integer;
ControlMode: Integer; // EVENTDRIVEN, TIMEDRIVEN
@@ -204,9 +164,9 @@ TSolutionObj = class(TDSSObject)
Harmonic: Double;
HarmonicList: pDoubleArray;
HarmonicListSize: Integer;
- hYsystem: NativeUint; {Handle for main (system) Y matrix}
- hYseries: NativeUint; {Handle for series Y matrix}
- hY: NativeUint; {either hYsystem or hYseries}
+ hYsystem: NativeUint; // Handle for main (system) Y matrix
+ hYseries: NativeUint; // Handle for series Y matrix
+ hY: NativeUint; // either hYsystem or hYseries
IntervalHrs: Double; // Solution interval since last solution, hrs.
IsDynamicModel: Boolean;
IsHarmonicModel: Boolean;
@@ -234,21 +194,15 @@ TSolutionObj = class(TDSSObject)
ProgressCount: Integer; // used in SolutionAlgs
SolverOptions: Uint64; // KLUSolveX options
-
- {Voltage and Current Arrays}
+ // Voltage and Current Arrays
NodeV: pNodeVArray; // Main System Voltage Array allows NodeV^[0]=0
Currents: pNodeVArray; // Main System Currents Array
-{$IFDEF DSS_CAPI_PM}
- {A-Diakoptics variables}
- Node_dV: pNodeVArray; // Used to store the partial solution voltage
- Ic_Local: pNodeVArray; // Used to store the complementary curret
-{$ENDIF}
-//******************************************************************************
+// ******************************************************************************
IncMat: Tsparse_matrix; // Incidence sparse matrix
Laplacian: Tsparse_matrix; // Laplacian sparse matrix
-//****************************Timing variables**********************************
+// ****************************Timing variables**********************************
SolveStartTime: Int64;
SolveEndtime: Int64;
GStartTime: Int64;
@@ -258,13 +212,13 @@ TSolutionObj = class(TDSSObject)
Solve_Time_Elapsed: Double;
Total_Solve_Time_Elapsed: Double;
Step_Time_Elapsed: Double;
-//******************************************************************************
+// ******************************************************************************
// ActiveCell of the Incidence Matrix:
// [0] = row
// [1] = col
// [2] = value
ActiveIncCell: array[0..2] of Integer;
-//******************************************************************************
+// ******************************************************************************
// IncMatrix Row and column descriptors
// Rows array (array of strings that tells what is the order of the PDElements)
// Columns array (array of strigns with the names of the cols of the Inc matrix)'
@@ -276,21 +230,36 @@ TSolutionObj = class(TDSSObject)
temp_counter: Integer;
Active_Cols: array of Integer;
Active_Cols_Idx: array of Integer;
-//******************************************************************************
-//********************Diakoptics solution mode variables************************
-{$IFDEF DSS_CAPI_PM}
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ // A-Diakoptics variables
+
+ ADiakoptics: Boolean;
+ ADiak_Init: Boolean;
+ ADiak_PCInj: Boolean;
+
+ Node_dV: pNodeVArray; // Used to store the partial solution voltage
+ Ic_Local: pNodeVArray; // Used to store the complementary curret
+// ******************************************************************************
+// ********************Diakoptics solution mode variables************************
ADiakoptics_ready: Boolean;
ADiakoptics_Actors: Integer;
LockNodeV: TCriticalSection;
+ LocalBusIdx: array of Integer;
+ AD_IBus: TList; // Location of the Current injection bus
+ AD_ISrcIdx: TList; // Locator of the ISource bus in actor 1
+
+ function SolveAD(Initialize: Boolean): Integer; // solve one of the A-Diakoptics stages locally
+ procedure SendCmd2Actors(Msg: Integer); // Sends a message to other actors different than 1
+ procedure UpdateISrc; // Updates the local ISources using the data available at Ic for actor 1
+ function VoltInActor1(NodeIdx: Integer): complex; // returns the voltage indicated in NodeIdx in the context of the actor 1
{$ENDIF}
-//******************************************************************************
- constructor Create(ParClass: TDSSClass; const solutionname: String);
+
+ constructor Create(dssContext: TDSSContext; const solutionname: String);
destructor Destroy; OVERRIDE;
function Converged: Boolean;
procedure SetGeneratordQdV;
- procedure ZeroAuxCurrents;
function SolveZeroLoadSnapShot: Integer;
procedure DoPFLOWsolution;
@@ -315,8 +284,7 @@ TSolutionObj = class(TDSSObject)
function VDiff(i, j: Integer): Complex; // Difference between two node voltages
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False);
procedure WriteConvergenceReport(const Fname: String);
procedure Update_dblHour;
procedure Increment_time;
@@ -331,8 +299,6 @@ TSolutionObj = class(TDSSObject)
property Time_Step: Double READ Step_Time_Elapsed; // Solve + sample
property Total_Time: Double READ Total_Time_Elapsed WRITE Set_Total_Time;
- // Procedures that use to be private before 01-20-2016
-
procedure AddInAuxCurrents(SolveType: Integer);
function SolveSystem(V: pNodeVArray): Integer;
procedure GetPCInjCurr;
@@ -340,30 +306,35 @@ TSolutionObj = class(TDSSObject)
procedure ZeroInjCurr;
procedure Upload2IncMatrix;
- procedure Calc_Inc_Matrix; // Calculates the incidence matrix for the Circuit
- procedure Calc_Inc_Matrix_Org; // Calculates the incidence matrix hierarchically organized for the Circuit
+ procedure Calc_Inc_Matrix; // Calculates the incidence matrix for the Circuit
+ procedure Calc_Inc_Matrix_Org; // Calculates the incidence matrix hierarchically organized for the Circuit
- function get_IncMatrix_Row(Col: Integer): Integer; // Gets the index of the Row connected to the specified Column
- function get_IncMatrix_Col(Row: Integer): Integer; // Gets the index of the Column connected to the specified Row
- function CheckLocationIdx(Idx: Integer): Integer; // Evaluates the area covered by the tearing point to see if there is a better one
+ function get_IncMatrix_Row(Col: Integer): Integer; // Gets the index of the Row connected to the specified Column
+ function get_IncMatrix_Col(Row: Integer): Integer; // Gets the index of the Column connected to the specified Row
+ function CheckLocationIdx(Idx: Integer): Integer; // Evaluates the area covered by the tearing point to see if there is a better one
+ function get_PDE_Bus1_Location(myPDE: String): Integer; // Gets the index of myPDE -> bus1 within the Inc matrix
- procedure AddLines2IncMatrix; // Adds the Lines to the Incidence matrix arrays
- procedure AddXfmr2IncMatrix; // Adds the Xfmrs to the Incidence matrix arrays
- procedure AddSeriesCap2IncMatrix; // Adds capacitors in series to the Incidence matrix arrays
- procedure AddSeriesReac2IncMatrix; // Adds Reactors in series to the Incidence matrix arrays
+ procedure AddLines2IncMatrix; // Adds the Lines to the Incidence matrix arrays
+ procedure AddXfmr2IncMatrix; // Adds the Xfmrs to the Incidence matrix arrays
+ procedure AddSeriesCap2IncMatrix; // Adds capacitors in series to the Incidence matrix arrays
+ procedure AddSeriesReac2IncMatrix; // Adds Reactors in series to the Incidence matrix arrays
end;
-{==========================================================================}
-
implementation
uses
+ BufStream,
SolutionAlgs,
DSSClassDefs,
DSSGlobals,
{$IFDEF MSWINDOWS}
SHELLAPI,
+{$ELSE}
+ {$IFDEF DSS_CAPI_PM}
+ initc,
+ cpucount,
+ {$ENDIF}
{$ENDIF}
CmdForms,
PDElement,
@@ -372,7 +343,6 @@ implementation
Executive,
AutoAdd,
YMatrix,
- ParserDel,
Generator,
Load,
CKtTree,
@@ -383,9 +353,8 @@ implementation
Circuit,
Utilities,
KLUSolve,
- DSSPointerList,
Line,
-{$IFDEF DSS_CAPI_PM}
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
Diakoptics,
{$ENDIF}
DSSHelper;
@@ -393,68 +362,58 @@ implementation
const
NumPropsThisClass = 1;
-// ===========================================================================================
-constructor TDSSSolution.Create(dssContext: TDSSContext); // Collection of all solution objects
-begin
- inherited Create(dssContext);
- Class_Name := 'Solution';
- DSSClassType := DSS_OBJECT + HIDDEN_ELEMENT;
-
- ActiveElement := 0;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
-end;
-
-// ===========================================================================================
-destructor TDSSSolution.Destroy;
-
+{$IFDEF DSS_CAPI_PM}
+ {$if defined(WINDOWS)}
+function Set_Thread_Affinity(Hnd : THandle; CPU : integer): Integer;
+var
+ CPU_bit: integer;
+ Op_Result: Dword;
begin
- // ElementList and CommandList freed in inherited destroy
- inherited Destroy;
+ if CPU >= 0 then
+ CPU_bit := 1 shl CPU
+ else
+ CPU_bit := -1;
+ Op_Result := SetThreadAffinityMask(Hnd, CPU_bit);
+ if Op_Result = 0 then
+ raise Exception.Create(Format(_('Error setting thread affinity mask: %d'), [GetLastError]));
+ Result := Op_Result;
end;
-
-// ===========================================================================================
-procedure TDSSSolution.DefineProperties;
+ {$ELSEIF defined(freebsd) or defined(darwin)}
+function Set_Thread_Affinity(Hnd: TThreadId; CPU: integer): Integer;
begin
-
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
- PropertyName[1] := '-------';
-
-
- // define Property help values
- PropertyHelp[1] := 'Use Set Command to set Solution properties.';
-
-
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ Result := 0;
end;
+ {$ELSE}
+function pthread_setaffinity_np(pid : PtrUint; cpusetsize: NativeUInt; cpuset: Pointer): NativeInt; cdecl; external;
-// ===========================================================================================
-function TDSSSolution.NewObject(const ObjName: String): Integer;
+function Set_Thread_Affinity(Hnd: TThreadId; CPU: integer): Integer;
+const
+ cpu_SetSize = SizeOf(QWord); // 8 bytes, 64 cores max -- to match the limitations of the Windows version
+var
+ cpu_set : QWord;
+ Op_Result : longint;
begin
- // Make a new Solution Object and add it to Solution class list
- DSS.ActiveSolutionObj := TSolutionObj.Create(Self, ObjName);
- // this one is different than the rest of the objects.
- Result := AdDobjectToList(DSS.ActiveSolutionObj);
+ if CPU >= 0 then
+ cpu_set := 1 shl CPU
+ else
+ begin
+ cpu_set := 0;
+ cpu_set := not cpu_set;
+ end;
+ Op_Result := pthread_setaffinity_np(Hnd, cpu_SetSize, @cpu_set);
+ if Op_Result <> 0 then raise Exception.Create('Error setting thread affinity mask');
+ Result := Op_Result;
end;
+ {$ENDIF}
+{$ENDIF}
-// ===========================================================================================
-constructor TSolutionObj.Create(ParClass: TDSSClass; const SolutionName: String);
-// ===========================================================================================
+constructor TSolutionObj.Create(dssContext: TDSSContext; const SolutionName: String);
begin
- inherited Create(ParClass);
- Name := LowerCase(SolutionName);
+ inherited Create;
+ // Name := AnsiLowerCase(SolutionName);
+ DSS := dssContext;
SolverOptions := 0;
@@ -491,7 +450,7 @@ constructor TSolutionObj.Create(ParClass: TDSSClass; const SolutionName: String)
SystemYChanged := TRUE;
SeriesYInvalid := TRUE;
- {Define default harmonic list}
+ // Define default harmonic list
HarmonicListSize := 5;
HarmonicList := AllocMem(SizeOf(harmonicList^[1]) * HarmonicListSize);
HarmonicList^[1] := 1.0;
@@ -526,22 +485,27 @@ constructor TSolutionObj.Create(ParClass: TDSSClass; const SolutionName: String)
DefaultControlMode := ControlMode;
Algorithm := NORMALSOLVE;
- RandomType := GAUSSIAN; // default to gaussian
+ RandomType := GAUSSIAN; // default to gaussian
NumberOfTimes := 100;
IntervalHrs := 1.0;
-
- InitPropertyValues(0);
-
{$IFDEF DSS_CAPI_PM}
- ADiakoptics_Ready := FALSE; // A-Diakoptics needs to be initialized
- if not Assigned(DSS.ActorMA_Msg) then
- DSS.ActorMA_Msg := TEvent.Create(NIL, TRUE, FALSE, '');
-
- LockNodeV := SyncObjs.TCriticalSection.Create;
+ if not Assigned(DSS.ThreadStatusEvent) then
+ DSS.ThreadStatusEvent := TEvent.Create(NIL, TRUE, FALSE, '');
+{$ENDIF}
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ ADiakoptics := False;
+ ADiak_Init := False;
+ ADiak_PCInj := False;
+
+ Node_dV := NIL;
+ Ic_Local := NIL;
+ AD_IBus := NIL;
+ AD_ISrcIdx := NIL;
+ ADiakoptics_Ready := FALSE; // A-Diakoptics needs to be initialized
+ LockNodeV := SyncObjs.TCriticalSection.Create9);
{$ENDIF}
end;
-// ===========================================================================================
destructor TSolutionObj.Destroy;
begin
Reallocmem(AuxCurrents, 0);
@@ -557,63 +521,49 @@ destructor TSolutionObj.Destroy;
if hYseries <> 0 then
DeleteSparseSet(hYseries);
-// SetLogFile ('c:\\temp\\KLU_Log.txt', 0);
-
Reallocmem(HarmonicList, 0);
{$IFDEF DSS_CAPI_PM}
// Sends a message to the working actor
with DSS do
begin
- ActorMA_Msg.SetEvent();
+ // ThreadStatusEvent.SetEvent();
if ActorThread <> NIL then
begin
- ActorThread.Send_Message(EXIT_ACTOR);
+ SolutionAbort := True;
+ ActorThread.Send_Message(TActorMessage.EXIT_ACTOR);
ActorThread.WaitFor();
ActorThread.Free();
ActorThread := nil;
end;
- ActorMA_Msg.Free;
- ActorMA_Msg := NIL;
+ ThreadStatusEvent.Free;
+ ThreadStatusEvent := NIL;
end;
+{$ENDIF}
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
LockNodeV.Free;
{$ENDIF}
inherited Destroy;
end;
-
-// ===========================================================================================
-function TDSSSolution.Edit: Integer;
-
-begin
- Result := 0;
-
- DSS.ActiveSolutionObj := DSS.ActiveCircuit.Solution;
-
- with DSS.ActiveSolutionObj do
- begin
-
- // This is all we do here now...
- Solve;
-
- end; {WITH}
-end;
-
-// ===========================================================================================
procedure TSolutionObj.Solve;
-
+{$IFDEF DSS_CAPI_PM}
+var
+ PMParent: TDSSContext;
+begin
+ PMParent := DSS.GetPrime();
+{$ELSE}
begin
+{$ENDIF}
DSS.ActiveCircuit.Issolved := FALSE;
DSS.SolutionWasAttempted := TRUE;
InitProgressForm; // initialize Progress Form;
-{Check of some special conditions that must be met before executing solutions}
-
+ // Check of some special conditions that must be met before executing solutions
if DSS.ActiveCircuit.EmergMinVolts >= DSS.ActiveCircuit.NormalMinVolts then
begin
- DoSimpleMsg('Error: Emergency Min Voltage Must Be Less Than Normal Min Voltage!' +
- CRLF + 'Solution Not Executed.', 480);
+ DoSimpleMsg(DSS, _('Error: Emergency Min Voltage Must Be Less Than Normal Min Voltage! Solution Not Executed.'), 480);
Exit;
end;
@@ -625,10 +575,9 @@ procedure TSolutionObj.Solve;
Exit;
end;
try
-{Main solution Algorithm dispatcher}
+ // Main solution Algorithm dispatcher
with DSS.ActiveCircuit do
begin
-
case Year of
0:
DefaultGrowthFactor := 1.0; // RCD 8-17-00
@@ -639,141 +588,131 @@ procedure TSolutionObj.Solve;
{$IFDEF DLL_ENGINE}
Fire_InitControls;
{$ENDIF}
- {CheckFaultStatus; ???? needed here??}
- {$IFDEF MSWINDOWS}
- QueryPerformanceCounter(GStartTime);
+ // CheckFaultStatus; ???? needed here??
+
+{$IFDEF DSS_CAPI_PM}
+ // If we won't run in parallel and don't have a thread already,
+ // don't use a new thread
+ if (not PMParent.Parallel_enabled) and (DSS.ActorThread = NIL) then
+ begin
{$ENDIF}
-{$IFNDEF DSS_CAPI_PM}
- case Dynavars.SolutionMode of
- TSolveMode.SNAPSHOT:
- SolveSnap;
- TSolveMode.YEARLYMODE:
- SolveYearly;
- TSolveMode.DAILYMODE:
- SolveDaily;
- TSolveMode.DUTYCYCLE:
- SolveDuty;
- TSolveMode.DYNAMICMODE:
- SolveDynamic;
- TSolveMode.MONTECARLO1:
- SolveMonte1;
- TSolveMode.MONTECARLO2:
- SolveMonte2;
- TSolveMode.MONTECARLO3:
- SolveMonte3;
- TSolveMode.PEAKDAY:
- SolvePeakDay;
- TSolveMode.LOADDURATION1:
- SolveLD1;
- TSolveMode.LOADDURATION2:
- SolveLD2;
- TSolveMode.DIRECT:
- SolveDirect;
- TSolveMode.MONTEFAULT:
- SolveMonteFault; // Monte Carlo Fault Cases
- TSolveMode.FAULTSTUDY:
- SolveFaultStudy;
- TSolveMode.AUTOADDFLAG:
- DSS.ActiveCircuit.AutoAddObj.Solve;
- TSolveMode.HARMONICMODE:
- SolveHarmonic;
- TSolveMode.GENERALTIME:
- SolveGeneralTime;
- TSolveMode.HARMONICMODET:
- SolveHarmonicT; //Declares the Hsequential-time harmonics
- else
- DoSimpleMsg('Unknown solution mode.', 481);
+ {$IFDEF WINDOWS}
+ QueryPerformanceCounter(GStartTime);
+ {$ELSE}
+ GStartTime := GetTickCount64;
+ {$ENDIF}
+ case Dynavars.SolutionMode of
+ TSolveMode.SNAPSHOT:
+ SolveSnap;
+ TSolveMode.YEARLYMODE:
+ SolveYearly;
+ TSolveMode.DAILYMODE:
+ SolveDaily;
+ TSolveMode.DUTYCYCLE:
+ SolveDuty;
+ TSolveMode.DYNAMICMODE:
+ SolveDynamic;
+ TSolveMode.MONTECARLO1:
+ SolveMonte1;
+ TSolveMode.MONTECARLO2:
+ SolveMonte2;
+ TSolveMode.MONTECARLO3:
+ SolveMonte3;
+ TSolveMode.PEAKDAY:
+ SolvePeakDay;
+ TSolveMode.LOADDURATION1:
+ SolveLD1;
+ TSolveMode.LOADDURATION2:
+ SolveLD2;
+ TSolveMode.DIRECT:
+ SolveDirect;
+ TSolveMode.MONTEFAULT:
+ SolveMonteFault; // Monte Carlo Fault Cases
+ TSolveMode.FAULTSTUDY:
+ SolveFaultStudy;
+ TSolveMode.AUTOADDFLAG:
+ DSS.ActiveCircuit.AutoAddObj.Solve;
+ TSolveMode.HARMONICMODE:
+ SolveHarmonic;
+ TSolveMode.GENERALTIME:
+ SolveGeneralTime;
+ TSolveMode.HARMONICMODET:
+ SolveHarmonicT; //Declares the Hsequential-time harmonics
+ else
+ DoSimpleMsg(DSS, _('Unknown solution mode.'), 481);
+ end;
+ {$IFDEF WINDOWS}
+ QueryPerformanceCounter(GEndTime);
+ {$ELSE}
+ GEndTime := GetTickCount64;
+ {$ENDIF}
+ Total_Solve_Time_Elapsed := ((GEndTime - GStartTime) / CPU_Freq) * 1000000;
+ Total_Time_Elapsed := Total_Time_Elapsed + Total_Solve_Time_Elapsed;
+ Exit;
+{$IFDEF DSS_CAPI_PM}
end;
- {$IFDEF MSWINDOWS}
- QueryPerformanceCounter(GEndTime);
- {$ENDIF}
- Total_Solve_Time_Elapsed := ((GEndTime - GStartTime) / CPU_Freq) * 1000000;
- Total_Time_Elapsed := Total_Time_Elapsed + Total_Solve_Time_Elapsed;
-{$ELSE} // DSS_CAPI_PM
// Creates the actor again in case of being terminated due to an error before
if (DSS.ActorThread = NIL) or DSS.ActorThread.Terminated then
begin
- if DSS.ActorThread.Terminated then
+ if (DSS.ActorThread <> NIL) and DSS.ActorThread.Terminated then
DSS.ActorThread.Free;
- New_Actor(DSS);
+ DSS.ActorThread := TSolver.Create(DSS, True, DSS.CPU, DSS.ThreadStatusEvent);
+ // DSS.ActorThread.Priority := tpTimeCritical;
+ DSS.ActorStatus := TActorStatus.Busy;
+ DSS.ActorThread.Start();
end;
- {CheckFaultStatus; ???? needed here??}
+ // CheckFaultStatus; ???? needed here??
// Resets the event for receiving messages from the active actor
// Updates the status of the Actor in the GUI
- DSS.ActorStatus := TActorStatus.Busy;
- DSS.ActorMA_Msg.ResetEvent;
- {$IFNDEF FPC}
- if not DSS.ADiakoptics then
- begin
- if not IsDLL then
- ScriptEd.UpdateSummaryForm('1');
- end
- else
- begin
- if DSS.IsPrime then
- if not IsDLL then
- ScriptEd.UpdateSummaryForm('1');
- end;
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- QueryPerformanceCounter(GEndTime);
+ // DSS.ActorStatus := TActorStatus.Busy;
+ // DSS.ThreadStatusEvent.SetEvent();
+
+ {$IFDEF WINDOWS}
+ QueryPerformanceCounter(GStartTime);
+ {$ELSE}
+ GStartTime := GetTickCount64;
{$ENDIF}
- Total_Solve_Time_Elapsed := ((GEndTime - GStartTime) / CPU_Freq) * 1000000;
- Total_Time_Elapsed := Total_Time_Elapsed + Total_Solve_Time_Elapsed;
// Sends message to start the Simulation
- DSS.ActorThread.Send_Message(SIMULATE);
+ DSS.ActorThread.Send_Message(TActorMessage.SIMULATE);
+
// If the parallel mode is not active, Waits until the actor finishes
- if not DSS.Parallel_enabled then
- begin
+ if not DSS.GetPrime().Parallel_enabled then
Wait4Actors(DSS, ALL_ACTORS);
- {$IFNDEF FPC}
- if not DSS.ADiakoptics then
- begin
- if not IsDLL then
- ScriptEd.UpdateSummaryForm('1');
- end
- else
- begin
- if DSS.IsPrime then
- if not IsDLL then
- ScriptEd.UpdateSummaryForm('1');
- end;
- {$ENDIF}
- end;
{$ENDIF} // DSS_CAPI_PM
-
-
except
-
On E: Exception do
begin
- DoSimpleMsg('Error Encountered in Solve: ' + E.Message, 482);
+ DoSimpleMsg(DSS, 'Error Encountered in Solve: %s', [E.Message], 482);
DSS.SolutionAbort := TRUE;
end;
end;
-
end;
-// ===========================================================================================
function TSolutionObj.Converged: Boolean;
-
var
i: Integer;
VMag: Double;
begin
-
-// base convergence on voltage magnitude
+ // base convergence on voltage magnitude
MaxError := 0.0;
for i := 1 to DSS.ActiveCircuit.NumNodes do
begin
- VMag := Cabs(NodeV^[i]);
-
- { If base specified, use it; otherwise go on present magnitude }
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ if not ADiakoptics or (DSS.Parent = NIL) then
+{$ENDIF}
+ VMag := Cabs(NodeV^[i])
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ else
+ VMag := Cabs(VoltInActor1(i));
+{$ELSE}
+ ;
+{$ENDIF}
+ // If base specified, use it; otherwise go on present magnitude
if NodeVbase^[i] > 0.0 then
ErrorSaved^[i] := Abs(Vmag - VmagSaved^[i]) / NodeVbase^[i]
else
@@ -785,29 +724,21 @@ function TSolutionObj.Converged: Boolean;
end;
- if (MaxError <= ConvergenceTolerance) and (not IsNaN(MaxError)) then
+ if (MaxError <= ConvergenceTolerance) and (not IsNaN(MaxError)) and (not IsInfinite(MaxError)) then
Result := TRUE
else
Result := FALSE;
-
ConvergedFlag := Result;
end;
-
-// ===========================================================================================
procedure TSolutionObj.GetSourceInjCurrents;
-
// Add in the contributions of all source type elements to the global solution vector InjCurr
-
var
pElem: TDSSCktElement;
-
begin
-
with DSS.ActiveCircuit do
begin
-
pElem := Sources.First;
while pElem <> NIL do
begin
@@ -817,14 +748,10 @@ procedure TSolutionObj.GetSourceInjCurrents;
end;
end;
-
end;
-// ===========================================================================================
procedure TSolutionObj.SetGeneratorDispRef;
-
// Set the global generator dispatch reference
-
begin
with DSS.ActiveCircuit do
case Dynavars.SolutionMode of
@@ -866,39 +793,33 @@ procedure TSolutionObj.SetGeneratorDispRef;
TSolveMode.HARMONICMODET:
GeneratorDispatchReference := LoadMultiplier * DefaultGrowthFactor * DefaultHourMult.re;
else
- DoSimpleMsg('Unknown solution mode.', 483);
+ DoSimpleMsg(DSS, _('Unknown solution mode.'), 483);
end;
end;
-// ===========================================================================================
-procedure TSolutionObj.SetGeneratordQdV;
+procedure TSolutionObj.SetGeneratordQdV;
var
pGen: TGeneratorObj;
Did_One: Boolean;
GenDispSave: Double;
-
begin
Did_One := FALSE;
- // Save the generator dispatch level and set on high enough to
- // turn all generators on
+ // Save the generator dispatch level and set on high enough to
+ // turn all generators on
GenDispSave := DSS.ActiveCircuit.GeneratorDispatchReference;
DSS.ActiveCircuit.GeneratorDispatchReference := 1000.0;
with DSS.ActiveCircuit do
begin
-
pGen := Generators.First;
while pGen <> NIL do
begin
-
if pGen.Enabled then
begin
-
// for PV generator models only ...
if pGen.genModel = 3 then
begin
-
pGen.InitDQDVCalc;
// solve at base var setting
@@ -935,7 +856,7 @@ procedure TSolutionObj.SetGeneratordQdV;
end;
- // Restore generator dispatch reference
+ // Restore generator dispatch reference
DSS.ActiveCircuit.GeneratorDispatchReference := GenDispSave;
try
if Did_One // Reset Initial Solution
@@ -944,33 +865,27 @@ procedure TSolutionObj.SetGeneratordQdV;
except
ON E: EEsolv32Problem do
begin
- DoSimpleMsg('From SetGenerator DQDV, SolveZeroLoadSnapShot: ' + CRLF + E.Message + CheckYMatrixforZeroes(DSS), 7071);
+ DoSimpleMsg(DSS, 'From SetGenerator DQDV, SolveZeroLoadSnapShot: %s', [CRLF + E.Message + CheckYMatrixforZeroes(DSS)], 7071);
raise ESolveError.Create('Aborting');
end;
end;
-
end;
-// ===========================================================================================
procedure TSolutionObj.DoNormalSolution;
-
-{ Normal fixed-point solution
-
- Vn+1 = [Y]-1 Injcurr
-
- Where Injcurr includes only PC elements (loads, generators, etc.)
- i.e., the shunt elements.
-
- Injcurr are the current injected INTO the NODE
- (need to reverse current direction for loads)
-}
-
+// Normal fixed-point solution
+//
+// Vn+1 = [Y]-1 Injcurr
+//
+// Where Injcurr includes only PC elements (loads, generators, etc.)
+// i.e., the shunt elements.
+//
+// Injcurr are the current injected INTO the NODE
+// (need to reverse current direction for loads)
begin
-
Iteration := 0;
- {**** Main iteration loop ****}
+ // **** Main iteration loop ****
with DSS.ActiveCircuit do
repeat
Inc(Iteration);
@@ -978,55 +893,57 @@ procedure TSolutionObj.DoNormalSolution;
if LogEvents then
LogThisEvent(DSS, 'Solution Iteration ' + IntToStr(Iteration));
- { Get injcurrents for all PC devices }
- ZeroInjCurr;
- GetSourceInjCurrents; // sources
- GetPCInjCurr; // Get the injection currents from all the power conversion devices and feeders
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ if (not ADiakoptics) or (DSS.Parent <> NIL) then // Normal simulation
+ begin // In A-Diakoptics, all other actors do normal solution
+{$ENDIF}
+ // Get injcurrents for all PC devices
+ ZeroInjCurr;
+ GetSourceInjCurrents; // sources
+ GetPCInjCurr; // Get the injection currents from all the power conversion devices and feeders
- // The above call could change the primitive Y matrix, so have to check
- if SystemYChanged {$IFDEF DSS_CAPI_INCREMENTAL_Y}or (DSS.ActiveCircuit.IncrCktElements.Count <> 0){$ENDIF} then
+ // The above call could change the primitive Y matrix, so have to check
+ if SystemYChanged {$IFDEF DSS_CAPI_INCREMENTAL_Y}or (DSS.ActiveCircuit.IncrCktElements.Count <> 0){$ENDIF} then
+ begin
+ BuildYMatrix(DSS, WHOLEMATRIX, FALSE); // Does not realloc V, I
+ end;
+ if UseAuxCurrents then
+ AddInAuxCurrents(NORMALSOLVE);
+
+ // Solve for voltages -- Note: NodeV[0] = 0 + j0 always
+ if LogEvents then
+ LogThisEvent(DSS, 'Solve Sparse Set DoNormalSolution ...');
+ SolveSystem(NodeV);
+ LoadsNeedUpdating := FALSE;
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ end
+ else
begin
- BuildYMatrix(DSS, WHOLEMATRIX, FALSE); // Does not realloc V, I
+ ADiak_PCInj := TRUE;
+ Solve_Diakoptics(DSS); // A-Diakoptics
end;
- if UseAuxCurrents then
- AddInAuxCurrents(NORMALSOLVE);
-
- // Solve for voltages {Note:NodeV[0] = 0 + j0 always}
- if LogEvents then
- LogThisEvent(DSS, 'Solve Sparse Set DoNormalSolution ...');
- SolveSystem(NodeV);
- LoadsNeedUpdating := FALSE;
-
+{$ENDIF}
until (Converged and (Iteration >= MinIterations)) or (Iteration >= MaxIterations);
-
end;
-
-// ===========================================================================================
procedure TSolutionObj.DoNewtonSolution;
-
-{ Newton Iteration
-
- Vn+1 = Vn - [Y]-1 Termcurr
-
- Where Termcurr includes currents from all elements and we are
- attempting to get the currents to sum to zero at all nodes.
-
- Termcurr is the sum of all currents going INTO THE TERMINALS of
- the elements.
-
- For PD Elements, Termcurr = Yprim*V
-
- For Loads, Termcurr = (Sload/V)*
- For Generators, Termcurr = -(Sgen/V)*
-
-}
-
+// Newton Iteration
+//
+// Vn+1 = Vn - [Y]-1 Termcurr
+//
+// Where Termcurr includes currents from all elements and we are
+// attempting to get the currents to sum to zero at all nodes.
+//
+// Termcurr is the sum of all currents going INTO THE TERMINALS of
+// the elements.
+//
+// For PD Elements, Termcurr = Yprim*V
+//
+// For Loads, Termcurr = (Sload/V)*
+// For Generators, Termcurr = -(Sgen/V)*
var
i: Integer;
-
begin
-
with DSS.ActiveCircuit do
begin
ReAllocMem(dV, SizeOf(dV^[1]) * (NumNodes + 1)); // Make sure this is always big enough
@@ -1039,12 +956,12 @@ procedure TSolutionObj.DoNewtonSolution;
Inc(Iteration);
Inc(SolutionCount); // SumAllCurrents Uses ITerminal So must force a recalc
- // Get sum of currents at all nodes for all devices
+ // Get sum of currents at all nodes for all devices
ZeroInjCurr;
SumAllCurrents;
- // Call to current calc could change YPrim for some devices
- if SystemYChanged then
+ // Call to current calc could change YPrim for some devices
+ if SystemYChanged {$IFDEF DSS_CAPI_INCREMENTAL_Y}or (DSS.ActiveCircuit.IncrCktElements.Count <> 0){$ENDIF} then
begin
BuildYMatrix(DSS, WHOLEMATRIX, FALSE); // Does not realloc V, I
end;
@@ -1052,12 +969,12 @@ procedure TSolutionObj.DoNewtonSolution;
if UseAuxCurrents then
AddInAuxCurrents(NEWTONSOLVE);
- // Solve for change in voltages
+ // Solve for change in voltages
SolveSystem(dV);
LoadsNeedUpdating := FALSE;
- // Compute new guess at voltages
+ // Compute new guess at voltages
for i := 1 to NumNodes do // 0 node is always 0
with NodeV^[i] do
begin
@@ -1069,13 +986,8 @@ procedure TSolutionObj.DoNewtonSolution;
end;
end;
-
-// ===========================================================================================
procedure TSolutionObj.DoPFLOWsolution;
-
-
begin
-
Inc(SolutionCount); //Unique number for this solution
if VoltageBaseChanged then
@@ -1083,16 +995,15 @@ procedure TSolutionObj.DoPFLOWsolution;
if not SolutionInitialized then
begin
-
if DSS.ActiveCircuit.LogEvents then
LogThisEvent(DSS, 'Initializing Solution');
try
- //SolveZeroLoadSnapShot;
+ //SolveZeroLoadSnapShot;
SolveYDirect; // 8-14-06 This should give a better answer than zero load snapshot
except
ON E: EEsolv32Problem do
begin
- DoSimpleMsg('From DoPFLOWsolution.SolveYDirect: ' + CRLF + E.Message + CheckYMatrixforZeroes(DSS), 7072);
+ DoSimpleMsg(DSS, 'From DoPFLOWsolution.SolveYDirect: %s', [CRLF + E.Message + CheckYMatrixforZeroes(DSS)], 7072);
raise ESolveError.Create('Aborting');
end;
end;
@@ -1104,16 +1015,15 @@ procedure TSolutionObj.DoPFLOWsolution;
except
ON E: EEsolv32Problem do
begin
- DoSimpleMsg('From DoPFLOWsolution.SetGeneratordQdV: ' + CRLF + E.Message + CheckYMatrixforZeroes(DSS), 7073);
+ DoSimpleMsg(DSS, 'From DoPFLOWsolution.SetGeneratordQdV: %s', [CRLF + E.Message + CheckYMatrixforZeroes(DSS)], 7073);
raise ESolveError.Create('Aborting');
end;
end;
- { The above resets the active sparse set to hY }
+ // The above resets the active sparse set to hY
SolutionInitialized := TRUE;
end;
-
case Algorithm of
NORMALSOLVE:
DoNormalSolution;
@@ -1123,14 +1033,10 @@ procedure TSolutionObj.DoPFLOWsolution;
DSS.ActiveCircuit.Issolved := ConvergedFlag;
LastSolutionWasDirect := FALSE;
-
end;
-// ===========================================================================================
function TSolutionObj.SolveZeroLoadSnapShot: Integer;
-
// Solve without load for initialization purposes;
-
begin
Result := 0;
@@ -1143,7 +1049,7 @@ function TSolutionObj.SolveZeroLoadSnapShot: Integer;
ZeroInjCurr; // Side Effect: Allocates InjCurr
GetSourceInjCurrents; // Vsource, Isource and VCCS only
- {Make the series Y matrix the active matrix}
+ // Make the series Y matrix the active matrix
if hYseries = 0 then
raise EEsolv32Problem.Create('Series Y matrix not built yet in SolveZeroLoadSnapshot.');
hY := hYseries;
@@ -1153,22 +1059,17 @@ function TSolutionObj.SolveZeroLoadSnapShot: Integer;
SolveSystem(NodeV); // also sets voltages in radial part of the circuit if radial solution
- { Reset the main system Y as the solution matrix}
+ // Reset the main system Y as the solution matrix
if (hYsystem > 0) and not DSS.SolutionAbort then
hY := hYsystem;
end;
-// ===========================================================================================
procedure TSolutionObj.SetVoltageBases;
-
// Set voltage bases using voltage at first node (phase) of a bus
-
var
i: Integer;
bZoneCalc, bZoneLock: Boolean;
-
begin
-
try
// don't allow the meter zones to auto-build in this load flow solution, because the
// voltage bases are not available yet
@@ -1183,7 +1084,7 @@ procedure TSolutionObj.SetVoltageBases;
with DSS.ActiveCircuit do
for i := 1 to NumBuses do
with Buses^[i] do
- kVBase := NearestBasekV(DSS, Cabs(NodeV^[GetRef(1)]) * 0.001732) / SQRT3; // l-n base kV
+ kVBase := NearestBasekV(DSS, Cabs(NodeV^[RefNo[1]]) * 0.001732) / SQRT3; // l-n base kV
InitializeNodeVbase(DSS); // for convergence test
@@ -1197,61 +1098,78 @@ procedure TSolutionObj.SetVoltageBases;
except
ON E: EEsolv32Problem do
begin
- DoSimpleMsg('From SetVoltageBases.SolveZeroLoadSnapShot: ' + CRLF + E.Message + CheckYMatrixforZeroes(DSS), 7075);
+ DoSimpleMsg(DSS, 'From SetVoltageBases.SolveZeroLoadSnapShot: %s', [CRLF + E.Message + CheckYMatrixforZeroes(DSS)], 7075);
raise ESolveError.Create('Aborting');
end;
end;
-
end;
procedure TSolutionObj.SnapShotInit;
-
begin
-
SetGeneratorDispRef;
ControlIteration := 0;
ControlActionsDone := FALSE;
MostIterationsDone := 0;
LoadsNeedUpdating := TRUE; // Force the loads to update at least once
-
end;
procedure TSolutionObj.CheckControls;
-
begin
- if ControlIteration < MaxControlIterations then
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+var
+ i: Integer;
+begin
+ if not ADiakoptics or (DSS.Parent <> NIL) then
begin
- if ConvergedFlag then
+{$ENDIF}
+ if ControlIteration < MaxControlIterations then
+ begin
+ if ConvergedFlag then
+ begin
+ if DSS.ActiveCircuit.LogEvents then
+ LogThisEvent(DSS, 'Control Iteration ' + IntToStr(ControlIteration));
+ Sample_DoControlActions;
+ Check_Fault_Status;
+ end
+ else
+ ControlActionsDone := TRUE; // Stop solution process if failure to converge
+ end;
+
+ if SystemYChanged {$IFDEF DSS_CAPI_INCREMENTAL_Y}or (DSS.ActiveCircuit.IncrCktElements.Count <> 0){$ENDIF} then
+ begin
+ BuildYMatrix(DSS, WHOLEMATRIX, FALSE); // Rebuild Y matrix, but V stays same
+ end;
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ end
+ else
+ begin
+ if ControlIteration < MaxControlIterations then
begin
if DSS.ActiveCircuit.LogEvents then
LogThisEvent(DSS, 'Control Iteration ' + IntToStr(ControlIteration));
- Sample_DoControlActions;
- Check_Fault_Status;
- end
- else
- ControlActionsDone := TRUE; // Stop solution process if failure to converge
- end;
- if SystemYChanged {$IFDEF DSS_CAPI_INCREMENTAL_Y}or (DSS.ActiveCircuit.IncrCktElements.Count <> 0){$ENDIF} then
- begin
- BuildYMatrix(DSS, WHOLEMATRIX, FALSE); // Rebuild Y matrix, but V stays same
+ SendCmd2Actors(DO_CTRL_ACTIONS);
+ // Checks if there are pending ctrl actions at the actors
+ ControlActionsDone := TRUE;
+ for i := 2 to DSS.NumOfActors do
+ ControlActionsDone := ControlActionsDone and DSS.Children[i - 1].ActiveCircuit.Solution.ControlActionsDone;
+ end;
end;
+{$ENDIF}
end;
-// ===========================================================================================
function TSolutionObj.SolveSnap: Integer; // solve for now once
-
var
TotalIterations: Integer;
-
begin
SnapShotInit;
TotalIterations := 0;
{$IFDEF MSWINDOWS}
QueryPerformanceCounter(SolveStartTime);
-{$ENDIF}
+ {$ELSE}
+ SolveStartTime := GetTickCount64;
+ {$ENDIF}
repeat
-
Inc(ControlIteration);
Result := SolveCircuit; // Do circuit solution w/o checking controls
@@ -1271,68 +1189,85 @@ function TSolutionObj.SolveSnap: Integer; // solve for now once
if not ControlActionsDone and (ControlIteration >= MaxControlIterations) then
begin
- DoSimpleMsg('Warning Max Control Iterations Exceeded. ' + CRLF + 'Tip: Show Eventlog to debug control settings.', 485);
+ DoSimpleMsg(DSS, _('Warning Max Control Iterations Exceeded.') + CRLF + _('Tip: Show Eventlog to debug control settings.'), 485);
DSS.SolutionAbort := TRUE; // this will stop this message in dynamic power flow modes
end;
if DSS.ActiveCircuit.LogEvents then
LogThisEvent(DSS, 'Solution Done');
-{$IFDEF DLL_ENGINE}
+ {$IFDEF DLL_ENGINE}
Fire_StepControls;
-{$ENDIF}
-{$IFDEF MSWINDOWS}
+ {$ENDIF}
+
+ {$IFDEF MSWINDOWS}
QueryPerformanceCounter(SolveEndTime);
-{$ENDIF}
+ {$ELSE}
+ SolveEndTime := GetTickCount64;
+ {$ENDIF}
Solve_Time_Elapsed := ((SolveEndtime - SolveStartTime) / CPU_Freq) * 1000000;
Iteration := TotalIterations; { so that it reports a more interesting number }
end;
-// ===========================================================================================
function TSolutionObj.SolveDirect: Integer; // solve for now once, direct solution
-
begin
Result := 0;
LoadsNeedUpdating := TRUE; // Force possible update of loads and generators
- {$IFDEF MSWINDOWS}
+ {$IFDEF MSWINDOWS}
QueryPerformanceCounter(SolveStartTime);
-{$ENDIF}
+ {$ELSE}
+ SolveStartTime := GetTickCount64;
+ {$ENDIF}
- if SystemYChanged {$IFDEF DSS_CAPI_INCREMENTAL_Y}or (DSS.ActiveCircuit.IncrCktElements.Count <> 0){$ENDIF} then
+ Inc(SolutionCount); // Unique number for this solution
+
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ if not ADiakoptics or (DSS.Parent <> NIL) then
begin
- BuildYMatrix(DSS, WHOLEMATRIX, TRUE); // Side Effect: Allocates V
- end;
+{$ENDIF}
+ if SystemYChanged then
+ begin
+ BuildYMatrix(DSS, WHOLEMATRIX, TRUE); // Side Effect: Allocates V
+ end;
- Inc(SolutionCount); // Unique number for this solution
+ ZeroInjCurr; // Side Effect: Allocates InjCurr
+ GetSourceInjCurrents;
- ZeroInjCurr; // Side Effect: Allocates InjCurr
- GetSourceInjCurrents;
+ // Pick up PCELEMENT injections for Harmonics mode and Dynamics mode
+ // Ignore these injections for powerflow; Use only admittance in Y matrix
+ if IsDynamicModel or IsHarmonicModel then
+ GetPCInjCurr;
- // Pick up PCELEMENT injections for Harmonics mode and Dynamics mode
- // Ignore these injections for powerflow; Use only admittance in Y matrix
- if IsDynamicModel or IsHarmonicModel then
- GetPCInjCurr;
-
- if SolveSystem(NodeV) = 1 // Solve with Zero injection current
- then
+ if SolveSystem(NodeV) = 1 // Solve with Zero injection current
+ then
+ begin
+ DSS.ActiveCircuit.IsSolved := TRUE;
+ ConvergedFlag := TRUE;
+ end;
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ end
+ else
begin
+ ADiak_PCInj := FALSE;
+ Solve_Diakoptics(DSS); // A-Diakoptics
+
DSS.ActiveCircuit.IsSolved := TRUE;
ConvergedFlag := TRUE;
end;
-
- {$IFDEF MSWINDOWS}
- QueryPerformanceCounter(SolveEndTime);
{$ENDIF}
+ {$IFDEF MSWINDOWS}
+ QueryPerformanceCounter(SolveEndTime);
+ {$ELSE}
+ SolveEndTime := GetTickCount64;
+ {$ENDIF}
Solve_Time_Elapsed := ((SolveEndtime - SolveStartTime) / CPU_Freq) * 1000000;
Total_Time_Elapsed := Total_Time_Elapsed + Solve_Time_Elapsed;
Iteration := 1;
LastSolutionWasDirect := TRUE;
-
end;
-
function TSolutionObj.SolveCircuit: Integer;
begin
Result := 0;
@@ -1342,7 +1277,7 @@ function TSolutionObj.SolveCircuit: Integer;
except
ON E: EEsolv32Problem do
begin
- DoSimpleMsg('From SolveSnap.SolveDirect: ' + CRLF + E.Message + CheckYMatrixforZeroes(DSS), 7075);
+ DoSimpleMsg(DSS, 'From SolveSnap.SolveDirect: %s', [CRLF + E.Message + CheckYMatrixforZeroes(DSS)], 7075);
raise ESolveError.Create('Aborting');
end;
end
@@ -1351,21 +1286,22 @@ function TSolutionObj.SolveCircuit: Integer;
try
if SystemYChanged then
begin
- BuildYMatrix(DSS, WHOLEMATRIX, TRUE); // Side Effect: Allocates V
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ if not ADiakoptics or (DSS.Parent <> NIL) then
+{$ENDIF}
+ BuildYMatrix(DSS, WHOLEMATRIX, TRUE); // Side Effect: Allocates V
end;
DoPFLOWsolution;
except
ON E: EEsolv32Problem do
begin
- DoSimpleMsg('From SolveSnap.DoPflowSolution: ' + CRLF + E.Message + CheckYMatrixforZeroes(DSS), 7074);
+ DoSimpleMsg(DSS, 'From SolveSnap.DoPflowSolution: %s', [CRLF + E.Message + CheckYMatrixforZeroes(DSS)], 7074);
raise ESolveError.Create('Aborting');
end;
end
end;
-
end;
-// ===========================================================================================
procedure TSolutionObj.ZeroInjCurr;
var
I: Integer;
@@ -1373,14 +1309,14 @@ procedure TSolutionObj.ZeroInjCurr;
for i := 0 to DSS.ActiveCircuit.NumNodes do
Currents^[i] := CZERO;
end;
-// ===========================================================================================
+
procedure TSolutionObj.Upload2IncMatrix;
begin
// Uploads the values to the incidence matrix
IncMat.insert((ActiveIncCell[0] - 1), (ActiveIncCell[1] - 2), ActiveIncCell[2]);
ActiveIncCell[2] := -1;
end;
-// ===========================================================================================
+
procedure TSolutionObj.AddLines2IncMatrix;
var
LineBus: String;
@@ -1424,7 +1360,7 @@ procedure TSolutionObj.AddLines2IncMatrix;
end;
end;
end;
-// ===========================================================================================
+
procedure TSolutionObj.AddXfmr2IncMatrix;
var
LineBus: String;
@@ -1434,7 +1370,7 @@ procedure TSolutionObj.AddXfmr2IncMatrix;
EndFlag: Boolean;
lst: TDSSPointerList;
begin
-// This routine adds the Transformers to the incidence matrix vectors
+ // This routine adds the Transformers to the incidence matrix vectors
with DSS.ActiveCircuit do
begin
lst := DSS.ActiveCircuit.Transformers;
@@ -1447,13 +1383,13 @@ procedure TSolutionObj.AddXfmr2IncMatrix;
inc(temp_counter);
setlength(Inc_Mat_Rows, temp_counter);
Inc_Mat_Rows[temp_counter - 1] := 'Transformer.' + elem.Name;
- for TermIdx := 1 to elem.NumberOfWindings do
+ for TermIdx := 1 to elem.NumWindings do
begin
LineBus := elem.GetBus(TermIdx);
BusdotIdx := ansipos('.', LineBus);
if BusdotIdx <> 0 then
LineBus := Copy(LineBus, 0, BusdotIdx - 1); // removes the dot from the Bus Name
- // Evaluates the position of the Bus in the array
+ // Evaluates the position of the Bus in the array
ActiveIncCell[1] := 1;
EndFlag := TRUE;
while (ActiveIncCell[1] <= NumBuses) and (EndFlag) do
@@ -1470,7 +1406,7 @@ procedure TSolutionObj.AddXfmr2IncMatrix;
end;
end;
end;
-// ===========================================================================================
+
procedure TSolutionObj.AddSeriesCap2IncMatrix;
var
CapBus: String;
@@ -1480,7 +1416,7 @@ procedure TSolutionObj.AddSeriesCap2IncMatrix;
BusdotIdx: Integer;
CapEndFlag: Boolean;
begin
-// This rouitne adds the series capacitors to the incidence matrix vectors
+ // This routine adds the series capacitors to the incidence matrix vectors
with DSS.ActiveCircuit do
begin
lst := ShuntCapacitors;
@@ -1501,7 +1437,7 @@ procedure TSolutionObj.AddSeriesCap2IncMatrix;
BusdotIdx := ansipos('.', CapBus);
if BusdotIdx <> 0 then
CapBus := Copy(CapBus, 0, BusdotIdx - 1); // removes the dot from the Bus Name
- // Evaluates the position of the Bus in the array
+ // Evaluates the position of the Bus in the array
ActiveIncCell[1] := 1;
CapEndFlag := TRUE;
while (ActiveIncCell[1] <= NumBuses) and (CapEndFlag) do
@@ -1519,7 +1455,7 @@ procedure TSolutionObj.AddSeriesCap2IncMatrix;
end;
end;
end;
-// ===========================================================================================
+
procedure TSolutionObj.AddSeriesReac2IncMatrix;
var
RBus: String;
@@ -1529,7 +1465,7 @@ procedure TSolutionObj.AddSeriesReac2IncMatrix;
BusdotIdx: Integer;
EndFlag: Boolean;
begin
-// This rouitne adds the series reactors to the incidence matrix vectors
+ // This routine adds the series reactors to the incidence matrix vectors
with DSS.ActiveCircuit do
begin
DevClassIndex := DSS.ClassNames.Find('reactor');
@@ -1552,7 +1488,7 @@ procedure TSolutionObj.AddSeriesReac2IncMatrix;
BusdotIdx := ansipos('.', RBus);
if BusdotIdx <> 0 then
RBus := Copy(RBus, 0, BusdotIdx - 1); // removes the dot from the Bus Name
- // Evaluates the position of the Bus in the array
+ // Evaluates the position of the Bus in the array
ActiveIncCell[1] := 1;
EndFlag := TRUE;
while (ActiveIncCell[1] <= NumBuses) and (EndFlag) do
@@ -1569,12 +1505,12 @@ procedure TSolutionObj.AddSeriesReac2IncMatrix;
end;
end;
end;
-//*********Routine for extracting the Branch to Node incidence matrix***********
-//* The order depends on the way the lines, xfmr, series cap and reactors *
-//******************************************************************************
+
+// Routine for extracting the Branch to Node incidence matrix
+// The order depends on the way the lines, xfmr, series cap and reactors
procedure TSolutionObj.Calc_Inc_Matrix;
begin
- // If the sparse matrix obj doesn't exists creates it, otherwise deletes the content
+ // If the sparse matrix obj doesn't exists creates it, otherwise deletes the content
if IncMat = NIL then
IncMat := Tsparse_matrix.Create
else
@@ -1585,7 +1521,7 @@ procedure TSolutionObj.Calc_Inc_Matrix;
begin
temp_counter := 0;
ActiveIncCell[0] := 1; // Activates row 1 of the incidence matrix
- // Now we proceed to evaluate the link branches
+ // Now we proceed to evaluate the link branches
AddLines2IncMatrix; // Includes the Lines
AddXfmr2IncMatrix; // Includes the Xfmrs
AddSeriesCap2IncMatrix; // Includes Series Cap
@@ -1594,10 +1530,31 @@ procedure TSolutionObj.Calc_Inc_Matrix;
end;
end;
-{*******************************************************************************
-* This function delivers the Row index connected to the Column at the input *
-* Inside the B2N incidence Matrix *
-********************************************************************************}
+// This function returns the index of bus 1 with the Incidence
+// matrix for the given PDE
+function TSolutionObj.get_PDE_Bus1_Location(myPDE: String): Integer;
+var
+ i,
+ j: Integer;
+ myBUS: String;
+begin
+ with DSS.ActiveCircuit do
+ begin
+ SetElementActive(myPDE);
+ myBUS := ActiveCktElement.GetBus(2);
+ j := ansipos('.', myBus);
+ if j <> 0 then
+ myBUS := copy(myBUS, 0, j - 1);
+ for i := 0 to High(Inc_Mat_Cols) do
+ if Inc_Mat_Cols[i] = myBUS then
+ break;
+ Result := i;
+ end;
+
+end;
+
+// This function delivers the Row index connected to the Column at the input
+// Inside the B2N incidence Matrix
function TSolutionObj.get_IncMatrix_Row(Col: Integer): Integer;
var
Tflag: Boolean;
@@ -1615,10 +1572,8 @@ function TSolutionObj.get_IncMatrix_Row(Col: Integer): Integer;
end;
end;
-{*******************************************************************************
-* This function delivers the Column index connected to the Row at the input *
-* Inside the B2N incidence Matrix *
-********************************************************************************}
+// This function delivers the Column index connected to the Row at the input
+// Inside the B2N incidence Matrix
function TSolutionObj.get_IncMatrix_Col(Row: Integer): Integer;
var
Tflag: Boolean;
@@ -1642,11 +1597,11 @@ function TSolutionObj.get_IncMatrix_Col(Row: Integer): Integer;
end;
end;
-//*********Routine for extracting the Branch to Node incidence matrix***********
-//* Organized hierarchically. This routine also calculates the *
-//* Levels vector for defining the proximity of the bus to the circuit's *
-//* Backbone. To do it, this routine uses the CktTree class *
-//******************************************************************************
+// Routine for extracting the Branch to Node incidence matrix
+//
+// Organized hierarchically. This routine also calculates the
+// Levels vector for defining the proximity of the bus to the circuit's
+// Backbone. To do it, this routine uses the CktTree class
procedure TSolutionObj.Calc_Inc_Matrix_Org;
var
@@ -1668,15 +1623,15 @@ procedure TSolutionObj.Calc_Inc_Matrix_Org;
val, // Local Shared variable
nPDE: Integer; // PDElements index
begin
+ ZeroLevel := 0;
try
if DSS.ActiveCircuit <> NIL then
begin
-// TreeNm := FileRoot + 'TopoTree_Cols.csv'; // For debuging
topo := DSS.ActiveCircuit.GetTopology;
nLevels := 0;
nPDE := 0;
setlength(Inc_Mat_Cols, 0);
- //Init the spaser matrix
+ //Init the sparse matrix
if IncMat = NIL then
IncMat := Tsparse_matrix.Create
else
@@ -1689,8 +1644,8 @@ procedure TSolutionObj.Calc_Inc_Matrix_Org;
while Assigned(PDElem) do
begin
nLevels := topo.Level;
- PDE_Name := PDElem.ParentClass.Name + '.' + PDElem.Name;
-//******************Gets the buses to which the PDE is connected****************
+ PDE_Name := PDElem.FullName;
+ // Gets the buses to which the PDE is connected
with DSS.ActiveCircuit do
begin
DSS.ActiveCircuit.SetElementActive(PDE_Name);
@@ -1743,10 +1698,8 @@ procedure TSolutionObj.Calc_Inc_Matrix_Org;
PDElem := topo.GoForward;
end;
end;
-{*******************************************************************************
-* Now the levels array needs to be reprocessed to get the 0 level buses, *
-* they are on a continuous path from the feeder head to the feeder end *
-********************************************************************************}
+ // Now the levels array needs to be reprocessed to get the 0 level buses,
+ // they are on a continuous path from the feeder head to the feeder end
BusdotIdx := MaxIntValue(Inc_Mat_levels);
for i := 0 to length(Inc_Mat_levels) do
if Inc_Mat_levels[i] = BusdotIdx then
@@ -1760,7 +1713,7 @@ procedure TSolutionObj.Calc_Inc_Matrix_Org;
end;
Inc_Mat_levels[ZeroLevel] := 0;
end;
-//**********Normalize the branches of the level between zero level buses********
+ // **********Normalize the branches of the level between zero level buses********
BusdotIdx := 0;
j := 0;
ZeroLevel := 0;
@@ -1784,7 +1737,7 @@ procedure TSolutionObj.Calc_Inc_Matrix_Org;
Temp_Array[High(Temp_Array)] := Inc_Mat_levels[i];
end;
end;
-//************Verifies is something else was missing at the end*****************
+ // ************Verifies if something else was missing at the end*****************
if (ZeroLevel < (length(Inc_Mat_levels) - 1)) then
begin
BusdotIdx := 0; // Counter for defining the level
@@ -1818,10 +1771,8 @@ procedure TSolutionObj.Calc_Inc_Matrix_Org;
end;
end;
-{*******************************************************************************
-* This routine evaluates if the current location is the best or if its *
-* Necessary to move back one PDE just to cover a wider area *
-********************************************************************************}
+// This routine evaluates if the current location is the best or if its
+// Necessary to move back one PDE just to cover a wider area
function TSolutionObj.CheckLocationIdx(Idx: Integer): Integer;
begin
if Inc_Mat_Levels[Idx - 1] = 0 then
@@ -1829,15 +1780,12 @@ function TSolutionObj.CheckLocationIdx(Idx: Integer): Integer;
else
Result := idx;
end;
-// ===========================================================================================
+
procedure TSolutionObj.GetPCInjCurr;
+// Get inj currents from all enabled PC devices
var
pElem: TDSSCktElement;
-
-{ Get inj currents from all enabled PC devices }
-
begin
-
with DSS.ActiveCircuit do
begin
pElem := PCElements.First;
@@ -1849,37 +1797,28 @@ procedure TSolutionObj.GetPCInjCurr;
pElem := PCElements.Next;
end;
end;
-
end;
-procedure TSolutionObj.DumpProperties(F: TFileStream; complete: Boolean);
-
+procedure TSolutionObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i, j: Integer;
- // for dumping the matrix in compressed columns
+ // for dumping the matrix in compressed columns
p: Longword;
hY: NativeUInt;
nBus, nNZ: Longword;
ColPtr, RowIdx: array of Longword;
cVals: array of Complex;
begin
-
FSWriteln(F, '! OPTIONS');
- // Inherited DumpProperties(F,Complete);
+ // Inherited DumpProperties(F,Complete);
FSWriteln(F, '! NumNodes = ', IntToStr(DSS.ActiveCircuit.NumNodes));
- {WITH ParentClass Do
- FOR i := 1 to NumProperties Do
- Begin
- FSWriteln(F,'Set ',PropertyName^[i],'=',PropertyValue^[i]);
- End;
- }
- FSWriteln(F, 'Set Mode=', GetSolutionModeID(DSS));
- FSWriteln(F, 'Set ControlMode=', GetControlModeID(DSS));
- FSWriteln(F, 'Set Random=', GetRandomModeID(DSS));
+ FSWriteln(F, 'Set Mode=', DSS.SolveModeEnum.OrdinalToString(ord(DSS.ActiveCircuit.Solution.mode)));
+ FSWriteln(F, 'Set ControlMode=', DSS.ControlModeEnum.OrdinalToString(DSS.ActiveCircuit.Solution.Controlmode));
+ FSWriteln(F, 'Set Random=', DSS.RandomModeEnum.OrdinalToString(DSS.ActiveCircuit.Solution.RandomType));
FSWriteln(F, 'Set hour=', IntToStr(DynaVars.intHour));
FSWriteln(F, 'Set sec=', Format('%-g', [DynaVars.t]));
FSWriteln(F, 'Set year=', IntToStr(Year));
@@ -1891,7 +1830,7 @@ procedure TSolutionObj.DumpProperties(F: TFileStream; complete: Boolean);
FSWriteln(F, 'Set tolerance=', Format('%-g', [ConvergenceTolerance]));
FSWriteln(F, 'Set maxiterations=', IntToStr(MaxIterations));
FSWriteln(F, 'Set miniterations=', IntToStr(MinIterations));
- FSWriteln(F, 'Set loadmodel=', GetLoadModel(DSS));
+ FSWriteln(F, 'Set loadmodel=', DSS.DefaultLoadModelEnum.OrdinalToString(DSS.ActiveCircuit.solution.LoadModel));
FSWriteln(F, 'Set loadmult=', Format('%-g', [DSS.ActiveCircuit.LoadMultiplier]));
FSWriteln(F, 'Set Normvminpu=', Format('%-g', [DSS.ActiveCircuit.NormalMinVolts]));
@@ -1932,12 +1871,7 @@ procedure TSolutionObj.DumpProperties(F: TFileStream; complete: Boolean);
inc(i);
end;
FSWriteln(F, ')');
- case Algorithm of
- NORMALSOLVE:
- FSWriteln(F, 'Set algorithm=normal');
- NEWTONSOLVE:
- FSWriteln(F, 'Set algorithm=newton');
- end;
+ FSWriteln(F, 'Set algorithm=' + DSS.SolveAlgEnum.OrdinalToString(Algorithm));
FSWrite(F, 'Set Trapezoidal=');
FSWriteln(F, StrYorN(DSS.ActiveCircuit.TrapezoidalIntegration));
FSWriteln(F, 'Set genmult=', Format('%-g', [DSS.ActiveCircuit.GenMultiplier]));
@@ -1957,10 +1891,9 @@ procedure TSolutionObj.DumpProperties(F: TFileStream; complete: Boolean);
if Complete then
with DSS.ActiveCircuit do
begin
-
hY := Solution.hY;
- // get the compressed columns out of KLU
+ // get the compressed columns out of KLU
FactorSparseMatrix(hY); // no extra work if already done
GetNNZ(hY, @nNZ);
GetSize(hY, @nBus);
@@ -1974,7 +1907,7 @@ procedure TSolutionObj.DumpProperties(F: TFileStream; complete: Boolean);
FSWriteln(F, ' Row Col G B');
FSWriteln(F);
- // traverse the compressed column format
+ // traverse the compressed column format
for j := 0 to nBus - 1 do
begin /// the zero-based column
for p := ColPtr[j] to ColPtr[j + 1] - 1 do
@@ -1987,12 +1920,15 @@ procedure TSolutionObj.DumpProperties(F: TFileStream; complete: Boolean);
end;
function TSolutionObj.VDiff(i, j: Integer): Complex;
-
begin
- Result := Csub(NodeV^[i], NodeV^[j]); // V1-V2
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ if ADiakoptics and (DSS.Parent <> NIL) then
+ Result := VoltInActor1(i) - VoltInActor1(j) // V1-V2
+ else
+{$ENDIF}
+ Result := NodeV^[i] - NodeV^[j]; // V1-V2
end;
-
procedure TSolutionObj.WriteConvergenceReport(const Fname: String);
var
i: Integer;
@@ -2000,7 +1936,7 @@ procedure TSolutionObj.WriteConvergenceReport(const Fname: String);
sout: String;
begin
try
- F := TFileStream.Create(Fname, fmCreate);
+ F := TBufferedFileStream.Create(Fname, fmCreate);
FSWriteln(F);
FSWriteln(F, '-------------------');
@@ -2030,12 +1966,9 @@ procedure TSolutionObj.WriteConvergenceReport(const Fname: String);
FireOffEditor(DSS, Fname);
end;
-
end;
-// =========================================================================================== =
procedure TSolutionObj.SumAllCurrents;
-
var
pelem: TDSSCktElement;
@@ -2051,7 +1984,6 @@ procedure TSolutionObj.SumAllCurrents;
end;
end;
-// =========================================================================================== =
procedure TSolutionObj.DoControlActions;
var
XHour: Integer;
@@ -2087,15 +2019,11 @@ procedure TSolutionObj.DoControlActions;
end;
end;
end;
-
end;
-// =========================================================================================== =
procedure TSolutionObj.SampleControlDevices;
-
var
ControlDevice: TControlElem;
-
begin
with DSS.ActiveCircuit do
begin
@@ -2113,39 +2041,29 @@ procedure TSolutionObj.SampleControlDevices;
except
On E: Exception do
begin
- DoSimpleMsg(Format('Error Sampling Control Device "%s.%s" %s Error = %s', [ControlDevice.ParentClass.Name, ControlDevice.Name, CRLF, E.message]), 484);
+ DoSimpleMsg(DSS, 'Error Sampling Control Device "%s". Error = %s', [ControlDevice.FullName, E.message], 484);
raise EControlProblem.Create('Solution aborted.');
end;
end;
end;
-
end;
-// =========================================================================================== =
procedure TSolutionObj.Sample_DoControlActions;
-
-
begin
-
if ControlMode = CONTROLSOFF then
ControlActionsDone := TRUE
else
begin
-
SampleControlDevices;
DoControlActions;
- {This variable lets control devices know the bus list has changed}
+ // This variable lets control devices know the bus list has changed
DSS.ActiveCircuit.Control_BusNameRedefined := FALSE; // Reset until next change
end;
-
end;
procedure TSolutionObj.Set_Mode(const Value: TSolveMode);
-
-
begin
-
DynaVars.intHour := 0;
DynaVars.t := 0.0;
Update_dblHour;
@@ -2167,7 +2085,7 @@ procedure TSolutionObj.Set_Mode(const Value: TSolveMode);
SolutionInitialized := FALSE; // reinitialize solution when mode set (except dynamics)
PreserveNodeVoltages := FALSE; // don't do this unless we have to
SampleTheMeters := FALSE;
- // Reset defaults for solution modes
+ // Reset defaults for solution modes
case Dynavars.SolutionMode of
TSolveMode.PEAKDAY,
@@ -2266,44 +2184,26 @@ procedure TSolutionObj.Set_Mode(const Value: TSolveMode);
end;
end;
- {Moved here 9-8-2007 so that mode is changed before reseting monitors, etc.}
-
- // Reset Meters and Monitors
+ // Reset Meters and Monitors
DSS.MonitorClass.ResetAll;
DSS.EnergyMeterClass.ResetAll;
DoResetFaults(DSS);
DoResetControls(DSS);
-
end;
procedure TSolutionObj.AddInAuxCurrents(SolveType: Integer);
-
begin
- {FOR i := 1 to DSS.ActiveCircuit.NumNodes Do Caccum(Currents^[i], AuxCurrents^[i]);}
// For Now, only AutoAdd Obj uses this
-
if Dynavars.SolutionMode = TSolveMode.AUTOADDFLAG then
DSS.ActiveCircuit.AutoAddObj.AddCurrents(SolveType);
-
-end;
-
-procedure TSolutionObj.ZeroAuxCurrents;
-var
- i: Integer;
-begin
- for i := 1 to DSS.ActiveCircuit.NumNodes do
- AuxCurrents^[i] := CZERO;
end;
procedure TSolutionObj.Check_Fault_Status;
-
var
pFault: TFaultOBj;
-
begin
with DSS.ActiveCircuit do
begin
-
pFault := TFaultObj(Faults.First);
while pFault <> NIL do
begin
@@ -2311,42 +2211,13 @@ procedure TSolutionObj.Check_Fault_Status;
pFault := TFaultObj(Faults.Next);
end;
- end; {End With}
-end;
-
-
-{ This procedure is called for Solve Direct and any other solution method
- that does not get the injection currents for PC elements normally. In Dynamics mode,
- Generators are voltage sources ...
-
-Procedure TSolutionObj.GetMachineInjCurrents;
-
-Var
- pElem:TDSSCktElement;
-
-begin
- // do machines in Dynamics Mode
- IF IsDynamicModel THEN
- With DSS.ActiveCircuit DO Begin
-
- pElem := Generators.First;
- WHILE pElem<>nil Do Begin
- IF pElem.Enabled THEN pElem.InjCurrents; // uses NodeRef to add current into InjCurr Array;
- pElem := Generators.Next;
- End;
-
- End;
-
+ end;
end;
-}
function TSolutionObj.OK_for_Dynamics(const Value: TSolveMode): Boolean;
-
var
ValueIsDynamic: Boolean;
-
begin
-
Result := TRUE;
case Value of
@@ -2358,7 +2229,7 @@ function TSolutionObj.OK_for_Dynamics(const Value: TSolveMode): Boolean;
ValueIsDynamic := FALSE;
end;
- {When we go in and out of Dynamics mode, we have to do some special things}
+ // When we go in and out of Dynamics mode, we have to do some special things
if IsDynamicModel and not ValueIsDynamic then
InvalidateAllPCELEMENTS(DSS); // Force Recomp of YPrims when we leave Dynamics mode
@@ -2369,22 +2240,19 @@ function TSolutionObj.OK_for_Dynamics(const Value: TSolveMode): Boolean;
CalcInitialMachineStates(DSS) // set state variables for machines (loads and generators)
else
begin
- {Raise Error Message if not solved}
- DoSimpleMsg('Circuit must be solved in a non-dynamic mode before entering Dynamics or Fault study modes!' + CRLF +
- 'If you attempted to solve, then the solution has not yet converged.', 486);
+ // Raise Error Message if not solved
+ DoSimpleMsg(DSS, _('Circuit must be solved in a non-dynamic mode before entering Dynamics or Fault study modes!') + CRLF +
+ _('If you attempted to solve, then the solution has not yet converged.'), 486);
if DSS.In_ReDirect then
DSS.Redirect_Abort := TRUE; // Get outta here
Result := FALSE;
end;
end;
-
end;
function TSolutionObj.OK_for_Harmonics(const Value: TSolveMode): Boolean;
-
- {When we go in and out of Harmonics mode, we have to do some special things}
+// When we go in and out of Harmonics mode, we have to do some special things
begin
-
Result := TRUE;
if IsHarmonicModel and not ((Value = TSolveMode.HARMONICMODE) or (Value = TSolveMode.HARMONICMODET)) then
@@ -2408,14 +2276,12 @@ function TSolutionObj.OK_for_Harmonics(const Value: TSolveMode): Boolean;
end
else
begin
-
- DoSimpleMsg('Circuit must be solved in a fundamental frequency power flow or direct mode before entering Harmonics mode!', 487);
+ DoSimpleMsg(DSS, _('Circuit must be solved in a fundamental frequency power flow or direct mode before entering Harmonics mode!'), 487);
if DSS.In_ReDirect then
DSS.Redirect_Abort := TRUE; // Get outta here
Result := FALSE;
end;
end;
-
end;
procedure TSolutionObj.Set_Frequency(const Value: Double);
@@ -2445,15 +2311,6 @@ procedure TSolutionObj.Increment_time;
end;
end;
-procedure TSolutionObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := '';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
procedure TSolutionObj.Set_Year(const Value: Integer);
begin
if DSS.DIFilesAreOpen then
@@ -2479,11 +2336,10 @@ procedure TSolutionObj.SaveVoltages;
BusName: String;
sout: String;
begin
-
try
try
- F := TFileStream.Create(DSS.OutputDirectory {CurrentDSSDir} + DSS.CircuitName_ + 'SavedVoltages.Txt', fmCreate);
+ F := TBufferedFileStream.Create(DSS.OutputDirectory {CurrentDSSDir} + DSS.CircuitName_ + 'SavedVoltages.txt', fmCreate);
with DSS.ActiveCircuit do
for i := 1 to NumBuses do
@@ -2491,7 +2347,7 @@ procedure TSolutionObj.SaveVoltages;
BusName := BusList.NameOfIndex(i);
for j := 1 to Buses^[i].NumNodesThisBus do
begin
- Volts := NodeV^[Buses^[i].GetRef(j)];
+ Volts := NodeV^[Buses^[i].RefNo[j]];
WriteStr(sout, BusName, ', ', Buses^[i].GetNum(j): 0, Format(', %-.7g, %-.7g', [Cabs(Volts), CDang(Volts)]));
FSWriteln(F, sout);
end;
@@ -2500,7 +2356,7 @@ procedure TSolutionObj.SaveVoltages;
except
On E: Exception do
begin
- DoSimpleMsg('Error opening Saved Voltages File: ' + E.message, 488);
+ DoSimpleMsg(DSS, 'Error opening Saved Voltages File: %s', [E.message], 488);
Exit;
end;
end;
@@ -2508,50 +2364,52 @@ procedure TSolutionObj.SaveVoltages;
finally
FreeAndNil(F);
- DSS.GlobalResult := DSS.OutputDirectory {CurrentDSSDir} + DSS.CircuitName_ + 'SavedVoltages.Txt';
+ DSS.GlobalResult := DSS.OutputDirectory {CurrentDSSDir} + DSS.CircuitName_ + 'SavedVoltages.txt';
end;
-
end;
-{ ************* MAIN SOLVER CALL ************************}
+// ************* MAIN SOLVER CALL ************************
function TSolutionObj.SolveSystem(V: pNodeVArray): Integer;
-
var
- RetCode: Integer;
- iRes: Longword;
+ iRes: LongWord;
dRes: Double;
-
begin
+ Result := 0;
- {Note: NodeV[0] = 0 + j0 always. Therefore, pass the address of the element 1 of the array.
- }
+ // Note: NodeV[0] = 0 + j0 always. Therefore, pass the address of the element 1 of the array.
try
- RetCode := SolveSparseSet(hY, pComplexArray(@V^[1]), pComplexArray(@Currents^[1])); // Solve for present InjCurr
-{$IFDEF DSS_CAPI}
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ if not ADiakoptics or (DSS.Parent = NIL) then
+{$ENDIF}
+ Result := SolveSparseSet(hY, pComplexArray(@V^[1]), pComplexArray(@Currents^[1])) // Solve for present InjCurr
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ else
+ Result := SolveSparseSet(hY, pComplexArray(@V^[LocalBusIdx[0]]), pComplexArray(@Currents^[1])); // Solve for present InjCurr in Actor 1 context
+{$ELSE}
+ ;
+{$ENDIF}
+
if (DSS_CAPI_INFO_SPARSE_COND) then // Disabled by default with DSS C-API
begin
-{$ENDIF}
- // new information functions
+ // new information functions
GetFlops(hY, @dRes);
GetRGrowth(hY, @dRes);
GetRCond(hY, @dRes);
- // GetCondEst (hY, @dRes); // this can be expensive
-{$IFDEF DSS_CAPI}
+ // GetCondEst (hY, @dRes); // this can be expensive
+ GetSize(hY, @iRes);
+ GetNNZ(hY, @iRes);
+ GetSparseNNZ(hY, @iRes);
+ GetSingularCol(hY, @iRes);
end;
-{$ENDIF}
- GetSize(hY, @iRes);
- GetNNZ(hY, @iRes);
- GetSparseNNZ(hY, @iRes);
- GetSingularCol(hY, @iRes);
except
- On E: Exception do
- raise EEsolv32Problem.Create('Error Solving System Y Matrix. Sparse matrix solver reports numerical error: ' + E.Message);
+ On E: Exception do //Raise
+ begin
+ DoSimpleMsg(DSS, 'Error Solving System Y Matrix. Sparse matrix solver reports numerical error: %s', [E.Message], 0);
+ DSS.SolutionAbort := TRUE;
+ end;
end;
-
- Result := RetCode;
-
end;
procedure TSolutionObj.Update_dblHour;
@@ -2561,19 +2419,17 @@ procedure TSolutionObj.Update_dblHour;
procedure TSolutionObj.UpdateLoopTime;
begin
-
-// Update Loop time is called from end of time step cleanup
-// Timer is based on beginning of SolveSnap time
-
- {$IFDEF MSWINDOWS}
+ // Update Loop time is called from end of time step cleanup
+ // Timer is based on beginning of SolveSnap time
+ {$IFDEF MSWINDOWS}
QueryPerformanceCounter(LoopEndTime);
-{$ENDIF}
+ {$ELSE}
+ LoopEndTime := GetTickCount64;
+ {$ENDIF}
Step_Time_Elapsed := ((LoopEndtime - SolveStartTime) / CPU_Freq) * 1000000;
-
end;
procedure TSolutionObj.UpdateVBus;
-
// Save present solution vector values to buses
var
i, j: Integer;
@@ -2583,7 +2439,7 @@ procedure TSolutionObj.UpdateVBus;
with Buses^[i] do
if Assigned(Vbus) then
for j := 1 to NumNodesThisBus do
- VBus^[j] := NodeV^[GetRef(j)];
+ VBus^[j] := NodeV^[RefNo[j]];
end;
procedure TSolutionObj.RestoreNodeVfromVbus;
@@ -2595,260 +2451,159 @@ procedure TSolutionObj.RestoreNodeVfromVbus;
with Buses^[i] do
if Assigned(Vbus) then
for j := 1 to NumNodesThisBus do
- NodeV^[GetRef(j)] := VBus^[j];
-
+ NodeV^[RefNo[j]] := VBus^[j];
end;
function TSolutionObj.SolveYDirect: Integer;
-
-{ Solves present Y matrix with no injection sources except voltage and current sources }
-
+// Solves present Y matrix with no injection sources except voltage and current sources
begin
-
Result := 0;
-
- ZeroInjCurr; // Side Effect: Allocates InjCurr
- GetSourceInjCurrents;
- if IsDynamicModel then
- GetPCInjCurr; // Need this in dynamics mode to pick up additional injections
-
- SolveSystem(NodeV); // Solve with Zero injection current
-
-end;
-
-
-{$IFDEF DSS_CAPI_PM}
-//TODO: move DelFilesFromDir to Utilities, it's also used in Circuit
-{*******************************************************************************
-* Routine created to empty a recently created folder *
-********************************************************************************}
- {$IFDEF MSWINDOWS}
-procedure DelFilesFromDir(Directory, FileMask: String; DelSubDirs: Boolean);
-var
- SourceLst: String;
- FOS: TSHFileOpStruct;
-begin
- FillChar(FOS, SizeOf(FOS), 0);
- FOS.wFunc := FO_DELETE;
- SourceLst := Directory + PathDelim + FileMask + #0;
- FOS.pFrom := Pchar(SourceLst);
- if not DelSubDirs then
- FOS.fFlags := FOS.fFlags or FOF_FILESONLY;
- // Remove the next line if you want a confirmation dialog box
- FOS.fFlags := FOS.fFlags or FOF_NOCONFIRMATION;
- // Add the next line for a "silent operation" (no progress box)
- FOS.fFlags := FOS.fFlags or FOF_SILENT;
- SHFileOperation(FOS);
-end;
-
- {$ENDIF}
- {$IFDEF UNIX}
-procedure DeltreeDir(Directory: String);
-var
- Info: TSearchRec;
-begin
- if FindFirst(Directory + PathDelim + '*', faAnyFile and faDirectory, Info) = 0 then
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ if not ADiakoptics or (DSS.Parent <> NIL) then
begin
- repeat
- with Info do
- begin
- if (name = '.') or (name = '..') then
- continue;
- if (Attr and faDirectory) = faDirectory then
- begin
- DeltreeDir(Directory + PathDelim + Name)
- end
- else
- begin
- DeleteFile(Directory + PathDelim + Name);
- end;
- end;
- until FindNext(info) <> 0;
- end;
- rmdir(Directory);
-end;
-
-procedure DelFilesFromDir(Directory, FileMask: String; DelSubDirs: Boolean);
-var
- Info: TSearchRec;
- flags: Longint;
-begin
- if DelSubDirs then
- flags := faAnyFile and faDirectory
+{$ENDIF}
+ ZeroInjCurr; // Side Effect: Allocates InjCurr
+ GetSourceInjCurrents;
+ if IsDynamicModel then
+ GetPCInjCurr; // Need this in dynamics mode to pick up additional injections
+
+ SolveSystem(NodeV); // Solve with Zero injection current
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ end
else
- flags := faAnyFile;
-
- if FindFirst(Directory + PathDelim + FileMask, flags, Info) = 0 then
begin
- repeat
- with Info do
- begin
- if (name = '.') or (name = '..') then
- continue;
- if (Attr and faDirectory) = faDirectory then
- begin
- try
- DeltreeDir(Directory + PathDelim + Name)
- except
- Writeln('Could not remove directory ' + Directory + PathDelim + Name);
- end;
- end
- else
- begin
- DeleteFile(Directory + PathDelim + Name);
- end;
- end;
- until FindNext(info) <> 0;
+ ADiak_PCInj := False;
+ Solve_Diakoptics(DSS); // A-Diakoptics
end;
+{$ENDIF}
end;
- {$ENDIF}
-{*******************************************************************************
-* Used to create the OpenDSS Solver thread *
-********************************************************************************
-}
-
-constructor TSolver.Create(dssContext: TDSSContext; Susp: Boolean; local_CPU: Integer; CallBack: TInfoMessageCall; AEvent: TEvent);
-
-var
- Parallel: TParallel_Lib;
- Thpriority: String;
+{$IFDEF DSS_CAPI_PM}
+// Used to create the OpenDSS Solver thread
+constructor TSolver.Create(dssContext: TDSSContext; Susp: Boolean; local_CPU: Integer; AEvent: TEvent);
begin
DSS := dssContext;
- UIEvent := AEvent;
- FInfoProc := CallBack;
+ StatusEvent := AEvent;
FreeOnTerminate := FALSE;
- ActorMsg := TEvent.Create(NIL, TRUE, FALSE, '');
- MsgType := -1;
- ActorActive := TRUE;
+ // ActorID := ID;
+ MessageQueueEvent := TEvent.Create(NIL, TRUE, FALSE, '');
+ actorMessagesLock := SyncObjs.TCriticalSection.Create();
+ ActorIsActive := TRUE;
Processing := FALSE;
+ actorMessages := TQueue.Create;
inherited Create(Susp);
- {$IFDEF MSWINDOWS} // Only for windows
-// Parallel.Set_Process_Priority(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
- Parallel.Set_Thread_affinity(handle, local_CPU);
-// Parallel.Set_Thread_Priority(handle,THREAD_PRIORITY_TIME_CRITICAL);
- {$ELSE}
-// Parallel.Set_Thread_Priority(self,THREAD_PRIORITY_TIME_CRITICAL);
- Parallel.Set_Thread_affinity(handle, local_CPU);
- {$ENDIF}
-
-end;
-
-procedure TSolver.Send_Message(Msg: Integer);
-begin
- MsgType := Msg;
- ActorMsg.SetEvent;
+ if local_CPU >= 0 then
+ Set_Thread_affinity(handle, local_CPU);
+ // Parallel.Set_Thread_Priority(handle, THREAD_PRIORITY_TIME_CRITICAL);
end;
-procedure TSolver.Set_Processing(NVal: Boolean);
+destructor TSolver.Destroy;
begin
- Processing := NVal;
+ inherited destroy;
end;
-function TSolver.Get_Processing(): Boolean;
+// Send a message to the actor
+procedure TSolver.Send_Message(Msg: TActorMessage);
begin
- Result := Processing;
+ try
+ actorMessagesLock.Acquire();
+ actorMessages.Enqueue(Msg);
+ DSS.ActorStatus := TActorStatus.Busy;
+ DSS.ThreadStatusEvent.SetEvent();
+ MessageQueueEvent.SetEvent();
+ finally
+ actorMessagesLock.Release();
+ end;
end;
+// Returns the CPU assigned to the actor
function TSolver.Get_CPU(): Integer;
begin
Result := DSS.CPU;
end;
+// Sets the CPU assigned to the actor
procedure TSolver.Set_CPU(CPU: Integer);
-var
- Parallel: TParallel_Lib;
begin
DSS.CPU := CPU;
- Parallel.Set_Thread_affinity(handle, CPU);
+ Set_Thread_affinity(handle, CPU);
// Parallel.Set_Thread_Priority(handle,THREAD_PRIORITY_TIME_CRITICAL);
end;
-{*******************************************************************************
-* executes the selected solution algorithm *
-********************************************************************************
-}
-
+// Executes the selected solution algorithm
procedure TSolver.Execute;
var
-{$IFNDEF FPC}
- ScriptEd: TScriptEdit;
-{$ENDIF}
- i,
- j,
- idx: Integer;
- VSourceObj: TVsourceObj;
- Volts: Polar;
-
+ MsgType: TActorMessage;
begin
with DSS.ActiveCircuit, Solution do
- begin
- while ActorActive do
+ while ActorIsActive do
begin
- ActorMsg.WaitFor(INFINITE);
- ActorMsg.ResetEvent;
- Processing := TRUE;
-
- // Evaluates the incoming message
- case MsgType of
- SIMULATE: // Simulates the active ciruit on this actor
- try
- begin // Checks if this is the coordinator actor in A-Diakoptics mode
- if DSS.ADiakoptics and DSS.IsPrime and (not DSS.IsSolveAll) then
- begin
- Solve_Diakoptics(DSS)
- end
+ MessageQueueEvent.WaitFor(INFINITE);
+ DSS.ActorStatus := TActorStatus.Busy;
+ StatusEvent.SetEvent();
+ while True do
+ begin
+ try
+ actorMessagesLock.Acquire();
+ if actorMessages.Count = 0 then
+ break;
+
+ //TODO: peak and check if last message is EXIT?
+ MsgType := actorMessages.Dequeue();
+ finally
+ actorMessagesLock.Release();
+ end;
+ MessageQueueEvent.ResetEvent();
+
+ Processing := TRUE;
+ case MsgType of // Evaluates the incoming message
+ TActorMessage.SIMULATE: // Simulates the circuit on this actor
+ try
+ // Normal solution routine
+ case Dynavars.SolutionMode of
+ TSolveMode.SNAPSHOT:
+ SolveSnap;
+ TSolveMode.YEARLYMODE:
+ SolveYearly;
+ TSolveMode.DAILYMODE:
+ SolveDaily;
+ TSolveMode.DUTYCYCLE:
+ SolveDuty;
+ TSolveMode.DYNAMICMODE:
+ SolveDynamic;
+ TSolveMode.MONTECARLO1:
+ SolveMonte1;
+ TSolveMode.MONTECARLO2:
+ SolveMonte2;
+ TSolveMode.MONTECARLO3:
+ SolveMonte3;
+ TSolveMode.PEAKDAY:
+ SolvePeakDay;
+ TSolveMode.LOADDURATION1:
+ SolveLD1;
+ TSolveMode.LOADDURATION2:
+ SolveLD2;
+ TSolveMode.DIRECT:
+ SolveDirect;
+ TSolveMode.MONTEFAULT:
+ SolveMonteFault; // Monte Carlo Fault Cases
+ TSolveMode.FAULTSTUDY:
+ SolveFaultStudy;
+ TSolveMode.AUTOADDFLAG:
+ AutoAddObj.Solve;
+ TSolveMode.HARMONICMODE:
+ SolveHarmonic;
+ TSolveMode.GENERALTIME:
+ SolveGeneralTime;
+ TSolveMode.HARMONICMODET:
+ SolveHarmonicT; //Declares the Hsequential-time harmonics
else
- begin
- // Verifies if there is an A-Diakoptics simulation running to update the local Vsources
- if DSS.ADiakoptics and (not DSS.IsSolveAll) then
- Start_Diakoptics();
-
- // Normal solution routine
- case Dynavars.SolutionMode of
- TSolveMode.SNAPSHOT:
- SolveSnap;
- TSolveMode.YEARLYMODE:
- SolveYearly;
- TSolveMode.DAILYMODE:
- SolveDaily;
- TSolveMode.DUTYCYCLE:
- SolveDuty;
- TSolveMode.DYNAMICMODE:
- SolveDynamic;
- TSolveMode.MONTECARLO1:
- SolveMonte1;
- TSolveMode.MONTECARLO2:
- SolveMonte2;
- TSolveMode.MONTECARLO3:
- SolveMonte3;
- TSolveMode.PEAKDAY:
- SolvePeakDay;
- TSolveMode.LOADDURATION1:
- SolveLD1;
- TSolveMode.LOADDURATION2:
- SolveLD2;
- TSolveMode.DIRECT:
- SolveDirect;
- TSolveMode.MONTEFAULT:
- SolveMonteFault; // Monte Carlo Fault Cases
- TSolveMode.FAULTSTUDY:
- SolveFaultStudy;
- TSolveMode.AUTOADDFLAG:
- AutoAddObj.Solve;
- TSolveMode.HARMONICMODE:
- SolveHarmonic;
- TSolveMode.GENERALTIME:
- SolveGeneralTime;
- TSolveMode.HARMONICMODET:
- SolveHarmonicT; //Declares the Hsequential-time harmonics
- else
- DoSimpleMsg('Unknown solution mode.', 481);
- end;
+ DoSimpleMsg(DSS, _('Unknown solution mode.'), 481);
end;
- {$IFDEF MSWINDOWS}
+
+ {$IFDEF WINDOWS}
QueryPerformanceCounter(GEndTime);
{$ELSE}
GEndTime := GetTickCount64;
@@ -2857,171 +2612,383 @@ procedure TSolver.Execute;
Total_Time_Elapsed := Total_Time_Elapsed + Total_Solve_Time_Elapsed;
Processing := FALSE;
FMessage := '1';
- DSS.ActorStatus := TActorStatus.Idle;
-
- // If this is an A-Diakoptics actor reports the results to the coordinator (Actor 1)
- if DSS.ADiakoptics and not DSS.IsPrime then
- Notify_Main();
-
- // Sends a message to Actor Object (UI) to notify that the actor has finised
- UIEvent.SetEvent;
- {$IFDEF MSWINDOWS}
- if not DSS.ADiakoptics then
- begin
- if DSS.Parallel_enabled then
- if not IsDLL then
- queue(CallCallBack); // Refreshes the GUI if running asynchronously
- end
- else
+ // Sends a message to Actor Object (UI) to notify that the actor has finised
+ // UIEvent.SetEvent;
+ // if DSS.GetPrime().Parallel_enabled then
+ // if not IsDLL then
+ // queue(CallCallBack); // Refreshes the GUI if running asynchronously
+ except
+ On E: Exception do
begin
- if (DSS.Parallel_enabled and DSS.IsPrime) then
- if not IsDLL then
- queue(CallCallBack); // Refreshes the GUI if running asynchronously
+ FMessage := '1';
+ DSS.ActorStatus := TActorStatus.Idle;
+ StatusEvent.SetEvent();
+ DSS.SolutionAbort := TRUE;
+ // if DSS.GetPrime().Parallel_enabled then
+ // begin
+ // if not IsDLL then
+ // queue(CallCallBack); // Refreshes the GUI if running asynchronously
+ // end
+ // else
+ DoSimpleMsg(DSS, 'Error Encountered in Solve: %s', [E.Message], 482);
end;
- {$ENDIF}
end;
- except
- On E: Exception do
- begin
- FMessage := '1';
- DSS.ActorStatus := TActorStatus.Idle;
- DSS.SolutionAbort := TRUE;
- UIEvent.SetEvent;
- if not DSS.ADiakoptics then
- begin
- if DSS.Parallel_enabled then
- if not IsDLL then
- Queue(CallCallBack); // Refreshes the GUI if running asynchronously
- end
- else
- begin
- if (DSS.Parallel_enabled and DSS.IsPrime) then
- if not IsDLL then
- queue(CallCallBack); // Refreshes the GUI if running asynchronously
- end;
- if not DSS.Parallel_enabled then
- DoSimpleMsg('Error Encountered in Solve: ' + E.Message, 482);
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ TActorMessage.INIT_ADIAKOPTICS:
+ begin
+ if DSS.Parent <> NIL then
+ Start_Diakoptics; // Initializes the actor for Diakoptics (if needed)
+ IndexBuses;
+ end;
+ TActorMessage.SOLVE_AD1:
+ SolveAD(True); // Solves the model if the actor has PIE
+ TActorMessage.SOLVE_AD2:
+ SolveAD(False); // Complements the solution
+ TActorMessage.GETCTRLMODE:
+ begin
+ // Brings the control mode from actor 1
+ ControlMode := DSS.Parent.ActiveCircuit.Solution.ControlMode;
+ DefaultControlMode := ControlMode;
+ MaxControlIterations := DSS.Parent.ActiveCircuit.Solution.MaxControlIterations;
+ end;
+{$ENDIF}
+ TActorMessage.ZEROIVECTOR:
+ ZeroInjCurr;
+ TActorMessage.GETCURRINJ:
+ GetSourceInjCurrents;
+ TActorMessage.CALC_INJ_CURR:
+ GetPCInjCurr;
+ TActorMessage.DO_CTRL_ACTIONS:
+ begin
+ ControlActionsDone := FALSE;
+ Sample_DoControlActions;
+ try
+ actorMessagesLock.Acquire();
+ actorMessages.enqueue(TActorMessage.CHECK_FAULT);
+ actorMessages.enqueue(TActorMessage.CHECKYBUS);
+ finally
+ actorMessagesLock.Release();
end;
end;
- EXIT_ACTOR: // Terminates the thread
- begin
- ActorActive := FALSE;
- end
- else // I don't know what the message is
- DoSimpleMsg('Unknown Message.', 7010);
- end;
+ TActorMessage.CHECK_FAULT:
+ Check_Fault_Status;
+ TActorMessage.CHECKYBUS:
+ if SystemYChanged {$IFDEF DSS_CAPI_INCREMENTAL_Y}or (IncrCktElements.Count <> 0){$ENDIF} then
+ BuildYMatrix(DSS, WHOLEMATRIX, FALSE); // Does not realloc V, I
+ TActorMessage.EXIT_ACTOR: // Terminates the thread
+ begin
+ ActorIsActive := FALSE;
+ try
+ actorMessagesLock.Acquire();
+ actorMessages.Clear();
+ finally
+ actorMessagesLock.Release();
+ end;
+ end;
+ else
+ // I don't know what the message is
+ DoSimpleMsg(DSS, _('Unknown Message.'), 7010);
+ end;
+
+ end; // while actorMessages.Count > 0
+ DSS.ActorStatus := TActorStatus.Idle;
+ StatusEvent.SetEvent();
+ end; // while ActorIsActive
+end;
+
+procedure TSolver.DoTerminate; // Is the end of the thread
+var
+ ex: TObject;
+begin
+ ActorIsActive := FALSE;
+ Processing := FALSE;
+ DSS.ActorStatus := TActorStatus.Idle;
+ StatusEvent.SetEvent();
+ MessageQueueEvent.Free;
+ actorMessagesLock.Free;
+ actorMessages.Free;
+ inherited;
+end;
+{$ENDIF} //DSS_CAPI_PM
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+function TSolutionObj.SolveAD(Initialize: Boolean): Integer; // solves a step for Adiakoptics locally
+begin
+ Result := 0;
+ if Initialize then
+ begin
+ ZeroInjCurr;
+ GetSourceInjCurrents; // sources
+ if ADiak_PCInj then
+ begin
+ LoadsNeedUpdating := TRUE; // Force the loads to update at least once
+ GetPCInjCurr // Get the injection currents from all the power conversion devices and feeders
+ end
+ else
+ if IsDynamicModel or IsHarmonicModel then
+ begin
+ LoadsNeedUpdating := TRUE; // Force the loads to update at least once
+ GetPCInjCurr; // for direct solve
end;
- end;
+ // The above call could change the primitive Y matrix, so have to check
+ if SystemYChanged {$IFDEF DSS_CAPI_INCREMENTAL_Y}or (DSS.ActiveCircuit.IncrCktElements.Count <> 0){$ENDIF} then
+ BuildYMatrix(DSS, WHOLEMATRIX, FALSE); // Does not realloc V, I
+
+ if UseAuxCurrents then
+ AddInAuxCurrents(NORMALSOLVE);
+ end
+ else
+ UpdateISrc;
+
+ // Solve for voltages -- Note:NodeV[0] = 0 + j0 always
+ SolveSystem(DSS.Parent.ActiveCircuit.Solution.NodeV);
+ LoadsNeedUpdating := FALSE;
+ LastSolutionWasDirect := TRUE;
+ DSS.ActiveCircuit.IsSolved := TRUE;
end;
-procedure TSolver.CallCallBack;
+procedure TSolutionObj.SendCmd2Actors(Msg: Integer);
+var
+ i: Integer;
+ ChDSS: TDSSContext;
begin
- if Assigned(FInfoProc) then
- FInfoProc(FMessage);
+ for i := 2 to DSS.NumOfActors do
+ begin
+ ChDSS := DSS.Children[i - 1];
+ ChDSS.ActorStatus := TActorStatus.Busy;
+ if ChDSS.ActorThread <> NIL then
+ ChDSS.ActorThread.Send_Message(Msg);
+ end;
+ Wait4Actors(DSS, AD_ACTORS);
end;
// Initializes the variables of the A-Diakoptics worker
procedure TSolver.Start_Diakoptics();
var
- row,
- j,
- i: Integer;
- VSource: TVsourceObj;
- Volts: Polar;
- CNum: Complex;
- PrimeCircuit: TDSSCircuit;
+ jj: Integer;
+ VSourceObj: TVsourceObj;
+ BusName: String;
+ myPDEList: ArrayOfString;
begin
- PrimeCircuit := DSS.GetPrime().ActiveCircuit;
-
with DSS.ActiveCircuit, Solution do
begin
- j := PrimeCircuit.Ic.NZero - 1; // Brings the number of Non-Zero elements from Ic
- if j > 0 then
+ // Select the main voltage source
+ VSourceObj := DSS.VsourceClass.ElementList.First;
+ // Gets the name of the branch directly connected to the feeder head to remove it
+ // (applies to all actors but actor 2 - first chunk of the system)
+ BusName := VSourceObj.GetBus(1);
+ jj := ansipos('.', BusName); // removes the dot
+ if (jj > 0) then
+ BusName := BusName.Substring(0, (jj - 1));
+ SetActiveBus(DSS, BusName); // Activates the Bus
+ myPDEList := getPDEatBus(BusList.NameOfIndex(ActiveBusIndex));
+ // Disables the link branch
+ DSS.DssExecutive.Command := myPDEList[0] + '.enabled=False'; //TODO: rewrite
+ // Now disables all the VSources added artificially
+ while VSourceObj <> NIL do
begin
- // Clears the local Ic vector
- for i := 1 to NumNodes do
- Ic_Local[i] := cZERO; // probably not necessary
+ BusName := AnsiLowerCase(VSourceObj.Name);
+ if (BusName = 'source') then
+ VSourceObj.Enabled := FALSE // Disables the artificial VSource phase 1
+ else
+ if (BusName = 'vph_2') then
+ VSourceObj.Enabled := FALSE // Disables the artificial VSource phase 2
+ else
+ if (BusName = 'vph_3') then
+ VSourceObj.Enabled := FALSE; // Disables the artificial VSource phase 3
+
+ VSourceObj := DSS.VsourceClass.ElementList.Next;
+ end;
+ end;
+end;
+
+// // Uploads the local voltage array in the masters
+// // using the index map obtained in previous steps
+// procedure TSolutionObj.UploadV2Master;
+// var
+// idx,
+// i: Integer;
+// begin
+// with DSS.ActiveCircuit do
+// begin
+// for i := 1 to NumNodes do
+// begin
+// idx := LocalBusIdx[i - 1];
+// DSS.Parent.ActiveCircuit.Solution.NodeV^[idx] := Solution.NodeV^[i];
+// end;
+// end;
+// end;
+
+// Returns the voltage at the node given at NodeIdx in
+// context of actor 1 (A-Diakoptics)
+function TSolutionObj.VoltInActor1(NodeIdx: Integer): Complex;
+begin
+ if NodeIdx <> 0 then
+ NodeIdx := NodeIdx + (LocalBusIdx[0] - 1);
+ // In the context of actor 1
+ Result := DSS.Parent.ActiveCircuit.Solution.NodeV^[NodeIdx];
+end;
+
+// Updates the local ISources using the data obtained
+// for Ic in actor 1
+procedure TSolutionObj.UpdateISrc;
+var
+ idx,
+ i: Integer;
+ Found: Boolean;
+ myCmplx: Complex;
- // Brings the section of the vector needed for this actor
- for i := 0 to j do
+begin
+ Found := FALSE;
+ with DSS.ActiveCircuit, Solution do
+ begin
+ for i := 0 to (AD_IBus.Count - 1) do
+ begin
+ for idx := 0 to (DSS.Parent.ActiveCircuit.Ic.NZero - 1) do
begin
- if PrimeCircuit.Ic.CData[i].row >= VIndex then
+ if DSS.Parent.ActiveCircuit.Ic.CData[idx].Row = AD_ISrcIdx.Items[i] then
begin
- row := PrimeCircuit.Ic.CData[i].row;
- Ic_Local[row - VIndex + 1] := PrimeCircuit.Ic.CData[i].Value;
+ Found := TRUE;
+ break;
end;
end;
-
- // Solves to find the total solution
- SolveSparseSet(hY, @Node_dV[1], @Ic_Local[1]);
-
- // Sends the total voltage for this part to the coordinator
+ if Found then
+ begin
+ // Adds the present current with the adjustment
+ myCmplx := DSS.Parent.ActiveCircuit.Ic.CData[idx].Value * (-1.0);
+ Currents^[AD_IBus.Items[i]] := myCmplx + Currents^[AD_IBus.Items[i]];
+ end; // Otherwise is just another ISource in the zone
+ end;
+ end;
+end;
+// Checks if the actor has power injection Obj
+// function TSolver.HasInjObj(): Boolean;
+// var
+// ListSize,
+// jj: Integer;
+// VSourceObj: TVsourceObj;
+// ISourceObj: TIsourceObj;
+// begin
+// if ActorID = 2 then
+// Result := TRUE
+// else
+// begin
+// Result := FALSE;
+// with DSS.ActiveCircuit, Solution do
+// begin
+// // Starts looking for VSource
+// VSourceObj := DSS.VsourceClass.ElementList.First;
+// while VSourceObj <> NIL do
+// begin
+// if VSourceObj.enabled then
+// begin
+// Result := TRUE;
+// break;
+// end;
+// VSourceObj := DSS.VsourceClass.ElementList.Next;
+// end;
+// if not Result then
+// begin
+// // Goes for ISources
+// ISourceObj := DSS.IsourceClass.ElementList.First;
+// while ISourceObj <> NIL do
+// begin
+// if ISourceObj.enabled then
+// begin
+// Result := TRUE;
+// break;
+// end;
+// ISourceObj := DSS.IsourceClass.ElementList.Next;
+// end;
+// end;
+// end;
+// end;
+// end;
+
+// locates the local buses into actor 1's bus array
+procedure TSolver.IndexBuses();
+var
+ Found: Boolean;
+ k,
+ i,
+ j: Integer;
+ myBus: String;
+ LclBus,
+ SrcBus: array of String;
+begin
+ // First, get the list of buses in actor 1
+ setlength(SrcBus, 1);
+ if DSS.Parent.ActiveCircuit <> NIL then
+ begin
+ with DSS.Parent.ActiveCircuit do
+ begin
for i := 1 to NumNodes do
begin
- CNum := csub(NodeV[i], Node_dV[i]);
- PrimeCircuit.Solution.NodeV[i + VIndex] := CNum;
+ with MapNodeToBus^[i] do
+ SrcBus[high(SrcBus)] := Format('%s.%-d', [AnsiUpperCase(BusList.NameOfIndex(Busref)), NodeNum]);
+ setlength(SrcBus, (length(SrcBus) + 1));
end;
-
- // Sets the voltage at the feeder head
- VSource := DSS.VSourceClass.ElementList.First;
- for i := 1 to 3 do
+ end;
+ end;
+ // rebuilds the Y matrix to relocate the local buses
+ BuildYMatrix(DSS, WHOLEMATRIX, TRUE); // Side Effect: Allocates V
+ // Then, get the list of buses in my actor
+ setlength(LclBus, 1);
+ if DSS.ActiveCircuit <> NIL then
+ begin
+ with DSS.ActiveCircuit do
+ begin
+ for i := 1 to NumNodes do
begin
- CNum := cadd(NodeV[i], Node_dV[i]);
- Volts := ctopolardeg(CNum);
- VSource.kVBase := Volts.mag / 1000; // is in kV
- VSource.Angle := Volts.ang;
- VSource := DSS.VSourceClass.ElementList.Next;
+ with MapNodeToBus^[i] do
+ LclBus[high(LclBus)] := Format('%s.%-d', [AnsiUpperCase(BusList.NameOfIndex(Busref)), NodeNum]);
+ setlength(LclBus, (length(LclBus) + 1));
end;
end;
end;
-end;
-
-procedure TSolver.Notify_Main;
-var
- CNum: Complex;
- i, j,
- idx: Integer;
- PrimeCircuit: TDSSCircuit;
-begin
- PrimeCircuit := DSS.GetPrime().ActiveCircuit;
-
- // Will do something
- with DSS.ActiveCircuit, Solution do
+ // Initializes the bus index vector
+ with DSS.ActiveCircuit.solution do
begin
- i := NumNodes;
- for idx := 1 to i do
+ Setlength(LocalBusIdx, length(LclBus) - 1);
+ for i := 0 to High(LocalBusIdx) do
begin
- // if it doesn't includes any power injection element (Isource, VSource)
- // returns dV to correct the interconnection equation
- if not DSS.IsPrime then
- CNum := csub(NodeV^[idx], Node_dV^[idx])
- else
- CNum := NodeV^[idx];
-
- PrimeCircuit.V_0.Insert((idx + VIndex - 1), 0, CNum);
+ for j := 0 to High(SrcBus) do
+ if LclBus[i] = SrcBus[j] then
+ break;
+ LocalBusIdx[i] := j + 1;
end;
- end;
-end;
-
-procedure TSolver.DoTerminate; // Is the end of the thread
-var
- ex: TObject;
-begin
- ActorActive := FALSE;
- Processing := FALSE;
- DSS.ActorStatus := TActorStatus.Idle;
- UIEvent.SetEvent;
- ActorMsg.Free;
-// FreeAndNil(UIEvent);
- inherited;
-end;
+ // Initializes the list for easy accessing the ADiakoptics Isources
+ if AD_IBus = NIL then
+ AD_IBus := TList.Create
+ else
+ AD_IBus.Clear;
+ if AD_ISrcIdx = NIL then
+ AD_ISrcIdx := TList.Create
+ else
+ AD_ISrcIdx.Clear;
+ // Locates the ISource used for the local ADiakoptics algorithm
+ for j := 0 to (DSS.Parent.ActiveCircuit.Contours.NZero - 1) do
+ begin
+ myBus := SrcBus[DSS.Parent.ActiveCircuit.Contours.CData[j].Row];
+ // checks if the bus in in this circuit
+ Found := FALSE;
+ for k := 0 to High(LclBus) do
+ begin
+ if LclBus[k] = myBus then
+ begin
+ Found := TRUE;
+ break;
+ end;
+ end;
+ if Found then // If found, add it to the indexed list
+ begin
+ AD_IBus.Add(k + 1);
+ AD_ISrcIdx.Add(DSS.Parent.ActiveCircuit.Contours.CData[j].Row);
+ end;
+ end;
-destructor TSolver.Destroy;
-begin
- inherited destroy;
+ end;
end;
-
-{$ENDIF} //DSS_CAPI_PM
+{$ENDIF}
end.
diff --git a/src/Common/SolutionAlgs.pas b/src/Common/SolutionAlgs.pas
index 663f6cc79..0cab52553 100644
--- a/src/Common/SolutionAlgs.pas
+++ b/src/Common/SolutionAlgs.pas
@@ -7,14 +7,7 @@
----------------------------------------------------------
}
-{ Solution Algorithms}
-
-{
- 9-20-00 Added SolveDynamic
-
- 1/22/01 Added SolutionAbort Check wherever solution in a potentially long loop
- 4/2/04 Updated SolutionAbort to work through redirect files and long scripts
-}
+// Solution Algorithms
interface
@@ -60,17 +53,13 @@ implementation
uses
DSSGlobals,
-{$IFDEF FPC}
CmdForms,
-{$ELSE}
- DSSForms,
-{$ENDIF}
Utilities,
SysUtils,
MathUtil,
Math,
Fault,
- uComplex,
+ UComplex, DSSUcomplex,
YMatrix,
Spectrum,
Vsource,
@@ -153,7 +142,7 @@ function TSolutionAlgs.SolveYearly: Integer;
ShowPctProgress(0);
{$ENDIF}
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
try
IntervalHrs := DynaVars.h / 3600.0; // needed for energy meters and storage elements
@@ -207,7 +196,7 @@ function TSolutionAlgs.SolveDaily: Integer;
begin
Result := 0;
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
// t:=0.0;
// DSS.MonitorClass.ResetAll;
@@ -263,7 +252,7 @@ function TSolutionAlgs.SolvePeakDay: Integer;
begin
Result := 0;
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
DynaVars.t := 0.0;
@@ -318,7 +307,7 @@ function TSolutionAlgs.SolveDuty: Integer;
ShowPctProgress(0);
{$ENDIF}
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
// t:=0.0;
// DSS.MonitorClass.ResetAll;
@@ -367,7 +356,7 @@ function TSolutionAlgs.SolveGeneralTime: Integer;
begin
Result := 0;
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
IntervalHrs := DynaVars.h / 3600.0; // needed for energy meters and storage devices
for N := 1 to NumberOfTimes do
@@ -417,7 +406,7 @@ function TSolutionAlgs.SolveDynamic: Integer;
begin
Result := 0;
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
try
SolutionInitialized := TRUE; // If we're in dynamics mode, no need to re-initialize.
@@ -457,7 +446,7 @@ function TSolutionAlgs.SolveMonte1: Integer;
begin
Result := 0;
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
try
LoadMultiplier := 1.0; // Always set with prop in case matrix must be rebuilt
@@ -505,7 +494,6 @@ function TSolutionAlgs.SolveMonte1: Integer;
{$ENDIF}
end;
end;
-
end;
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -519,7 +507,7 @@ function TSolutionAlgs.SolveMonte2: Integer;
begin
Result := 0;
- with DSS.ActiveCircuit, DSS.ActiveCircuit.solution do
+ with DSS.ActiveCircuit, Solution do
begin
try
DynaVars.t := 0.0;
@@ -603,7 +591,7 @@ function TSolutionAlgs.SolveMonte3: Integer;
begin
Result := 0;
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
// Time must be set beFore entering this routine
try
@@ -627,7 +615,6 @@ function TSolutionAlgs.SolveMonte3: Integer;
for N := 1 to NumberOfTimes do
if not DSS.SolutionAbort then
begin
-
// Always set LoadMultiplier WITH prop in case matrix must be rebuilt
case Randomtype of
UNIFORM:
@@ -680,12 +667,12 @@ function TSolutionAlgs.SolveLD1: Integer;
begin
Result := 0;
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
try
if LoadDurCurveObj = NIL then
begin
- DoSimpleMsg('Load Duration Curve Not Defined (Set LDCurve=... command). Cannot perForm solution.', 470);
+ DoSimpleMsg(DSS, _('Load Duration Curve Not Defined (Set LDCurve=... command). Cannot perForm solution.'), 470);
Exit;
end;
@@ -709,7 +696,6 @@ function TSolutionAlgs.SolveLD1: Integer;
with DynaVars do
for i := 1 to Ndaily do
begin
-
// Set the time
Increment_time;
@@ -719,7 +705,6 @@ function TSolutionAlgs.SolveLD1: Integer;
begin
for N := 1 to LoadDurCurveObj.NumPoints do
begin
-
LoadMultiplier := LoadDurCurveObj.Mult(N); // Always set LoadMultiplier with prop in case matrix must be rebuilt
// Adjust meter interval to interval on value of present Load-Duration Curve
IntervalHrs := LoadDurCurveObj.PresentInterval;
@@ -777,11 +762,11 @@ function TSolutionAlgs.SolveLD2: Integer;
begin
Result := 0;
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
if LoadDurCurveObj = NIL then
begin
- DoSimpleMsg('Load Duration Curve Not Defined (Set LDCurve=... command). Cannot perForm solution.', 471);
+ DoSimpleMsg(DSS, _('Load Duration Curve Not Defined (Set LDCurve=... command). Cannot perForm solution.'), 471);
Exit;
end;
@@ -808,7 +793,6 @@ function TSolutionAlgs.SolveLD2: Integer;
for N := 1 to LoadDurCurveObj.NumPoints do
begin
-
// Adjust meter interval to interval on value of present Load-Duration Curve
LoadMultiplier := LoadDurCurveObj.Mult(N); // Always set LoadMultiplier WITH prop in case matrix must be rebuilt
IntervalHrs := LoadDurCurveObj.PresentInterval;
@@ -869,7 +853,7 @@ function TSolutionAlgs.SolveMonteFault: Integer;
begin
Result := 0;
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
try
LoadModel := ADMITTANCE; // All Direct solution
@@ -910,10 +894,8 @@ function TSolutionAlgs.SolveMonteFault: Integer;
{$ENDIF}
end;
end;
-
end;
-{--------------------------------------------------------------------------}
procedure TSolutionAlgs.AllocateAllSCParms;
var
i: Integer;
@@ -926,7 +908,6 @@ procedure TSolutionAlgs.AllocateAllSCParms;
end;
-{--------------------------------------------------------------------------}
procedure TSolutionAlgs.ComputeIsc;
{ Compute Isc at all buses for current values of Voc and Ysc }
var
@@ -942,59 +923,48 @@ procedure TSolutionAlgs.ComputeIsc;
end;
end;
-
-{--------------------------------------------------------------------------}
procedure TSolutionAlgs.ComputeYsc(iB: Integer);
-
-{Compute YSC for I-th bus}
-{Assume InjCurr is zeroed}
-
+// Compute YSC for I-th bus
+// Assume InjCurr is zeroed
var
i,
j,
ref1: Integer;
-
begin
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
with Buses^[iB] do
begin
Zsc.Clear;
for i := 1 to NumNodesThisBus do
begin
- ref1 := GetRef(i);
+ ref1 := RefNo[i];
if ref1 > 0 then
begin
Currents^[ref1] := cONE;
- {SparseSet expects 1st element of voltage array, not 0-th element}
+ // SparseSet expects 1st element of voltage array, not 0-th element
if SolveSparseSet(hYsystem, pComplexArray(@NodeV^[1]), pComplexArray(@Currents^[1])) < 1 then
raise EEsolv32Problem.Create('Error Solving System Y Matrix in ComputeYsc. Problem with Sparse matrix solver.');
- {Extract Voltage Vector = column of Zsc}
+ // Extract Voltage Vector = column of Zsc
for j := 1 to NumNodesThisBus do
begin
- Zsc.SetElement(j, i, NodeV^[GetRef(j)]);
+ Zsc.SetElement(j, i, NodeV^[RefNo[j]]);
end;
Currents^[Ref1] := cZERO;
- end; {IF ref...}
+ end;
end;
Ysc.CopyFrom(Zsc);
- Ysc.invert; {Save as admittance}
+ Ysc.invert; // Save as admittance
end;
end;
end;
-
-{--------------------------------------------------------------------------}
procedure TSolutionAlgs.ComputeAllYsc;
var
iB, j: Integer;
-
-
begin
-
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
-
for j := 1 to NumNodes do
Currents^[j] := cZERO;
@@ -1015,7 +985,6 @@ procedure TSolutionAlgs.ComputeAllYsc;
end;
end;
-//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure TSolutionAlgs.DisableAllFaults;
begin
with DSS.ActiveCircuit do
@@ -1029,11 +998,7 @@ procedure TSolutionAlgs.DisableAllFaults;
end;
end;
-
-//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function TSolutionAlgs.SolveFaultStudy: Integer;
-
-
begin
Result := 0;
@@ -1080,21 +1045,20 @@ function TSolutionAlgs.SolveFaultStudy: Integer;
// Now should have all we need to make a short circuit report
end;
-//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure TSolutionAlgs.AddFrequency(var FreqList: pDoublearray; var NumFreq, MaxFreq: Integer; F: Double);
-{Add unique Frequency, F to list in ascending order, reallocating if necessary}
+// Add unique Frequency, F to list in ascending order, reallocating if necessary
var
i, j: Integer;
begin
- {See if F is in List}
+ // See if F is in List
for i := 1 to NumFreq do
begin
- {Allow a little tolerance (0.1 hz) for the Frequency for round off error}
+ // Allow a little tolerance (0.1 hz) for the Frequency for round off error
if Abs(F - FreqList^[i]) < 0.1 then
Exit; // Already in List, nothing to do
end;
- {OK, it's not in list, so let's Add it}
+ // OK, it's not in list, so let's Add it
Inc(NumFreq);
if NumFreq > MaxFreq then
begin // Let's make a little more room
@@ -1102,7 +1066,7 @@ procedure TSolutionAlgs.AddFrequency(var FreqList: pDoublearray; var NumFreq, Ma
ReallocMem(FreqList, SizeOf(FreqList^[1]) * MaxFreq);
end;
- {Let's add it in ascending order}
+ // Let's add it in ascending order
for i := 1 to NumFreq - 1 do
begin
if F < FreqList^[i] then
@@ -1115,17 +1079,15 @@ procedure TSolutionAlgs.AddFrequency(var FreqList: pDoublearray; var NumFreq, Ma
end;
end;
- {If we fall through, tack it on to the end}
+ // If we fall through, tack it on to the end
FreqList^[NumFreq] := F;
end;
-//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function TSolutionAlgs.GetSourceFrequency(pc: TPCElement): Double; // TODO - applicable to VCCS?
var
pVsrc: TVsourceObj;
pIsrc: TIsourceObj;
begin
-
if Comparetext(pc.DSSClassName, 'vsource') = 0 then
begin
pVsrc := pc as TVsourceObj;
@@ -1136,10 +1098,8 @@ function TSolutionAlgs.GetSourceFrequency(pc: TPCElement): Double; // TODO - app
pIsrc := pc as TIsourceObj;
Result := pIsrc.srcFrequency;
end;
-
end;
-//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure TSolutionAlgs.CollectAllFrequencies(var FreqList: pDoubleArray; var NumFreq: Integer);
var
@@ -1150,23 +1110,23 @@ procedure TSolutionAlgs.CollectAllFrequencies(var FreqList: pDoubleArray; var Nu
f: Double;
begin
- {Make a List of all frequencies in Use}
+ // Make a List of all frequencies in Use
- {accumulate all unique Frequencies}
+ // accumulate all unique Frequencies
MaxFreq := 20; // Initial List size
NumFreq := 0;
Reallocmem(FreqList, Sizeof(FreqList^[1]) * MaxFreq);
with DSS.ActiveCircuit do
begin
- {Check Sources -- each could have a different base frequency}
+ // Check Sources -- each could have a different base frequency
p := Sources.First;
while p <> NIL do
begin
if p.Enabled then
- if DSS.SpectrumClass.Find(p.Spectrum) <> NIL then
+ if p.SpectrumObj <> NIL then
begin
- pSpectrum := DSS.SpectrumClass.GetActiveObj;
+ pSpectrum := p.SpectrumObj;
f := GetSourceFrequency(p);
for j := 1 to pSpectrum.NumHarm do
begin
@@ -1177,8 +1137,8 @@ procedure TSolutionAlgs.CollectAllFrequencies(var FreqList: pDoubleArray; var Nu
end;
end;
- {Mark Spectra being used}
- {Check loads and generators - these are assumed to be at fundamental frequency}
+ // Mark Spectra being used
+ // Check loads and generators - these are assumed to be at fundamental frequency
SpectrumInUse := AllocMem(SizeOf(Integer) * DSS.SpectrumClass.ElementCount); //Allocate and zero
with DSS.ActiveCircuit do
begin
@@ -1186,15 +1146,15 @@ procedure TSolutionAlgs.CollectAllFrequencies(var FreqList: pDoubleArray; var Nu
while p <> NIL do
begin
if p.enabled then
- if DSS.SpectrumClass.Find(p.Spectrum) <> NIL then
+ if (p.SpectrumObj <> NIL) and (DSS.SpectrumClass.Find(p.SpectrumObj.Name) <> NIL) then
begin
SpectrumInUse^[DSS.SpectrumClass.Active] := 1;
end;
p := PCelements.Next;
end;
- end; {With}
+ end;
- {Add marked Spectra to list}
+ // Add marked Spectra to list
for i := 1 to DSS.SpectrumClass.ElementCount do
begin
if SpectrumInUse^[i] = 1 then
@@ -1211,8 +1171,6 @@ procedure TSolutionAlgs.CollectAllFrequencies(var FreqList: pDoubleArray; var Nu
ReallocMem(SpectrumInUse, 0);
end;
-
-//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function TSolutionAlgs.SolveHarmonic: Integer;
var
FrequencyList: pDoubleArray;
@@ -1228,7 +1186,7 @@ function TSolutionAlgs.SolveHarmonic: Integer;
ProgressCaption('Performing Harmonic Solution');
{$ENDIF}
- with DSS.ActiveCircuit, DSS.ActiveCircuit.solution do
+ with DSS.ActiveCircuit, Solution do
begin
try
@@ -1241,7 +1199,7 @@ function TSolutionAlgs.SolveHarmonic: Integer;
DSS.MonitorClass.SampleAll; // Store the fundamental frequency in the monitors
- { Get the list of Harmonic Frequencies to solve at}
+ // Get the list of Harmonic Frequencies to solve at
if DoAllHarmonics then
CollectAllFrequencies(FrequencyList, NFreq) // Allocates FrequencyList
else
@@ -1254,7 +1212,6 @@ function TSolutionAlgs.SolveHarmonic: Integer;
for i := 1 to NFreq do
begin
-
Frequency := FrequencyList^[i]; // forces rebuild of SystemY
if Abs(Harmonic - 1.0) > EPSILON then
begin // Skip fundamental
@@ -1270,7 +1227,7 @@ function TSolutionAlgs.SolveHarmonic: Integer;
// Storage devices are assumed to stay the same since there is no time variation in this mode
end;
- end; {FOR}
+ end;
{$IFDEF DSS_CAPI_PM}
DSS.ActorPctProgress := 100;
@@ -1288,15 +1245,13 @@ function TSolutionAlgs.SolveHarmonic: Integer;
// Now should have all we need to make a short circuit report
end;
-
end;
-//========================================================================================
-function TSolutionAlgs.SolveHarmTime: Integer; // It is based in SolveGeneralTime routine
+function TSolutionAlgs.SolveHarmTime: Integer; // It is based in SolveGeneralTime routine
begin
Result := 0;
- with DSS.ActiveCircuit, DSS.ActiveCircuit.Solution do
+ with DSS.ActiveCircuit, Solution do
begin
IntervalHrs := DynaVars.h / 3600.0; // needed for energy meters and storage devices
if not DSS.SolutionAbort then
@@ -1310,7 +1265,7 @@ function TSolutionAlgs.SolveHarmTime: Integer; // It is based in SolveGenera
end;
end;
end;
-//=============================================================================
+
function TSolutionAlgs.SolveHarmonicT: Integer;
var
FrequencyList: pDoubleArray;
@@ -1321,7 +1276,7 @@ function TSolutionAlgs.SolveHarmonicT: Integer;
FrequencyList := NIL; // Set up for Reallocmem
- with DSS.ActiveCircuit, DSS.ActiveCircuit.solution do
+ with DSS.ActiveCircuit, Solution do
begin
IntervalHrs := DynaVars.h / 3600.0; // needed for energy meters and storage devices
try
@@ -1351,7 +1306,6 @@ function TSolutionAlgs.SolveHarmonicT: Integer;
for i := 1 to NFreq do
begin
-
Frequency := FrequencyList^[i]; // forces rebuild of SystemY
if Abs(Harmonic - 1.0) > EPSILON then
begin // Skip fundamental
@@ -1368,7 +1322,6 @@ function TSolutionAlgs.SolveHarmonicT: Integer;
ReallocMem(FrequencyList, 0);
end;
end;
-
end;
end.
diff --git a/src/Common/Sparse_Math.pas b/src/Common/Sparse_Math.pas
index a0720b886..0643f6719 100644
--- a/src/Common/Sparse_Math.pas
+++ b/src/Common/Sparse_Math.pas
@@ -13,10 +13,7 @@
interface
uses
-{$IFNDEF FPC}
- dialogs,
-{$ENDIF}
- UComplex,
+ UComplex, DSSUcomplex,
Ucmatrix;
type
@@ -72,7 +69,7 @@ Tsparse_Complex = class(Tobject)
function checkifexists(r, c: Integer): Integer;
procedure getrow(index: Integer; cols: PData; vals: PComplexArr);
- function getvalue(row, col: Integer): Complex;
+ function getvalue(r, c: Integer): Complex;
function R_equal(acols, bcols: PData; avals, bvals: PComplexArr): Boolean;
PUBLIC
@@ -231,7 +228,6 @@ function Tsparse_matrix.insert(r, c, val: Integer): Integer;
if row < r then
row := r;
end;
-
end;
// Adds another sparse matrix to this matrix
@@ -294,7 +290,6 @@ function Tsparse_matrix.add(b: Tsparse_matrix): Tsparse_matrix;
end;
end;
-
end;
// Transposes the sparse matrix
@@ -470,7 +465,7 @@ function Tsparse_Complex.R_equal(acols, bcols: PData; avals, bvals: PComplexArr)
end;
// Returns the value contained at the specific position
-function Tsparse_Complex.getvalue(row, col: Integer): Complex;
+function Tsparse_Complex.getvalue(r, c: Integer): Complex;
var
go_flag: Boolean;
i: Integer;
@@ -480,7 +475,7 @@ function Tsparse_Complex.getvalue(row, col: Integer): Complex;
i := 0;
while go_flag do
begin
- if (CData[i].Row = row) and (CData[i].Col = col) then
+ if (CData[i].Row = r) and (CData[i].Col = c) then
begin
Result := CData[i].Value;
go_flag := FALSE;
@@ -492,7 +487,6 @@ function Tsparse_Complex.getvalue(row, col: Integer): Complex;
go_flag := FALSE;
end;
end;
-
end;
// Gets the columns and values at each columns for the row specified
@@ -615,7 +609,6 @@ function Tsparse_Complex.insert(r, c: Integer; val: Complex): Integer;
if row < r then
row := r;
end;
-
end;
// Adds another sparse matrix to this matrix
@@ -657,7 +650,7 @@ function Tsparse_Complex.add(b: Tsparse_Complex): Tsparse_Complex;
end
else
begin
- addeval := cadd(CData[apos].Value, b.CData[bpos].Value);
+ addeval := CData[apos].Value + b.CData[bpos].Value;
if (addeval.re <> 0) and (addeval.im <> 0) then
Result.insert(CData[apos].Row, CData[apos].Col, addeval);
inc(apos);
@@ -678,7 +671,6 @@ function Tsparse_Complex.add(b: Tsparse_Complex): Tsparse_Complex;
end;
end;
-
end;
// Transposes the sparse matrix
@@ -793,7 +785,7 @@ function Tsparse_Complex.TransposeConj(): Tsparse_Complex;
Result.CData[rpos].Col := CData[i].Row;
// same value
- Result.CData[rpos].Value := Conjg(CData[i].Value);
+ Result.CData[rpos].Value := cong(CData[i].Value);
end;
// the above method ensures
@@ -864,8 +856,8 @@ function Tsparse_Complex.multiply(b: Tsparse_Complex): Tsparse_Complex;
inc(tempb) //skip b
else
begin
- // same col, so multiply and increment
- sum := cadd(sum, cmul(CData[tempa].Value, b.CData[tempb].Value));
+ // same col, so multiply and increment
+ sum := sum + CData[tempa].Value * b.CData[tempb].Value;
inc(tempa);
inc(tempb);
end;
diff --git a/src/Common/Terminal.pas b/src/Common/Terminal.pas
index 5f8cc3a83..918b8829a 100644
--- a/src/Common/Terminal.pas
+++ b/src/Common/Terminal.pas
@@ -7,7 +7,7 @@
----------------------------------------------------------
}
-{ Definition of classes for all terminals of a DSS element}
+// Definition of classes for all terminals of a DSS element
interface
@@ -29,18 +29,13 @@ interface
TerminalArray = array of TPowerTerminal;
- {
- Control Terminal is managed by override functions in classes that are derived from this class
- }
+// Control Terminal is managed by override functions in classes that are derived from this class
implementation
uses
SysUtils;
-
-{TPowerTerminal}
-
procedure TPowerTerminal.Init(Ncond: Integer);
var
i: Integer;
@@ -53,12 +48,10 @@ procedure TPowerTerminal.Init(Ncond: Integer);
ActiveConductor := 1;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TPowerTerminal.Set_ActiveConductor(value: Integer);
begin
if (Value > 0) and (Value <= Length(ConductorsClosed)) then
ActiveConductor := Value;
end;
-
end.
diff --git a/src/Common/Utilities.pas b/src/Common/Utilities.pas
index c8516ff29..b8c191074 100644
--- a/src/Common/Utilities.pas
+++ b/src/Common/Utilities.pas
@@ -8,18 +8,13 @@
----------------------------------------------------------
}
-
-{
- 12-18-2002 RCD Converted Eventlog to in-memory rather than file
-}
-
interface
uses
ArrayDef,
CktElement,
PDElement,
- UComplex,
+ UComplex, DSSUcomplex,
UcMatrix,
DSSClass,
Classes,
@@ -34,6 +29,27 @@ interface
);
{$SCOPEDENUMS OFF}
+const
+ sCRLF: String = sLineBreak;
+ clAqua: Integer = $FFFF00;
+ clBlack: Integer = $000000;
+ clBlue: Integer = $FF0000;
+ clDkGray: Integer = $808080;
+ clFuchsia: Integer = $FF00FF;
+ clGray: Integer = $808080;
+ clGreen: Integer = $008000;
+ clLime: Integer = $00FF00;
+ clLtGray: Integer = $C0C0C0;
+ clMaroon: Integer = $000080;
+ clNavy: Integer = $800000;
+ clOlive: Integer = $008080;
+ clPurple: Integer = $800080;
+ clRed: Integer = $0000FF;
+ clSilver: Integer = $C0C0C0;
+ clTeal: Integer = $808000;
+ clWhite: Integer = $FFFFFF;
+ clYellow: Integer = $00FFFF;
+
function StrTOrF(const b: Boolean): String; inline;
function StrYOrN(const b: Boolean): String; inline;
function CompareTextShortest(const S1, S2: String): Integer;
@@ -48,59 +64,37 @@ function PadTrunc(const S: String; Width: Integer): String;
function IntArrayToString(iarray: pIntegerArray; count: Integer): String;
function DblArrayToString(dblarray: pDoubleArray; count: Integer): String;
function CmplxArrayToString(cpxarray: pComplexArray; count: Integer): String;
+function StringListToString(lst: TStringList): String;
function EncloseQuotes(const s: String): String;
procedure ShowMessageBeep(DSS: TDSSContext; const s: String);
-function FullName(pElem: TDSSCktElement): String;
-{Parsing Utilities}
+// Parsing Utilities
procedure ParseObjectClassandName(DSS: TDSSContext; const FullObjName: String; var ClassName, ObjName: String);
procedure ParseIntArray(DSS: TDSSContext; var iarray: pIntegerArray; var count: Integer; const s: String);
-function InterpretSolveMode(const s: String): TSolveMode;
-function InterpretControlMode(const s: String): Integer;
-function InterpretLoadModel(DSS: TDSSContext; const s: String): Integer;
function InterpretYesNo(const s: String): Boolean;
-function InterpretRandom(const s: String): Integer;
-function InterpretAddType(const s: String): Integer;
-function InterpretConnection(const s: String): Integer;
-function InterpretSolveAlg(const s: String): Integer;
-function InterpretCktModel(const s: String): Boolean;
procedure InitDblArray(NumValues: Integer; Xarray: pDoubleArray; Value: Double);
-procedure InitIntArray(NumValues: Integer; Xarray: pIntegerArray; Value: Integer);
function InterpretDblArray(DSS: TDSSContext; const s: String; MaxValues: Integer; ResultArray: pDoubleArray): Integer;
function InterpretIntArray(DSS: TDSSContext; const s: String; MaxValues: Integer; ResultArray: pIntegerArray): Integer;
-procedure InterpretAndAllocStrArray(DSS: TDSSContext; const s: String; var Size: Integer; var ResultArray: pStringArray);
procedure InterpretTStringListArray(DSS: TDSSContext; const s: String; var ResultList: TStringList);
function InterpretTimeStepSize(DSS: TDSSContext; const s: String): Double;
-function InterpretLoadShapeClass(const s: String): Integer;
-function InterpretEarthModel(const s: String): Integer;
function InterpretColorName(DSS: TDSSContext; const s: String): Integer;
-function InterpretComplex(DSS: TDSSContext; const s: String): Complex;
function ConstructElemName(DSS: TDSSContext; const Param: String): String;
-function InterpretCoreType(const str: String): Integer;
-
-function GetSolutionModeID(DSS: TDSSContext): String;
-function GetSolutionModeIDName(idx: TSolveMode): String;
-function GetControlModeID(DSS: TDSSContext): String;
-function GetRandomModeID(DSS: TDSSContext): String;
-function GetLoadModel(DSS: TDSSContext): String;
-function GetActiveLoadShapeClass(DSS: TDSSContext): String;
-function GetDSSArray_Real(n: Integer; dbls: pDoubleArray): String;
+
+function GetDSSArray_Real(n: Integer; dbls: pDoubleArray; scale: Double = 1.0): String;
function GetDSSArray_Single(n: Integer; sngs: pSingleArray): String;
function GetDSSArray_Integer(n: Integer; ints: pIntegerArray): String;
-function GetEarthModel(n: Integer): String;
function GetOCPDeviceType(pElem: TDSSCktElement): Integer;
function GetOCPDeviceTypeString(icode: Integer): String;
// Addition to deal with Memory mapped data
function InterpretDblArrayMMF(DSS: TDSSContext; mmPtr: pByte; FileType: TLSFileType; Column, INDEX, DataSize: Integer): double;
-{misc functions}
+// misc functions
function DoExecutiveCommand(DSS: TDSSContext; const s: String): Integer;
function GetCktElementIndex(DSS: TDSSContext; const FullObjName: String): Integer;
function IsShuntElement(const Elem: TDSSCktElement): Boolean;
function IsLineElement(const Elem: TDSSCktElement): Boolean;
function IsTransformerElement(const Elem: TDSSCktElement): Boolean;
-// --- moved to ReduceAlgs 2/13/19 Function IsStubLine(const Elem:TDSSCktElement):Boolean;
function CheckParallel(const Line1, Line2: TDSSCktElement): Boolean;
function AllTerminalsClosed(ThisElement: TDSSCktElement): Boolean;
function Str_Real(const Value: Double; NumDecimals: Integer): String;
@@ -108,35 +102,31 @@ procedure DumpAllDSSCommands(DSS: TDSSContext; var Filename: String);
procedure DumpAllocationFactors(DSS: TDSSContext; var Filename: String);
procedure DumpComplexMatrix(DSS: TDSSContext; F: TFileStream; AMatrix: TcMatrix);
function NearestBasekV(DSS: TDSSContext; kV: Double): Double;
-function PresentTimeInSec(DSS: TDSSContext): Double;
function DoResetFaults(DSS: TDSSContext): Integer;
function DoResetControls(DSS: TDSSContext): Integer;
procedure DoResetKeepList(DSS: TDSSContext);
function GetNodeNum(DSS: TDSSContext; NodeRef: Integer): Integer;
-procedure InitStringToNull(var S: String);
-function CmulReal_im(const a: Complex; const Mult: Double): Complex; // Multiply only imaginary part by a real
-//FUNCTION IsValidNumericField(const NumberField:TEdit):Boolean;
function MaxdblArrayValue(npts: Integer; dbls: pDoubleArray): Double;
function iMaxAbsdblArrayValue(npts: Integer; dbls: pDoubleArray): Integer;
function iMaxAbssngArrayValue(npts: Integer; sngs: pSingleArray): Integer;
function QuadSolver(const a, b, c: Double): Double; // returns largest of two answers
-{Save Function Helper}
+// Save Function Helper
function WriteClassFile(DSS: TDSSContext; const DSS_Class: TDSSClass; FileName: String; IsCktElement: Boolean): Boolean;
function WriteVsourceClassFile(DSS: TDSSContext; const DSS_Class: TDSSClass; IsCktElement: Boolean): Boolean;
procedure WriteActiveDSSObject(DSS: TDSSContext; F: TFileStream; const NeworEdit: String);
function checkforblanks(const S: String): String;
function RewriteAlignedFile(DSS: TDSSContext; const Filename: String): Boolean;
-{Event Log}
+// Event Log
procedure ClearEventLog(DSS: TDSSContext);
procedure AppendToEventLog(DSS: TDSSContext; const opdev: String; const action: String);
procedure LogThisEvent(DSS: TDSSContext; const EventName: String);
procedure ClearErrorLog(DSS: TDSSContext);
-{Routines for doing common things to complex numbers}
+// Routines for doing common things to complex numbers
procedure RotatePhasorDeg(var Phasor: Complex; const h, AngleDeg: Double);
procedure RotatePhasorRad(var Phasor: Complex; const h, AngleRad: Double);
procedure ConvertComplexArrayToPolar(const Buffer: pComplexArray; N: Integer);
@@ -144,11 +134,9 @@ procedure ConvertComplexArrayToPowerandPF(const Buffer: pComplexArray; N: Intege
function Residual(p: Pointer; Nph: Integer): Complex;
function ResidualPolar(p: Pointer; Nph: Integer): Complex;
function Powerfactor(const S: Complex): Double;
-function ConvertPFToPFRange2(const value: Double): Double;
-function ConvertPFRange2ToPF(const value: Double): Double;
procedure CmulArray(pc: pcomplexarray; Multiplier: Double; size: Integer); // Multiply a complex array times a double
-{Support for going in and out of Dynamics Mode and Harmonics Mode}
+// Support for going in and out of Dynamics Mode and Harmonics Mode
procedure CalcInitialMachineStates(DSS: TDSSContext);
procedure InvalidateAllPCELEMENTS(DSS: TDSSContext);
function InitializeForHarmonics(DSS: TDSSContext): Boolean;
@@ -161,7 +149,7 @@ function GetTotalPowerFromSources(DSS: TDSSContext): Complex;
function GetMaxCktElementSize(DSS: TDSSContext): Integer;
function GetUniqueNodeNumber(DSS: TDSSContext; const sBusName: String; StartNode: Integer): Integer;
-{TraceBack Functions}
+// TraceBack Functions
function IsPathBetween(FromLine, ToLine: TPDElement): Boolean;
procedure TraceAndEdit(DSS: TDSSContext; FromLine, ToLine: TPDElement; NPhases: Integer; EditStr: String);
procedure GoForwardAndRephase(DSS: TDSSContext; FromLine: TPDElement; const PhaseString, EditStr, ScriptFileName: String; TransStop: Boolean);
@@ -170,7 +158,7 @@ procedure MakeDistributedGenerators(DSS: TDSSContext; kW, PF: Double; How: Strin
procedure Obfuscate(DSS: TDSSContext);
-function AdjustInputFilePath(DSS: TDSSContext; const param: String): String;
+function AdjustInputFilePath(DSS: TDSSContext; param: String): String;
procedure FSWriteLn(F: TFileStream; Ss: Array of String); inline; overload;
procedure FSWriteLn(F: TFileStream; S: String = ''); inline; overload;
procedure FSWriteLn(F: TFileStream; S: String; S2: String); inline; overload;
@@ -179,16 +167,24 @@ procedure FSWrite(F: TFileStream; S: String); inline; overload;
procedure FSWrite(F: TFileStream; S: String; S2: String); inline; overload;
procedure FSWrite(F: TFileStream; S: String; S2: String; S3: String); inline; overload;
-procedure FSReadln(F: TFileStream; out S: String);
+procedure FSReadln(F: TStream; out S: String);
procedure FSFlush(F: TFileStream);
function SliceProps(props: pStringArray; count: Integer): ArrayOfString; // The built-in Slice was causing issues on ARM64
+procedure DoSngFile(DSS: TDSSContext; var pA, pB: PDoubleArray; var NumPoints: Integer; OnlyLoadB: Boolean; const FileName: String; const ClassName: String; RoundA: Boolean = False);
+procedure DoDblFile(DSS: TDSSContext; var pA, pB: PDoubleArray; var NumPoints: Integer; OnlyLoadB: Boolean; const FileName: String; const ClassName: String; RoundA: Boolean = False);
+procedure DoCSVFile(DSS: TDSSContext; var pA, pB: PDoubleArray; var NumPoints: Integer; OnlyLoadB: Boolean; const FileName: String; const ClassName: String; RoundA: Boolean = False);
+
+procedure DelFilesFromDir(Directory: String; FileMask: String = '*'; DelSubDirs: Boolean = True);
+
implementation
uses
+ BufStream,
{$IFDEF WINDOWS}
Windows,
+ ShellApi,
{$ELSE}
BaseUnix,
Unix,
@@ -217,15 +213,11 @@ implementation
PCElement,
ControlElem,
DSSHelper,
- StrUtils,
- BufStream;
+ StrUtils;
const
- ZERONULL: Integer = 0;
padString: String = ' '; //50 blanks
paddotsString: String = ' .................................................'; //50 dots
- sCRLF: String = CRLF;
-
function StrTOrF(const b: Boolean): String; inline;
begin
@@ -242,12 +234,11 @@ function StrYOrN(const b: Boolean): String; inline;
else
Result := 'No'
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+
function CompareTextShortest(const S1, S2: String): Integer;
var
Teststr: String;
begin
-
if Length(S1) < Length(S2) then
begin
TestStr := Copy(S2, 1, Length(S1));
@@ -258,10 +249,8 @@ function CompareTextShortest(const S1, S2: String): Integer;
TestStr := Copy(S1, 1, Length(S2));
Result := CompareText(TestStr, S2);
end;
-
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function Pad(const S: String; Width: Integer): String;
// Pad out a string with blanks to Width characters
begin
@@ -274,24 +263,15 @@ function PadDots(const S: String; Width: Integer): String;
begin
Result := Copy(S, 1, Length(S)) + Copy(paddotsString, 1, (Width - Length(S)));
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+
function PadTrunc(const S: String; Width: Integer): String;
// Pad out a string with blanks to Width characters or truncate to Width Chars
begin
Result := Copy(Pad(S, Width), 1, Width);
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
-function FullName(pElem: TDSSCktElement): String;
-begin
- Result := EncloseQuotes(pElem.DSSClassName + '.' + UpperCase(pElem.Name));
-end;
-
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function StripExtension(const S: String): String;
-
-{Strips off everything up to a period.}
-
+// Strips off everything up to a period.
var
dotpos: Integer;
@@ -302,10 +282,8 @@ function StripExtension(const S: String): String;
Result := Copy(S, 1, dotpos);
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function StripClassName(const S: String): String;
-{Returns everything past the first period}
-
+// Returns everything past the first period
var
dotpos: Integer;
@@ -315,7 +293,6 @@ function StripClassName(const S: String): String;
end;
{$IFDEF WINDOWS}
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure FireOffEditor(DSS: TDSSContext; FileNm: String);
var
retval: Word;
@@ -323,6 +300,10 @@ procedure FireOffEditor(DSS: TDSSContext; FileNm: String);
if not DSS_CAPI_ALLOW_EDITOR then
Exit; // just ignore if Show is not allowed
+ if (@DSSPrime.DSSMessageCallback) <> NIL then
+ if 0 = DSSPrime.DSSMessageCallback(DSSPrime, PChar(FileNm), ord(DSSMessageType.FireOffEditor)) then
+ Exit;
+
try
if FileExists(FileNm) then
begin
@@ -331,24 +312,23 @@ procedure FireOffEditor(DSS: TDSSContext; FileNm: String);
case Retval of
0:
- DoSimpleMsg(DSS, 'System out of memory. Cannot start Editor.', 700);
+ DoSimpleMsg(DSS, _('System out of memory. Cannot start Editor.'), 700);
ERROR_BAD_FORMAT:
- DoSimpleMsg(DSS, 'Editor File is Invalid.', 701);
+ DoSimpleMsg(DSS, _('Editor File is Invalid.'), 701);
ERROR_FILE_NOT_FOUND:
- DoSimpleMsg(DSS, 'Editor "' + DefaultEditor + '" Not Found.' + CRLF + 'Did you set complete path name?', 702);
+ DoSimpleMsg(DSS, 'Editor "%s" not found. Did you set a complete path name?', [DefaultEditor], 702);
ERROR_PATH_NOT_FOUND:
- DoSimpleMsg(DSS, 'Path for Editor "' + DefaultEditor + '" Not Found.', 703);
+ DoSimpleMsg(DSS, 'Path for Editor "%s" not found.', [DefaultEditor], 703);
end;
end;
except
On E: Exception do
DoErrorMsg(DSS, 'FireOffEditor.', E.Message,
- 'Default Editor correctly specified???', 704);
+ _('Default Editor correctly specified???'), 704);
end;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure DoDOSCmd(DSS: TDSSContext; CmdString: String);
var
Handle: Word;
@@ -359,7 +339,7 @@ procedure DoDOSCmd(DSS: TDSSContext; CmdString: String);
except
On E: Exception do
- DoSimpleMsg(DSS, Format('DoDOSCmd Error:%s. Error in Command "%s"', [E.Message, CmdString]), 704);
+ DoSimpleMsg(DSS, 'DoDOSCmd Error:%s. Error in Command "%s"', [E.Message, CmdString], 704);
end;
end;
{$ELSE}
@@ -372,6 +352,10 @@ procedure FireOffEditor(DSS: TDSSContext; FileNm: String);
if not DSS_CAPI_ALLOW_EDITOR then
Exit; // just ignore if Show is not allowed
+ if (@DSSPrime.DSSMessageCallback) <> NIL then
+ if 0 = DSSPrime.DSSMessageCallback(DSSPrime, PChar(FileNm), ord(DSSMessageType.FireOffEditor)) then
+ Exit;
+
gotError := FALSE;
msg := 'Unknown error in process.';
try
@@ -387,7 +371,7 @@ procedure FireOffEditor(DSS: TDSSContext; FileNm: String);
end;
end;
if gotError then
- DoErrorMsg(DSS, 'FireOffEditor.', msg, 'Editor could not be started. Is the editor correctly specified?', 704);
+ DoErrorMsg(DSS, 'FireOffEditor.', msg, _('Editor could not be started. Is the editor correctly specified?'), 704);
end;
procedure DoDOSCmd(DSS: TDSSContext; CmdString: String);
@@ -397,7 +381,7 @@ procedure DoDOSCmd(DSS: TDSSContext; CmdString: String);
msg: String;
begin
gotError := FALSE;
- msg := 'Unknown error in command.';
+ msg := _('Unknown error in command.');
try
gotError := not RunCommand('/bin/bash', ['-c', CmdString], s);
except
@@ -408,19 +392,15 @@ procedure DoDOSCmd(DSS: TDSSContext; CmdString: String);
end;
end;
if gotError then
- DoSimpleMsg(DSS, Format('DoDOSCmd Error:%s. Error in Command "%s"', [msg, CmdString]), 704);
+ DoSimpleMsg(DSS, 'DoDOSCmd Error:%s. Error in Command "%s"', [msg, CmdString], 704);
end;
{$ENDIF}
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function IntArrayToString(iarray: pIntegerArray; count: Integer): String;
// Put array values in parentheses separated by commas.
-
var
i: Integer;
-
begin
-
Result := '[NULL]';
if count > 0 then
begin
@@ -433,16 +413,12 @@ function IntArrayToString(iarray: pIntegerArray; count: Integer): String;
end;
Result := Result + ']';
end;
-
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function DblArrayToString(dblarray: pDoubleArray; count: Integer): String;
// Put array values in brackets separated by commas.
-
var
i: Integer;
-
begin
Result := '[NULL]';
if count > 0 then
@@ -452,18 +428,13 @@ function DblArrayToString(dblarray: pDoubleArray; count: Integer): String;
Result := Result + Format(', %.10g', [dblarray^[i]]);
Result := Result + ']';
end;
-
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function CmplxArrayToString(cpxarray: pComplexArray; count: Integer): String;
// Put array values in brackets separated by commas.
-
var
i: Integer;
-
begin
-
Result := '[NULL]';
if count > 0 then
begin
@@ -472,147 +443,34 @@ function CmplxArrayToString(cpxarray: pComplexArray; count: Integer): String;
Result := Result + Format(', %.10g +j %.10g', [cpxarray^[i].re, cpxarray^[i].im]);
Result := Result + ']';
end;
-
end;
-
-function EncloseQuotes(const s: String): String;
-begin
- Result := '"' + s + '"';
-end;
-
-
-//----------------------------------------------------------------------------
-function InterpretSolveMode(const s: String): TSolveMode;
-
-// interpret solution mode
-// could be "nominal" "daily" "yearly" "montecarlo" "dutycycle" "loadduration" "peakdays" , etc.
-
+function StringListToString(lst: TStringList): String;
var
- SLC: String;
+ i: Integer;
begin
- SLC := lowercase(s);
-
- case SLC[1] of
- 's':
- Result := TSolveMode.SNAPSHOT;
- 'd':
- case SLC[2] of
- 'u':
- Result := TSolveMode.DUTYCYCLE;
- 'i':
- Result := TSolveMode.DIRECT;
- 'y':
- Result := TSolveMode.DYNAMICMODE;
- else
- Result := TSolveMode.DAILYMODE;
- end;
- 'f':
- Result := TSolveMode.FAULTSTUDY;
- 'h':
- case SLC[9] of //Modification added by Davis Montenegro 25/06/2014
- 't':
- Result := TSolveMode.HARMONICMODET; // For adding the harmoncis mode in time domain
- else
- Result := TSolveMode.HARMONICMODE;
- end;
- 'y':
- Result := TSolveMode.YEARLYMODE;
- 'm':
- case SLC[2] of
- '1':
- Result := TSolveMode.MONTECARLO1;
- '2':
- Result := TSolveMode.MONTECARLO2;
- '3':
- Result := TSolveMode.MONTECARLO3;
- 'f':
- Result := TSolveMode.MONTEFAULT;
- else
- Result := TSolveMode.MONTECARLO1;
- end;
- 'p':
- Result := TSolveMode.PEAKDAY;
- 'a':
- Result := TSolveMode.AUTOADDFLAG;
- 'l':
- case SLC[2] of
- 'd':
- case SLC[3] of
- '1':
- Result := TSolveMode.LOADDURATION1;
- '2':
- Result := TSolveMode.LOADDURATION2;
- else
- Result := TSolveMode.LOADDURATION1;
- end;
- else
- Result := TSolveMode.LOADDURATION1;
- end;
- 't':
- Result := TSolveMode.GENERALTIME;
-
- else
- Result := TSolveMode.SNAPSHOT;
+ if (lst = NIL) or (lst.Count = 0) then
+ begin
+ Result := '';
+ Exit;
end;
-
-
-end;
-
-//----------------------------------------------------------------------------
-function InterpretControlMode(const s: String): Integer;
-
-// interpret solution control mode
-
-var
- SLC: String;
-begin
- SLC := lowercase(s);
-
- case SLC[1] of
- 'o':
- Result := CONTROLSOFF;
- 'e':
- Result := EVENTDRIVEN; // "event"
- 't':
- Result := TIMEDRIVEN; // "time"
- 'm':
- Result := MULTIRATE; // "MultiRate"
- else
- Result := CTRLSTATIC;
+ Result := '[' + lst.Strings[0];
+ for i := 1 to lst.Count - 1 do
+ begin
+ Result := Result + ', ' + lst.Strings[i];
end;
-
-
+ Result := Result + ']';
end;
-//----------------------------------------------------------------------------
-function InterpretLoadModel(DSS: TDSSContext; const s: String): Integer;
-var
- S2: String;
-begin
- S2 := LowerCase(S);
- case S2[1] of
- 'a':
- Result := ADMITTANCE;
- 'p':
- Result := POWERFLOW;
- else
- Result := ADMITTANCE;
- end;
-{ If this represents a change, invalidate all the PC Yprims}
- if Result <> DSS.ActiveCircuit.Solution.LoadModel then
- DSS.ActiveCircuit.InvalidateAllPCElements;
+function EncloseQuotes(const s: String): String;
+begin
+ Result := '"' + s + '"';
end;
-//----------------------------------------------------------------------------
function InterpretYesNo(const s: String): Boolean;
-
//' Interpret Yes / no properties - can also be True/False
-var
- S2: Char;
begin
- S2 := LowerCase(S)[1];
- case S2 of
+ case AnsiLowerCase(S[1])[1] of
'y', 't':
Result := TRUE;
'n', 'f':
@@ -620,174 +478,39 @@ function InterpretYesNo(const s: String): Boolean;
else
Result := FALSE;
end;
-
-end;
-
-//----------------------------------------------------------------------------
-function InterpretRandom(const s: String): Integer;
-
-// interpret the type of random variation in the load
-// none|gaussian|uniform |lognormal
-
-var
- SLC: String;
-begin
- SLC := lowercase(s);
-
- case SLC[1] of
- 'g':
- Result := GAUSSIAN; //gaussian
- 'u':
- Result := UNIFORM; //uniform
- 'l':
- Result := LOGNORMAL; // Log-Normal
- else
- Result := 0; // no variation for any other entry
- end
-
end;
-
-//----------------------------------------------------------------------------
-function InterpretAddType(const s: String): Integer;
-// type of device to automatically add. Default is capacitor
-
-var
- SLC: String;
-begin
- SLC := lowercase(s);
-
- case SLC[1] of
- 'g':
- Result := GENADD;
- else
- Result := CAPADD;
- end
-
-end;
-
-//----------------------------------------------------------------------------
-function InterpretConnection(const s: String): Integer;
-{ Accepts (Case insensitive)
- delta or LL Result=1
- Y, wye, or LN Result=0
-}
-begin
- Result := 0;
- case lowercase(S)[1] of
- 'y', 'w':
- Result := 0; {Wye}
- 'd':
- Result := 1; {Delta or line-Line}
- 'l':
- case lowercase(s)[2] of
- 'n':
- Result := 0;
- 'l':
- Result := 1;
- end;
- end;
-end;
-
-//----------------------------------------------------------------------------
-function InterpretSolveAlg(const s: String): Integer;
-
-var
- SLC: String;
-
-begin
- SLC := copy(lowercase(s), 1, 2);
-
- if CompareText(SLC, 'ne') = 0 then
- Result := NEWTONSOLVE
- else
- Result := NORMALSOLVE;
-
-end;
-
-
-//----------------------------------------------------------------------------
-function InterpretCktModel(const s: String): Boolean;
-
-{Returns True if Positive Sequence}
-
-begin
-
- case s[1] of
- 'p', 'P':
- Result := TRUE
- else
- Result := FALSE
- end;
-
-end;
-
-//----------------------------------------------------------------------------
-function InterpretComplex(DSS: TDSSContext; const s: String): Complex;
-
-// interpret first two entries as complex numbers
-
-// var
-// ParmName: String;
-
-begin
- DSS.AuxParser.CmdString := S;
- {ParmName :=} DSS.AuxParser.NextParam;
- Result.re := DSS.AuxParser.dblvalue;
- {ParmName :=} DSS.AuxParser.NextParam;
- Result.im := DSS.AuxParser.dblvalue;
-end;
-
-//----------------------------------------------------------------------------
-
procedure InitDblArray(NumValues: Integer; Xarray: pDoubleArray; Value: Double);
var
i: Integer;
-{Set all elements of a double array}
+// Set all elements of a double array
begin
for i := 1 to NumValues do
Xarray^[i] := Value;
end;
-//----------------------------------------------------------------------------
-
-procedure InitIntArray(NumValues: Integer; Xarray: pIntegerArray; Value: Integer);
-var
- i: Integer;
-{Set all elements of a Integer array}
-begin
- for i := 1 to NumValues do
- Xarray^[i] := Value;
-end;
-
-//----------------------------------------------------------------------------
function InterpretDblArray(DSS: TDSSContext; const s: String; MaxValues: Integer; ResultArray: pDoubleArray): Integer;
-
-{ Get numeric values from an array specified either as a list on numbers or a text file spec.
- ResultArray must be allocated to MaxValues by calling routine.
-
- 9/7/2011 Modified to allow multi-column CSV files and result file
-
- CSV File my have one value per line or multiple columns and a header row.
- Example:
- ... mult=[file = myfilename, column=2, header=yes]
- ... mult=[file = %result%, column=2, header=yes] // last result file
-
- file= must be first
- the %result% variable implies the last result file
-
- or
-
- Use the Array=@lastfile variable syntax and the parser will automativally replace with last file name
-
-}
-
+// Get numeric values from an array specified either as a list on numbers or a text file spec.
+// ResultArray must be allocated to MaxValues by calling routine.
+//
+// 9/7/2011 Modified to allow multi-column CSV files and result file
+//
+// CSV File my have one value per line or multiple columns and a header row.
+// Example:
+// ... mult=[file = myfilename, column=2, header=yes]
+// ... mult=[file = %result%, column=2, header=yes] // last result file
+//
+// file= must be first
+// the %result% variable implies the last result file
+//
+// or
+//
+// Use the Array=@lastfile variable syntax and the parser will automativally replace with last file name
var
ParmName,
Param: String;
- F: TFileStream = nil;
MStream: TMemoryStream;
- FStream: TBufferedFileStream;
+ F: TStream = NIL; // input
i: Integer;
// Temp: Single;
CSVFileName: String;
@@ -798,33 +521,33 @@ function InterpretDblArray(DSS: TDSSContext; const s: String; MaxValues: Integer
sngArray: ArrayDef.PSingleArray;
begin
-
DSS.AuxParser.CmdString := S;
ParmName := DSS.AuxParser.NextParam;
Param := DSS.AuxParser.StrValue;
Result := MaxValues; // Default Return Value;
- {Syntax can be either a list of numeric values or a file specification: File= ...}
+ // Syntax can be either a list of numeric values or a file specification: File= ...
if CompareText(Parmname, 'file') = 0 then
begin
- {Default values}
+ // Default values
if compareText(param, '%result%') = 0 then
CSVFileName := DSS.LastResultFile
else
- CSVFileName := AdjustInputFilePath(DSS, Param);
+ CSVFileName := Param;
- if not FileExists(CSVFileName) then
- begin
- DoSimpleMsg(DSS, Format('CSV file "%s" does not exist', [CSVFileName]), 70401);
+ try
+ F := DSS.GetROFileStream(CSVFileName);
+ except
+ DoSimpleMsg(DSS, 'CSV file "%s" could not be opened', [CSVFileName], 70401);
Exit;
end;
- // Default options
+ // Default options
CSVColumn := 1;
CSVHeader := FALSE;
- // Look for other options (may be in either order)
+ // Look for other options (may be in either order)
ParmName := DSS.AuxParser.NextParam;
Param := DSS.AuxParser.StrValue;
while Length(Param) > 0 do
@@ -837,11 +560,9 @@ function InterpretDblArray(DSS: TDSSContext; const s: String; MaxValues: Integer
Param := DSS.AuxParser.StrValue;
end;
- // load the list from a file
+ // load the list from a file
try
- F := TBufferedFileStream.Create(CSVFileName, fmOpenRead);
-
if CSVHeader then
FSReadln(F, InputLIne); // skip the header row
@@ -864,7 +585,7 @@ function InterpretDblArray(DSS: TDSSContext; const s: String; MaxValues: Integer
except
On E: Exception do
begin
- DoSimpleMsg(DSS, Format('Error reading %d-th numeric array value from file: "%s" Error is:', [i, Param, E.message]), 705);
+ DoSimpleMsg(DSS, 'Error reading %d-th numeric array value from file: "%s" Error is:', [i, Param, E.message], 705);
Result := i - 1;
Break;
end;
@@ -881,33 +602,36 @@ function InterpretDblArray(DSS: TDSSContext; const s: String; MaxValues: Integer
else if (Length(Parmname) > 0) and (CompareTextShortest(Parmname, 'dblfile') = 0) then
begin
// load the list from a file of doubles (no checking done on type of data)
- if FileExists(Param) then
- begin
- FStream := TBufferedFileStream.Create(Param, fmOpenRead);
- Result := Min(Maxvalues, FStream.Size div sizeof(ResultArray^[1])); // no. of doubles
- FStream.ReadBuffer(ResultArray^[1], SizeOf(ResultArray^[1]) * Result);
- FStream.Free;
- end
- else
- DoSimpleMsg(DSS, Format('File of doubles "%s" not found.', [Param]), 70501);
+ try
+ F := DSS.GetROFileStream(Param);
+ except
+ DoSimpleMsg(DSS, 'File of doubles "%s" could not be opened.', [Param], 70501);
+ Exit;
+ end;
+ Result := Min(Maxvalues, F.Size div sizeof(ResultArray^[1])); // no. of doubles
+ F.ReadBuffer(ResultArray^[1], SizeOf(ResultArray^[1]) * Result);
+ F.Free;
end
else if (Length(Parmname) > 0) and (CompareTextShortest(Parmname, 'sngfile') = 0) then
begin
// load the list from a file of singles (no checking done on type of data)
+ try
+ F := DSS.GetROFileStream(Param)
+ except
+ DoSimpleMsg(DSS, 'File of Singles "%s" could not be opened.', [Param], 70502);
+ Exit;
+ end;
+
MStream := TMemoryStream.Create;
- if FileExists(Param) then
+ MStream.LoadFromStream(F);
+ F.Free;
+ sngArray := ArrayDef.PSingleArray(MStream.Memory);
+ // Now move the singles from the file into the destination array
+ Result := Min(Maxvalues, MStream.Size div sizeof(Single)); // no. of singles
+ for i := 1 to Result do
begin
- MStream.LoadFromFile(Param);
- sngArray := ArrayDef.PSingleArray(MStream.Memory);
- // Now move the singles from the file into the destination array
- Result := Min(Maxvalues, MStream.Size div sizeof(Single)); // no. of singles
- for i := 1 to Result do
- begin
- ResultArray^[i] := sngArray[i]; // Single to Double
- end;
- end
- else
- DoSimpleMsg(DSS, Format('File of Singles "%s" not found.', [Param]), 70502);
+ ResultArray^[i] := sngArray[i]; // Single to Double
+ end;
MStream.Free;
end
else
@@ -921,15 +645,13 @@ function InterpretDblArray(DSS: TDSSContext; const s: String; MaxValues: Integer
end;
end;
-//----------------------------------------------------------------------------
function InterpretDblArrayMMF(DSS: TDSSContext; mmPtr: pByte; FileType: TLSFileType; Column, INDEX, DataSize: Integer): double;
-{ Gets the value for the value at INDEX within the file mapped (mmPtr)
- Considers the flags FileType, Column for locating the data within the file
- FileType :
- 0 - normal file (ANSI char)
- 1 - dblfile
- 2 - sngfile
-}
+// Gets the value for the value at INDEX within the file mapped (mmPtr)
+// Considers the flags FileType, Column for locating the data within the file
+// FileType :
+// 0 - normal file (ANSI char)
+// 1 - dblfile
+// 2 - sngfile
var
DBLByteArray: array[0..7] of byte;
SGLByteArray: array[0..3] of byte;
@@ -975,7 +697,7 @@ function InterpretDblArrayMMF(DSS: TDSSContext; mmPtr: pByte; FileType: TLSFileT
except
on E:Exception Do
begin
- DoSimpleMsg(DSS, Format('Error reading %d-th numeric array value. Error is:', [i, E.message]), 785);
+ DoSimpleMsg(DSS, 'Error reading %d-th numeric array value. Error is:', [i, E.message], 785);
Result := i - 1;
end;
end;
@@ -1002,15 +724,13 @@ function InterpretDblArrayMMF(DSS: TDSSContext; mmPtr: pByte; FileType: TLSFileT
End;
function InterpretIntArray(DSS: TDSSContext; const s: String; MaxValues: Integer; ResultArray: pIntegerArray): Integer;
-
-{ Get numeric values from an array specified either as a list on numbers or a text file spec.
- ResultArray must be allocated to MaxValues by calling routine.
- File is assumed to have one value per line.}
-
+// Get numeric values from an array specified either as a list on numbers or a text file spec.
+// ResultArray must be allocated to MaxValues by calling routine.
+// File is assumed to have one value per line.
var
ParmName,
Param: String;
- F: TBufferedFileStream = nil;
+ F: TStream = nil;
i: Integer;
line: String;
begin
@@ -1019,13 +739,13 @@ function InterpretIntArray(DSS: TDSSContext; const s: String; MaxValues: Integer
Param := DSS.AuxParser.StrValue;
Result := Maxvalues; // Default return value
- {Syntax can be either a list of numeric values or a file specification: File= ...}
+ // Syntax can be either a list of numeric values or a file specification: File= ...
if CompareText(Parmname, 'file') = 0 then
begin
// load the list from a file
try
- F := TBufferedFileStream.Create(AdjustInputFilePath(DSS, Param), fmOpenRead);
+ F := DSS.GetROFileStream(Param);
for i := 1 to MaxValues do
begin
if (F.Position + 1) < F.Size then
@@ -1043,7 +763,7 @@ function InterpretIntArray(DSS: TDSSContext; const s: String; MaxValues: Integer
except
On E: Exception do
- DoSimpleMsg(DSS, 'Error trying to read numeric array values from file: "' + Param + '" Error is: ' + E.message, 706);
+ DoSimpleMsg(DSS, 'Error trying to read numeric array values from file "%s". Error is: %s', [Param, E.Message], 706);
end;
end
else
@@ -1059,26 +779,26 @@ function InterpretIntArray(DSS: TDSSContext; const s: String; MaxValues: Integer
end;
function InterpretTimeStepSize(DSS: TDSSContext; const s: String): Double;
-{Return stepsize in seconds}
+// Return stepsize in seconds
var
Code: Integer;
ch: Char;
s2: String;
begin
- {Try to convert and see if we get an error}
+ // Try to convert and see if we get an error
val(s, Result, Code);
if Code = 0 then
Exit; // Only a number was specified, so must be seconds
- {Error occurred so must have a units specifier}
+ // Error occurred so must have a units specifier
ch := s[Length(s)]; // get last character
s2 := copy(s, 1, Length(s) - 1);
Val(S2, Result, Code);
if Code > 0 then
- begin {check for error}
+ begin // check for error
Result := DSS.ActiveCircuit.solution.DynaVars.h; // Don't change it
- DoSimpleMsg(DSS, 'Error in specification of StepSize: ' + s, 99933);
+ DoSimpleMsg(DSS, 'Error in specification of StepSize: %s', [s], 99933);
Exit;
end;
case ch of
@@ -1089,158 +809,36 @@ function InterpretTimeStepSize(DSS: TDSSContext; const s: String): Double;
's': ; // Do nothing
else
Result := DSS.ActiveCircuit.solution.DynaVars.h; // Don't change it
- DoSimpleMsg(DSS, 'Error in specification of StepSize: "' + s + '" Units can only be h, m, or s (single char only) ', 99934);
+ DoSimpleMsg(DSS, 'Error in specification of StepSize: "%s". Units can only be h, m, or s (single char only)', [s], 99934);
end;
-
end;
-
-//----------------------------------------------------------------------------
-procedure InitStringToNull(var S: String);
-
-begin
- Move(ZeroNull, S, 4);
-end;
-
-//----------------------------------------------------------------------------
-procedure InterpretAndAllocStrArray(DSS: TDSSContext; const s: String; var Size: Integer; var ResultArray: pStringArray);
-
-{ Get string values from an array specified either as a list on strings or a text file spec.
- ResultArray is allocated as needed.
- File is assumed to have one value per line.}
-
-var
- ParmName,
- Param: String;
- F: TBufferedFileStream = nil;
- MaxSize: Integer;
-
-
- procedure ReallocStringArray;
- var
- j: Integer;
- begin
- Reallocmem(ResultArray, Sizeof(ResultArray^[1]) * MaxSize);
- for j := Size + 1 to MaxSize do
- InitStringToNull(ResultArray^[j]); // Init string values
- end;
-
- procedure BumpUpStringArray;
- begin
- Inc(MaxSize, 100);
- ReallocStringArray;
- end;
-
- procedure FreeStringArray;
- var
- j: Integer;
- begin
- if Assigned(ResultArray) then
- begin
- for j := 1 to Size do
- begin
- ResultArray^[j] := '';
- end;
- ReallocMem(ResultArray, 0);
- end;
- end;
-
-begin
-
- // Throw Away any Previous Allocation
- FreeStringArray;
-
- // Now Reallocate
- MaxSize := 100; // initialize
- Size := 0;
- ReAllocStringArray;
-
- DSS.AuxParser.CmdString := S;
- ParmName := DSS.AuxParser.NextParam;
- Param := DSS.AuxParser.StrValue;
-
- {Syntax can be either a list of string values or a file specification: File= ...}
-
- if CompareText(Parmname, 'file') = 0 then
- begin
- Param := AdjustInputFilePath(DSS, Param);
- // load the list from a file
-
- try
- F := TBufferedFileStream.Create(Param, fmOpenRead);
- while (F.Position + 1) < F.Size do
- begin
- FSReadln(F, Param);
- if Param <> '' then
- begin // Ignore Blank Lines in File
- Inc(Size);
- if Size > Maxsize then
- BumpUpStringArray;
- ResultArray^[Size] := Param;
- end;
- end;
- FreeAndNil(F);
-
- except
- On E: Exception do
- DoSimpleMsg(DSS, 'Error trying to read numeric array values from a file. Error is: ' + E.message, 707);
- end;
-
-
- end
- else
- begin // Parse list of values off input string
-
- // Parse Values of array list
- while Param <> '' do
- begin
- Inc(Size);
- if Size > Maxsize then
- BumpUpStringArray;
- ResultArray^[Size] := Param;
- ParmName := DSS.AuxParser.NextParam;
- Param := DSS.AuxParser.StrValue;
- end;
- end;
-
- MaxSize := Size; // Get rid of Excess Allocation
- ReallocStringArray;
-
-end;
-
-//----------------------------------------------------------------------------
procedure InterpretTStringListArray(DSS: TDSSContext; const s: String; var ResultList: TStringList);
-
-{ Get string values from an array specified either as a list on strings or a text file spec.
- ResultArray is allocated as needed.
- File is assumed to have one value per line.}
-
+// Get string values from an array specified either as a list on strings or a text file spec.
+// ResultArray is allocated as needed.
+// File is assumed to have one value per line.
var
ParmName,
Param,
NextParam: String;
- F: TBufferedFileStream = nil;
-
-
+ F: TStream = nil;
begin
-
- // Throw Away any Previous Allocation
- ResultList.Clear;
-
+ // Throw Away any Previous Allocation
+ if ResultList = NIL then
+ ResultList := TStringList.Create()
+ else
+ ResultList.Clear;
DSS.AuxParser.CmdString := S;
ParmName := DSS.AuxParser.NextParam;
Param := DSS.AuxParser.StrValue;
- {Syntax can be either a list of string values or a file specification: File= ...}
-
+ // Syntax can be either a list of string values or a file specification: File= ...
if CompareText(Parmname, 'file') = 0 then
begin
- Param := AdjustInputFilePath(DSS, Param);
// load the list from a file
-
try
- F := TBufferedFileStream.Create(Param, fmOpenRead);
+ F := DSS.GetROFileStream(Param);
while (F.Position + 1) < F.Size do
begin
FSReadln(F, Param);
@@ -1256,14 +854,11 @@ procedure InterpretTStringListArray(DSS: TDSSContext; const s: String; var Resul
except
On E: Exception do
- DoSimpleMsg(DSS, 'Error trying to read numeric array values from a file. Error is: ' + E.message, 708);
+ DoSimpleMsg(DSS, 'Error trying to read lines from a file. Error is: %s', [E.message], 708);
end;
-
-
end
else
begin // Parse list of values off input string
-
// Parse Values of array list
while Param <> '' do
begin
@@ -1272,32 +867,13 @@ procedure InterpretTStringListArray(DSS: TDSSContext; const s: String; var Resul
Param := DSS.AuxParser.StrValue;
end;
end;
-
end;
-function InterpretCoreType(const str: String): Integer;
-begin
- case str[1] of
- '1':
- Result := 1; // 1-phase
- '3':
- Result := 3; // 3-Leg
- '5':
- Result := 5; // 5-Leg
- else
- Result := 0; // default to shell
- end;
-end;
-
-//----------------------------------------------------------------------------
procedure ParseObjectClassandName(DSS: TDSSContext; const FullObjName: String; var ClassName, ObjName: String);
-
var
dotpos: Integer;
-
begin
-
- // Split off Obj class and name
+ // Split off Obj class and name
dotpos := Pos('.', FullObjName);
case dotpos of
0:
@@ -1312,198 +888,62 @@ procedure ParseObjectClassandName(DSS: TDSSContext; const FullObjName: String; v
end;
end;
- // Check object name in case it is a variable
+ // Check object name in case it is a variable
DSS.Parser.CheckforVar(ObjName);
-
-end;
-
-function GetSolutionModeIDName(idx: TSolveMode): String;
-begin
-
- case idx of
-
- TSolveMode.SNAPSHOT:
- Result := 'Snap';
- TSolveMode.DAILYMODE:
- Result := 'Daily';
- TSolveMode.YEARLYMODE:
- Result := 'Yearly';
- TSolveMode.MONTECARLO1:
- Result := 'M1';
- TSolveMode.MONTECARLO2:
- Result := 'M2';
- TSolveMode.MONTECARLO3:
- Result := 'M3';
- TSolveMode.LOADDURATION1:
- Result := 'LD1';
- TSolveMode.LOADDURATION2:
- Result := 'LD2';
- TSolveMode.PEAKDAY:
- Result := 'Peakday';
- TSolveMode.DUTYCYCLE:
- Result := 'DUtycycle';
- TSolveMode.DIRECT:
- Result := 'DIrect';
- TSolveMode.DYNAMICMODE:
- Result := 'DYnamic';
- TSolveMode.MONTEFAULT:
- Result := 'MF';
- TSolveMode.FAULTSTUDY:
- Result := 'Faultstudy';
- TSolveMode.AUTOADDFLAG:
- Result := 'Autoadd';
- TSolveMode.HARMONICMODE:
- Result := 'Harmonic';
- TSolveMode.HARMONICMODET:
- Result := 'HarmonicT';
- TSolveMode.GENERALTIME:
- Result := 'Time';
- else
- Result := 'UNKNOWN'
- end;
-
-end;
-
-function GetSolutionModeID(DSS: TDSSContext): String;
-
-begin
- Result := 'UNKNOWN';
- if DSS.ActiveCircuit <> NIL then
- Result := GetSolutionModeIDName(DSS.ActiveCircuit.Solution.mode);
-end;
-
-function GetControlModeID(DSS: TDSSContext): String;
-
-begin
- Result := 'Unknown';
- if DSS.ActiveCircuit <> NIL then
- case DSS.ActiveCircuit.Solution.Controlmode of
- CTRLSTATIC:
- Result := 'STATIC';
- EVENTDRIVEN:
- Result := 'EVENT';
- TIMEDRIVEN:
- Result := 'TIME';
- MULTIRATE:
- Result := 'MULTIRATE';
- CONTROLSOFF:
- Result := 'OFF';
- else
- Result := 'UNKNOWN'
- end;
-
-end;
-
-function GetRandomModeID(DSS: TDSSContext): String;
-
-begin
- Result := 'Unknown';
- if DSS.ActiveCircuit <> NIL then
- case DSS.ActiveCircuit.Solution.RandomType of
-
- 0:
- Result := 'None';
- GAUSSIAN:
- Result := 'Gaussian';
- UNIFORM:
- Result := 'Uniform';
- LOGNORMAL:
- Result := 'LogNormal';
- else
- Result := 'Unknown'
- end;
-
-end;
-
-function GetLoadModel(DSS: TDSSContext): String;
-begin
-
- case DSS.ActiveCircuit.solution.LoadModel of
- ADMITTANCE:
- Result := 'Admittance';
- else
- Result := 'PowerFlow'
- end;
-
-
end;
procedure ParseIntArray(DSS: TDSSContext; var iarray: pIntegerArray; var count: Integer; const s: String);
-
var
- // paramName: String;
param: String;
i: Integer;
-
begin
-
-// Parse the line once to get the count of tokens on string, S
+ // Parse the line once to get the count of tokens on string, S
DSS.AuxParser.cmdString := S;
Count := 0;
repeat
- {ParamName :=} DSS.AuxParser.NextParam;
+ DSS.AuxParser.NextParam;
Param := DSS.AuxParser.StrValue;
if Length(Param) > 0 then
Inc(Count);
until Length(Param) = 0;
-// reallocate iarray to new size
+ // reallocate iarray to new size
ReallocMem(iarray, sizeof(iarray^[1]) * count);
-// Parse again for real
+ // Parse again for real
DSS.AuxParser.cmdString := S;
for i := 1 to Count do
begin
- {ParamName :=} DSS.AuxParser.NextParam;
+ DSS.AuxParser.NextParam;
iarray^[i] := DSS.AuxParser.IntValue;
end;
-
-
end;
function IsShuntElement(const Elem: TDSSCktElement): Boolean;
begin
-
case (Elem.DSSObjType and CLASSMASK) of
-
CAP_ELEMENT:
Result := TCapacitorObj(Elem).IsShunt;
REACTOR_ELEMENT:
Result := TReactorObj(Elem).IsShunt;
-
else
Result := FALSE;
end;
-
end;
function IsLineElement(const Elem: TDSSCktElement): Boolean;
begin
-
- if ((Elem.DSSObjType and CLASSMASK) = LINE_ELEMENT) then
- Result := TRUE
- else
- Result := FALSE;
-
+ Result := ((Elem.DSSObjType and CLASSMASK) = LINE_ELEMENT)
end;
-
function IsTransformerElement(const Elem: TDSSCktElement): Boolean;
begin
-
- if ((Elem.DSSObjType and CLASSMASK) = XFMR_ELEMENT) then
- Result := TRUE
- else
- Result := FALSE;
-
+ Result := ((Elem.DSSObjType and CLASSMASK) = XFMR_ELEMENT)
end;
-
-//----------------------------------------------------------------------------
function GetCktElementIndex(DSS: TDSSContext; const FullObjName: String): Integer; //TODO: merge with TDSSCircuit.SetElementActive
// Given the full object name, return the index to the circuit element in the
// active circuit. Use full name if given. Else assume last class referenced.
-
var
DevIndex: Integer;
DevClassIndex: Integer;
@@ -1544,44 +984,37 @@ function GetCktElementIndex(DSS: TDSSContext; const FullObjName: String): Intege
Devindex := DSS.ActiveCircuit.Devicelist.FindNext; // Could be duplicates
end;
end;
-
end;
-//----------------------------------------------------------------------------
function Str_Real(const Value: Double; NumDecimals: Integer): String;
begin
try
-// Str(Value:0:NumDecimals, Result);
Result := FloatToStrF(Value, ffFixed, 0, NumDecimals);
except
Result := '*****';
end;
-
end;
-
-// - - - - - --------------------------------------------------
function ReplaceCRLF(const S: String): String;
begin
- {Replace CRLF with a \n character sequence}
+ // Replace CRLF with a \n character sequence
Result := StringReplace(S, CRLF, '\n', [rfReplaceAll]);
end;
-// - - - - - --------------------------------------------------
procedure DumpAllocationFactors(DSS: TDSSContext; var FileName: String);
-
var
F: TFileStream = nil;
pLoad: TLoadObj;
-
begin
-
try
- F := TFileStream.Create(FileName, fmCreate);
+ F := TBufferedFileStream.Create(FileName, fmCreate);
except
On E: Exception do
begin
- DoErrorMsg(DSS, 'Error opening ' + FileName + ' for writing.', E.Message, ' File protected or other file error.', 709);
+ DoErrorMsg(DSS,
+ Format(_('Error opening "%s" for writing.'), [FileName]),
+ E.Message,
+ _('File protected or other file error.'), 709);
FreeAndNil(F);
Exit;
end;
@@ -1599,87 +1032,75 @@ procedure DumpAllocationFactors(DSS: TDSSContext; var FileName: String);
FSWriteln(F, 'Load.' + pLoad.Name + '.CFactor=' + Format('%-.5g', [pLoad.CFactor]));
end;
pLoad := Loads.Next;
- end; {While}
- end; {With}
+ end;
+ end;
FreeAndNil(F);
DSS.GlobalResult := FileName;
-
end;
-
-// - - - - - --------------------------------------------------
procedure DumpAllDSSCommands(DSS: TDSSContext; var FileName: String);
-
var
F: TFileStream = nil;
pClass: TDSSClass;
i: Integer;
sout: String;
begin
-
try
- FileName := DSS.OutputDirectory + 'DSSCommandsDump.Txt';
- F := TFileStream.Create(FileName, fmCreate);
+ FileName := DSS.OutputDirectory + 'DSSCommandsDump.txt';
+ F := TBufferedFileStream.Create(FileName, fmCreate);
except
On E: Exception do
begin
- DoErrorMsg(DSS, 'Error opening ' + FileName + ' for writing.', E.Message, 'Disk protected or other file error', 710);
+ DoErrorMsg(DSS,
+ Format(_('Error opening "%s" for writing.'), [FileName]),
+ E.Message,
+ _('Disk protected or other file error'), 710);
FreeAndNil(F);
Exit;
end;
end;
- // dump Executive commands
+ // dump Executive commands
FSWriteln(F, '[execcommands]');
for i := 1 to NumExecCommands do
begin
- WriteStr(sout, i: 0, ', "', Execcommand[i], '", "', ReplaceCRLF(CommandHelp[i]), '"');
+ WriteStr(sout, i: 0, ', "', Execcommand[i], '", "', ReplaceCRLF(DSSHelp('Command.' + ExecCommand[i])), '"');
FSWriteln(F, sout);
end;
- // Dump Executive Options
+ // Dump Executive Options
FSWriteln(F, '[execoptions]');
for i := 1 to NumExecOptions do
begin
- WriteStr(sout, i: 0, ', "', ExecOption[i], '", "', ReplaceCRLF(OptionHelp[i]), '"');
+ WriteStr(sout, i: 0, ', "', ExecOption[i], '", "', ReplaceCRLF(DSSHelp('Executive.' + ExecOption[i])), '"');
FSWriteln(F, sout);
end;
- // Dump All presend DSSClasses
- pClass := DSS.DSSClassList.First;
- while pClass <> NIL do
- begin
- FSWriteln(F, '[' + pClass.name + ']');
- for i := 1 to pClass.NumProperties do
- begin
- WriteStr(sout, i: 0, ', "', pClass.PropertyName^[i], '", "', ReplaceCRLF(pClass.PropertyHelp^[i]), '"');
- FSWriteln(F, sout);
- end;
- pClass := DSS.DSSClassList.Next;
- end;
-
-
+ // Dump All present DSSClasses
+ pClass := DSS.DSSClassList.First;
+ while pClass <> NIL do
+ begin
+ FSWriteln(F, '[' + pClass.name + ']');
+ for i := 1 to pClass.NumProperties do
+ begin
+ WriteStr(sout, i: 0, ', "', pClass.PropertyName^[i], '", "', ReplaceCRLF(pClass.GetPropertyHelp(i)), '"');
+ FSWriteln(F, sout);
+ end;
+ pClass := DSS.DSSClassList.Next;
+ end;
FreeAndNil(F);
-
-
-
end;
-//----------------------------------------------------------------------------
function NearestBasekV(DSS: TDSSContext; kV: Double): Double;
-
-{Find closest base voltage}
-
+// Find closest base voltage
var
TestkV: Double;
Count: Integer;
Diff,
MinDiff: Double;
-
begin
-
Count := 1;
TestkV := DSS.ActiveCircuit.LegalVoltageBases^[1];
Result := TestkV;
@@ -1697,12 +1118,9 @@ function NearestBasekV(DSS: TDSSContext; kV: Double): Double;
Inc(Count);
TestkV := DSS.ActiveCircuit.LegalVoltageBases^[Count];
end;
-
end;
-//----------------------------------------------------------------------------
function SavePresentVoltages(DSS: TDSSContext): Boolean;
-
var
F: TFileStream = nil;
i: Integer;
@@ -1710,12 +1128,12 @@ function SavePresentVoltages(DSS: TDSSContext): Boolean;
begin
Result := TRUE;
try
- F := TFileStream.Create(DSS.OutputDirectory + DSS.CircuitName_ + 'SavedVoltages.dbl', fmCreate);
+ F := TBufferedFileStream.Create(DSS.OutputDirectory + DSS.CircuitName_ + 'SavedVoltages.dbl', fmCreate);
except
On E: Exception do
begin
- DoSimpleMsg(DSS, 'Error opening/creating file to save voltages: ' + E.message, 711);
+ DoSimpleMsg(DSS, 'Error opening/creating file to save voltages: %s', [E.message], 711);
Result := FALSE;
Exit;
end;
@@ -1738,31 +1156,26 @@ function SavePresentVoltages(DSS: TDSSContext): Boolean;
except
On E: Exception do
begin
- DoSimpleMsg(DSS, 'Error writing file to save voltages: ' + E.message, 712);
+ DoSimpleMsg(DSS, 'Error writing file to save voltages: %s', [E.message], 712);
Result := FALSE;
end;
end;
-
-
end;
-//----------------------------------------------------------------------------
function RetrieveSavedVoltages(DSS: TDSSContext): Boolean;
-
var
F: TFileStream = nil;
i: Integer;
dNumNodes: Double;
begin
-
Result := TRUE;
try
- F := TFileStream.Create(DSS.OutputDirectory + DSS.CircuitName_ + 'SavedVoltages.dbl', fmOpenRead);
+ F := TBufferedFileStream.Create(DSS.OutputDirectory + DSS.CircuitName_ + 'SavedVoltages.dbl', fmOpenRead or fmShareDenyWrite);
except
On E: Exception do
begin
- DoSimpleMsg(DSS, 'Error opening file to retrieve saved voltages: ' + E.message, 713);
+ DoSimpleMsg(DSS, 'Error opening file to retrieve saved voltages: %s', [E.message], 713);
Result := FALSE;
Exit;
end;
@@ -1780,7 +1193,7 @@ function RetrieveSavedVoltages(DSS: TDSSContext): Boolean;
end
else
begin
- DoSimpleMsg('Saved results do not match present circuit. Aborting.', 714);
+ DoSimpleMsg(DSS, _('Saved results do not match present circuit. Aborting.'), 714);
Result := FALSE;
end;
end;
@@ -1790,29 +1203,22 @@ function RetrieveSavedVoltages(DSS: TDSSContext): Boolean;
except
On E: Exception do
begin
- DoSimpleMsg(DSS, 'Error reading file to retrieve saved voltages: ' + E.message, 715);
+ DoSimpleMsg(DSS, 'Error reading file to retrieve saved voltages: %s', [E.message], 715);
Result := FALSE;
end;
end;
-
-
end;
-//----------------------------------------------------------------------------
function InitializeForHarmonics(DSS: TDSSContext): Boolean;
-
-{Intialize PCELEMENT base values for harmonics analysis}
-
+// Initialize PCELEMENT base values for harmonics analysis
var
pcElem: TPCElement;
-
begin
-
if SavePresentVoltages(DSS) // Zap voltage vector to disk
then
with DSS.ActiveCircuit do
begin
- // Go through all PC Elements
+ // Go through all PC Elements
pcElem := PCElements.First;
while pcElem <> NIL do
begin
@@ -1821,25 +1227,17 @@ function InitializeForHarmonics(DSS: TDSSContext): Boolean;
pcElem := PCElements.Next;
end;
Result := TRUE;
- end {With}
+ end
else
Result := FALSE;
-
end;
-
-//----------------------------------------------------------------------------
procedure CalcInitialMachineStates(DSS: TDSSContext);
-
var
pcelem: TPCElement;
-
begin
-
-// Do All PC Elements
-
-// If state variables not defined for a PC class, does nothing
-
+ // Do All PC Elements
+ // If state variables not defined for a PC class, does nothing
with DSS.ActiveCircuit do
begin
pcelem := PCElements.First;
@@ -1851,19 +1249,13 @@ procedure CalcInitialMachineStates(DSS: TDSSContext);
pcelem := PCElements.Next;
end;
end;
-
end;
-//----------------------------------------------------------------------------
procedure InvalidateAllPCELEMENTS(DSS: TDSSContext);
-
var
pcelem: TPCElement;
-
begin
-
-// Invalidate All PC Elements; Any could be a machine
-
+ // Invalidate All PC Elements; Any could be a machine
with DSS.ActiveCircuit do
begin
pcelem := PCElements.First;
@@ -1877,15 +1269,6 @@ procedure InvalidateAllPCELEMENTS(DSS: TDSSContext);
end;
end;
-function PresentTimeInSec(DSS: TDSSContext): Double;
-
-begin
- with DSS.ActiveCircuit.Solution do
- Result := Dynavars.t + DynaVars.intHour * 3600.0;
-end;
-
-
-//----------------------------------------------------------------------------
function DoResetFaults(DSS: TDSSContext): Integer;
var
pFault: TFaultOBj;
@@ -1900,15 +1283,12 @@ function DoResetFaults(DSS: TDSSContext): Integer;
pFault.Reset;
pFault := TFaultObj(Faults.Next);
end;
- end; {End With}
+ end;
end;
-
-//----------------------------------------------------------------------------
function DoResetControls(DSS: TDSSContext): Integer;
var
ControlDevice: TControlElem;
-
begin
Result := 0;
with DSS.ActiveCircuit do
@@ -1920,10 +1300,9 @@ function DoResetControls(DSS: TDSSContext): Integer;
ControlDevice.Reset;
ControlDevice := DSSControls.Next;
end;
- end; {End With}
+ end;
end;
-//----------------------------------------------------------------------------
function GetNodeNum(DSS: TDSSContext; NodeRef: Integer): Integer;
begin
if NodeRef = 0 then
@@ -1932,32 +1311,20 @@ function GetNodeNum(DSS: TDSSContext; NodeRef: Integer): Integer;
Result := DSS.ActiveCircuit.MapNodeToBus^[NodeRef].NodeNum
end;
-
-//----------------------------------------------------------------------------
procedure RotatePhasorDeg(var Phasor: Complex; const h, AngleDeg: Double);
-
// rotate a phasor by an angle and harmonic
-
begin
-
- Phasor := Cmul(Phasor, pdegtocomplex(1.0, h * AngleDeg));
-
+ Phasor := Phasor * pdegtocomplex(1.0, h * AngleDeg);
end;
procedure RotatePhasorRad(var Phasor: Complex; const h, AngleRad: Double);
-
// rotate a phasor by an angle and harmonic
-
begin
-
- Phasor := Cmul(Phasor, pclx(1.0, h * AngleRad));
-
+ Phasor := Phasor * pclx(1.0, h * AngleRad);
end;
-//----------------------------------------------------------------------------
procedure ConvertComplexArrayToPowerandPF(const Buffer: pComplexArray; N: Integer);
-
-{Creates continous PF function from 1 to 2 where 1-2 range is leading (opposite sign)}
+// Creates continous PF function from 1 to 2 where 1-2 range is leading (opposite sign)
var
Mag, PF: Double;
i: Integer;
@@ -1971,9 +1338,7 @@ procedure ConvertComplexArrayToPowerandPF(const Buffer: pComplexArray; N: Intege
end;
begin
-
-{Assume we get P + jQ}
-
+ // Assume we get P + jQ
for i := 1 to N do
begin
Mag := Cabs(Buffer^[i]);
@@ -1989,8 +1354,6 @@ procedure ConvertComplexArrayToPowerandPF(const Buffer: pComplexArray; N: Intege
end;
end;
-
-//----------------------------------------------------------------------------
procedure ConvertComplexArrayToPolar(const Buffer: pComplexArray; N: Integer);
var
X: Polar;
@@ -2007,7 +1370,6 @@ procedure ConvertComplexArrayToPolar(const Buffer: pComplexArray; N: Integer);
end;
end;
-//----------------------------------------------------------------------------
function Residual(p: Pointer; Nph: Integer): Complex;
// Assume p points to complex array
// compute residual of the number of phases specified and convert to polar
@@ -2019,22 +1381,18 @@ function Residual(p: Pointer; Nph: Integer): Complex;
pc := p;
Result := CZERO;
for i := 1 to Nph do
- Caccum(Result, pc^[i]);
+ Result += pc^[i];
end;
-//----------------------------------------------------------------------------
function ResidualPolar(p: Pointer; Nph: Integer): Complex;
// Assume p points to complex array
// compute residual of the number of phases specified and convert to polar
-
var
x: Complex;
begin
-
x := Residual(p, Nph);
Result.re := Cabs(x);
Result.im := Cdang(x);
-
end;
function Powerfactor(const S: Complex): Double;
@@ -2054,79 +1412,47 @@ function Powerfactor(const S: Complex): Double;
Result := 1.0;
end;
-function ConvertPFToPFRange2(const value: Double): Double;
-{Convert PF from +/- 1 to 0..2 Where 1..2 is leading}
-begin
- if value < 0.0 then
- Result := 2.0 + Value
- else
- Result := Value;
-end;
-
-function ConvertPFRange2ToPF(const value: Double): Double;
-
-begin
- if value > 1.0 then
- Result := value - 2.0
- else
- Result := Value;
-end;
-
procedure ClearEventLog;
-
begin
try
-{**** WriteDLLDebugFile(Format('ClearEventLog: EventStrings= %p', [@EventStrings])); }
DSS.EventStrings.Clear;
except
On E: Exception do
- DoSimpleMsg(DSS, Format('Exception clearing event log: %s, @EventStrings=%p', [E.Message, @DSS.EventStrings]), 7151);
+ DoSimpleMsg(DSS, 'Exception clearing event log: %s, @EventStrings=%p', [E.Message, @DSS.EventStrings], 7151);
end;
end;
procedure ClearErrorLog(DSS: TDSSContext);
-
begin
try
-{**** WriteDLLDebugFile(Format('ClearEventLog: EventStrings= %p', [@EventStrings])); }
DSS.ErrorStrings.Clear;
except
On E: Exception do
- DoSimpleMsg(DSS, Format('Exception clearing error log: %s, @EventStrings=%p', [E.Message, @DSS.EventStrings]), 71511);
+ DoSimpleMsg(DSS, 'Exception clearing error log: %s, @EventStrings=%p', [E.Message, @DSS.EventStrings], 71511);
end;
end;
procedure LogThisEvent(DSS: TDSSContext; const EventName: String);
-
begin
- {**** WriteDLLDebugFile(Format('LogThisEvent: EventStrings= %p', [@EventStrings])); }
with DSS.ActiveCircuit.Solution do
DSS.EventStrings.Add(Format('Hour=%d, Sec=%-.8g, Iteration=%d, ControlIter=%d, Event=%s',
[DynaVars.intHour, Dynavars.t, iteration, ControlIteration, EventName]));
-
- // 'Time=' + TimeToStr(Time)+': '+EventName);
- {**** ShowMessageForm(EventStrings); }
end;
procedure AppendToEventLog(DSS: TDSSContext; const opdev: String; const action: String);
var
S: String;
-
begin
- {**** WriteDLLDebugFile(Format('LogThisEvent: EventStrings= %p', [@EventStrings])); }
with DSS.ActiveCircuit.Solution do
S := Format('Hour=%d, Sec=%-.5g, ControlIter=%d, Element=%s, Action=%s',
- [DynaVars.intHour, Dynavars.t, ControlIteration, OpDev, Uppercase(action)]);
+ [DynaVars.intHour, Dynavars.t, ControlIteration, OpDev, AnsiUpperCase(action)]);
DSS.EventStrings.Add(S);
- {**** ShowMessageForm(EventStrings); }
end;
procedure DumpComplexMatrix(DSS: TDSSContext; F: TFileStream; AMatrix: TcMatrix);
-
var
i, j: Integer;
-
begin
try
if AMatrix <> NIL then
@@ -2155,12 +1481,10 @@ procedure DumpComplexMatrix(DSS: TDSSContext; F: TFileStream; AMatrix: TcMatrix)
except
On E: Exception do
begin
- DoSimpleMsg(DSS, 'Error in Dump Complex Matrix: ' + E.message + ' Write aborted.', 716);
+ DoSimpleMsg(DSS, 'Write aborted. Error in Dump Complex Matrix: %s', [E.message], 716);
end;
end;
-
-
end;
function AllTerminalsClosed(ThisElement: TDSSCktElement): Boolean;
@@ -2168,7 +1492,6 @@ function AllTerminalsClosed(ThisElement: TDSSCktElement): Boolean;
// Make sure at least one phase on each terminal is closed.
var
i, j: Integer;
-
begin
Result := FALSE;
for i := 1 to ThisElement.Nterms do
@@ -2186,11 +1509,9 @@ function AllTerminalsClosed(ThisElement: TDSSCktElement): Boolean;
end;
end;
-
function WriteVsourceClassFile(DSS: TDSSContext; const DSS_Class: TDSSClass; IsCktElement: Boolean): Boolean;
-{Special Function to write the Vsource class and change the DSS command of the first Source
- so that there is no problem with duplication when the circuit is subsequently created}
-
+// Special Function to write the Vsource class and change the DSS command of the first Source
+// so that there is no problem with duplication when the circuit is subsequently created
var
F: TFileStream = nil;
ClassName: String;
@@ -2200,17 +1521,17 @@ function WriteVsourceClassFile(DSS: TDSSContext; const DSS_Class: TDSSClass; IsC
Exit;
try
ClassName := DSS_Class.Name;
- F := TFileStream.Create(DSS.CurrentDSSDir + ClassName + '.dss', fmCreate);
- DSS.SavedFileList.Add(ClassName + '.dss');
+ F := TBufferedFileStream.Create(DSS.CurrentDSSDir + ClassName + '.dss', fmCreate);
+ DSS.SavedFileList.Add(DSS.CurrentDSSDir + ClassName + '.dss');
DSS_Class.First; // Sets DSS.ActiveDSSObject
WriteActiveDSSObject(DSS, F, 'Edit'); // Write First Vsource out as an Edit
while DSS_Class.Next > 0 do
begin
- // Skip Cktelements that have been checked before and written out by
- // something else
- if TDSSCktElement(DSS.ActiveDSSObject).HasBeenSaved then
+ // Skip Cktelements that have been checked before and written out by
+ // something else
+ if Flg.HasBeenSaved in TDSSCktElement(DSS.ActiveDSSObject).Flags then
Continue;
- // Skip disabled circuit elements; write all general DSS objects
+ // Skip disabled circuit elements; write all general DSS objects
WriteActiveDSSObject(DSS, F, 'New'); // sets HasBeenSaved := TRUE
end;
DSS_Class.Saved := TRUE;
@@ -2218,23 +1539,20 @@ function WriteVsourceClassFile(DSS: TDSSContext; const DSS_Class: TDSSClass; IsC
except
On E: Exception do
begin
- DoSimpleMsg(DSS, 'WriteClassFile Error: ' + E.Message, 717);
+ DoSimpleMsg(DSS, 'WriteClassFile Error: %s', [E.Message], 717);
Result := FALSE;
end;
end;
FreeAndNil(F);
-
end;
function WriteClassFile(DSS: TDSSContext; const DSS_Class: TDSSClass; FileName: String; IsCktElement: Boolean): Boolean;
-
var
F: TFileStream = nil;
ClassName: String;
Nrecords: Integer;
ParClass: TDssClass;
begin
-
Result := TRUE;
if DSS_Class.ElementCount = 0 then
@@ -2243,23 +1561,22 @@ function WriteClassFile(DSS: TDSSContext; const DSS_Class: TDSSClass; FileName:
try
ClassName := DSS_Class.Name;
if Length(FileName) = 0 then
- FileName := DSS.CurrentDSSDir + ClassName + '.DSS'; // default file name
- F := TFileStream.Create(FileName, fmCreate);
+ FileName := DSS.CurrentDSSDir + ClassName + '.dss'; // default file name
+ F := TBufferedFileStream.Create(FileName, fmCreate);
Nrecords := 0;
DSS_Class.First; // Sets ActiveDSSObject
repeat
-
- // Skip Cktelements that have been checked before and written out by
- // something else
+ // Skip Cktelements that have been checked before and written out by
+ // something else
if IsCktElement then
with TDSSCktElement(DSS.ActiveDSSObject) do
- if HasBeenSaved or (not Enabled) then
+ if (Flg.HasBeenSaved in Flags) or (not Enabled) then
Continue;
ParClass := DSS.ActiveDSSObject.ParentClass;
- if LowerCase(ParClass.Name) = 'loadshape' then
+ if AnsiLowerCase(ParClass.Name) = 'loadshape' then
if not TLoadShapeObj(DSS.ActiveDSSObject).Enabled then
continue;
WriteActiveDSSObject(DSS, F, 'New'); // sets HasBeenSaved := TRUE
@@ -2279,90 +1596,69 @@ function WriteClassFile(DSS: TDSSContext; const DSS_Class: TDSSClass; FileName:
except
On E: Exception do
begin
- DoSimpleMsg(DSS, 'WriteClassFile Error: ' + E.Message, 718);
+ DoSimpleMsg(DSS, 'WriteClassFile Error: %s', [E.Message], 718);
Result := FALSE;
end;
end;
FreeAndNil(F);
-
end;
function checkforblanks(const S: String): String;
-{Checks for blanks in the name and puts quotes around it}
+// Checks for blanks in the name and puts quotes around it
begin
Result := s;
if Pos(' ', S) > 0 then
- if S[1] <> '(' then // Ignore if already quoted
- if S[1] <> '[' then // Ignore if already quoted
- if S[1] <> '{' then // Ignore if already quoted
- Result := '"' + S + '"';
+ if (S[1] <> '(') and (S[1] <> '[') and (S[1] <> '{') and (S[1] <> '"') and (S[1] <> '''') then // Ignore if already quoted
+ Result := '"' + S + '"';
end;
-
procedure WriteActiveDSSObject(DSS: TDSSContext; F: TFileStream; const NeworEdit: String);
-
-var
- ParClass: TDssClass;
-
-
begin
- ParClass := DSS.ActiveDSSObject.ParentClass;
- // FSWrite(F, NeworEdit, ' "', ParClass.Name + '.' + DSS.ActiveDSSObject.Name,'"');
- FSWrite(F, Format('%s "%s.%s"', [NeworEdit, ParClass.Name, DSS.ActiveDSSObject.Name]));
+ // FSWrite(F, NeworEdit, ' "', DSS.ActiveDSSObject.FullName,'"');
+ FSWrite(F, Format('%s "%s"', [NeworEdit, DSS.ActiveDSSObject.FullName]));
DSS.ActiveDSSObject.SaveWrite(F);
-
- // Handle disabled circuit elements; Modified to allow applets to save disabled elements 12-28-06
+ // Handle disabled circuit elements; Modified to allow applets to save disabled elements 12-28-06
if (DSS.ActiveDSSObject.DSSObjType and ClassMask) <> DSS_Object then
if not TDSSCktElement(DSS.ActiveDSSObject).Enabled then
FSWrite(F, ' ENABLED=NO');
FSWriteln(F); // Terminate line
- DSS.ActiveDSSObject.HasBeenSaved := TRUE;
-
+ Include(DSS.ActiveDSSObject.Flags, Flg.HasBeenSaved);
end;
procedure DoResetKeepList(DSS: TDSSContext);
-
var
i: Integer;
-
begin
-
with DSS.ActiveCircuit do
for i := 1 to NumBuses do
Buses^[i].Keep := FALSE;
-
end;
function ExtractComment(const s: String): String;
-
begin
-
Result := copy(s, pos('!', s), Length(s));
-
end;
function RewriteAlignedFile(DSS: TDSSContext; const Filename: String): Boolean;
-
var
- Fin: TBufferedFileStream = nil;
+ Fin: TStream = nil;
Fout: TFileStream = nil;
SaveDelims, Line, Field, AlignedFile: String;
FieldLength: pIntegerArray;
ArraySize, FieldLen, FieldNum: Integer;
-
begin
Result := TRUE;
try
- Fin := TBufferedFileStream.Create(FileName, fmOpenRead);
+ Fin := DSS.GetROFileStream(FileName);
except
On E: Exception do
begin
- DoSimpleMsg(DSS, 'Error opening file: ' + Filename + ', ' + E.message, 719);
+ DoSimpleMsg(DSS, 'Error opening file "%s": %s', [Filename, E.message], 719);
Result := FALSE;
Exit
end;
@@ -2370,11 +1666,11 @@ function RewriteAlignedFile(DSS: TDSSContext; const Filename: String): Boolean;
try
AlignedFile := ExtractFilePath(FileName) + 'Aligned_' + ExtractFileName(FileName);
- Fout := TFileStream.Create(AlignedFile, fmCreate);
+ Fout := TBufferedFileStream.Create(AlignedFile, fmCreate);
except
On E: Exception do
begin
- DoSimpleMsg(DSS, 'Error opening file: ' + AlignedFile + ', ' + E.message, 720);
+ DoSimpleMsg(DSS, 'Error opening file "%s": %s', [AlignedFile, E.message], 720);
FreeAndNil(Fin);
Result := FALSE;
Exit;
@@ -2387,11 +1683,11 @@ function RewriteAlignedFile(DSS: TDSSContext; const Filename: String): Boolean;
FieldLength := Allocmem(Sizeof(Integer) * ArraySize);
try
- {Scan once to set field lengths}
+ // Scan once to set field lengths
while (Fin.Position + 1) < Fin.Size do
begin
FSReadln(Fin, line);
- DSS.AuxParser.CmdString := Line; // Load the parsr
+ DSS.AuxParser.CmdString := Line; // Load the parser
FieldNum := 0;
repeat
DSS.AuxParser.NextParam;
@@ -2417,7 +1713,7 @@ function RewriteAlignedFile(DSS: TDSSContext; const Filename: String): Boolean;
end;
- {Now go back and re-read while writing the new file}
+ // Now go back and re-read while writing the new file
Fin.Seek(0, soBeginning);
if (Fin.Position + 1) < Fin.Size then
@@ -2443,9 +1739,7 @@ function RewriteAlignedFile(DSS: TDSSContext; const Filename: String): Boolean;
FSWriteln(Fout);
end;
-
- finally {Make sure we do this stuff ...}
-
+ finally // Make sure we do this stuff ...
FreeAndNil(Fin);
FreeAndNil(Fout);
@@ -2455,19 +1749,16 @@ function RewriteAlignedFile(DSS: TDSSContext; const Filename: String): Boolean;
end;
DSS.GlobalResult := AlignedFile;
-
end;
function DoExecutiveCommand(DSS: TDSSContext; const s: String): Integer;
-
begin
DSS.DSSExecutive.command := S;
Result := DSS.DSSExecutive.Error;
end;
-
function CheckParallel(const Line1, Line2: TDSSCktElement): Boolean;
- {Check to see if two lines are in parallel}
+// Check to see if two lines are in parallel
begin
Result := FALSE;
if Line1.Terminals[0].BusRef = Line2.Terminals[0].BusRef then
@@ -2549,28 +1840,23 @@ function GetMinPUVoltage(DSS: TDSSContext; IgnoreNeutrals: Boolean): Double;
if not MinFound then
Result := -1.0;
-
end;
function GetTotalPowerFromSources(DSS: TDSSContext): Complex;
-
var
CktElem: TDSSCktElement;
-
begin
Result := CZERO;
cktElem := DSS.ActiveCircuit.Sources.First;
while CktElem <> NIL do
begin
- //----CktElem.ActiveTerminalIdx := 1;
- Caccum(Result, Cnegate(CktElem.power[1]));
+ Result -= CktElem.power[1];
cktElem := DSS.ActiveCircuit.Sources.Next;
end;
end;
procedure WriteUniformGenerators(DSS: TDSSContext; F: TFileStream; kW, PF: Double; DoGenerators: Boolean);
- { Distribute the generators uniformly amongst the feeder nodes that have loads}
-
+// Distribute the generators uniformly amongst the feeder nodes that have loads
var
kWeach: Double;
LoadClass: TDSSClass;
@@ -2607,8 +1893,7 @@ procedure WriteUniformGenerators(DSS: TDSSContext; F: TFileStream; kW, PF: Doubl
end;
procedure WriteRandomGenerators(DSS: TDSSContext; F: TFileStream; kW, PF: Double; DoGenerators: Boolean);
-{Distribute Generators randomly to loaded buses}
-
+// Distribute Generators randomly to loaded buses
var
kWeach: Double;
LoadClass: TDSSClass;
@@ -2618,7 +1903,7 @@ procedure WriteRandomGenerators(DSS: TDSSContext; F: TFileStream; kW, PF: Double
begin
LoadClass := GetDSSClassPtr(DSS, 'load');
Count := LoadClass.ElementList.Count;
- {Count enabled loads}
+ // Count enabled loads
LoadCount := 0;
for i := 1 to Count do
begin
@@ -2634,7 +1919,7 @@ procedure WriteRandomGenerators(DSS: TDSSContext; F: TFileStream; kW, PF: Double
randomize;
- {Place random sizes on load buses so that total is approximately what was spec'd}
+ // Place random sizes on load buses so that total is approximately what was spec'd
for i := 1 to Count do
begin
pLoad := TLoadObj(LoadClass.ElementList.Get(i));
@@ -2654,17 +1939,12 @@ procedure WriteRandomGenerators(DSS: TDSSContext; F: TFileStream; kW, PF: Double
FSWriteln(F);
end;
end;
-
-
end;
procedure WriteEveryOtherGenerators(DSS: TDSSContext; F: TFileStream; kW, PF: Double; Skip: Integer; DoGenerators: Boolean);
+// distribute generators on every other load, skipping the number specified
-{distribute generators on every other load, skipping the number specified}
-
-{Distribute the generator Proportional to load}
-
-
+// Distribute the generator Proportional to load
var
kWeach, TotalkW: Double;
LoadClass: TDSSClass;
@@ -2672,17 +1952,16 @@ procedure WriteEveryOtherGenerators(DSS: TDSSContext; F: TFileStream; kW, PF: Do
Count, i, skipcount: Integer;
begin
-
LoadClass := GetDSSClassPtr(DSS, 'load');
Count := LoadClass.ElementList.Count;
- {Add up the rated load in the enabled loads where gens will be placed}
+ // Add up the rated load in the enabled loads where gens will be placed
TotalkW := 0.0;
Skipcount := Skip;
for i := 1 to Count do
begin
pLoad := TLoadObj(LoadClass.ElementList.Get(i));
if pLoad.Enabled then
- {Do not count skipped loads}
+ // Do not count skipped loads
if Skipcount = 0 then
begin
TotalkW := TotalkW + pLoad.kWBase; // will be right value if pos seq, too
@@ -2721,25 +2000,20 @@ procedure WriteEveryOtherGenerators(DSS: TDSSContext; F: TFileStream; kW, PF: Do
else
Dec(SkipCount);
end;
-
end;
procedure WriteProportionalGenerators(DSS: TDSSContext; F: TFileStream; kW, PF: Double; DoGenerators: Boolean);
-
-{Distribute the generator Proportional to load}
-
-
+// Distribute the generator Proportional to load
var
kWeach,
TotalkW: Double;
LoadClass: TDSSClass;
pLoad: TLoadObj;
Count, i: Integer;
-
begin
LoadClass := GetDSSClassPtr(DSS, 'load');
Count := LoadClass.ElementList.Count;
- {Add up the rated load in the enabled loads}
+ // Add up the rated load in the enabled loads
TotalkW := 0.0;
for i := 1 to Count do
begin
@@ -2774,27 +2048,29 @@ procedure WriteProportionalGenerators(DSS: TDSSContext; F: TFileStream; kW, PF:
FSWriteln(F);
end;
end;
-
end;
procedure MakeDistributedGenerators(DSS: TDSSContext; kW, PF: Double; How: String; Skip: Integer; Fname: String; DoGenerators: Boolean);
-
var
F: TFileStream = nil;
WhatStr: String;
begin
- {Write outputfile and then redirect command parser to it.}
-
+ // Write outputfile and then redirect command parser to it.
try
if FileExists(Fname) then
- DoSimpleMsg(DSS, 'File "' + Fname + '" is about to be overwritten. Rename it now before continuing if you wish to keep it.', 721);//TODO: this is useless without forms
- F := TFileStream.Create(Fname, fmCreate);
+ begin
+ // Since we don't have a warning mechanism, it's better to abort
+ // and let the user remove the existing file first
+ DoSimpleMsg(DSS, 'File "%s" was about to be overwritten. Rename/remove the existing file and try again.', [Fname], 721);
+ Exit;
+ end;
+ F := TBufferedFileStream.Create(Fname, fmCreate);
except
On E: Exception do
begin
- DoSimpleMsg(DSS, 'Error opening "' + Fname + '" for writing. Aborting.', 722);
+ DoSimpleMsg(DSS, 'Error opening "%s" for writing. Aborting.', [Fname], 722);
Exit;
end;
end;
@@ -2808,10 +2084,10 @@ procedure MakeDistributedGenerators(DSS: TDSSContext; kW, PF: Double; How: Strin
FSWriteln(F, '! Created with Distribute Command:');
FSWriteln(f, Format('! Distribute kW=%-.6g PF=%-.6g How=%s Skip=%d file=%s what=%s', [kW, PF, How, Skip, Fname, WhatStr]));
FSWriteln(F);
- // FSWriteln(F, 'Set allowduplicates=yes');
+ // FSWriteln(F, 'Set allowduplicates=yes');
if Length(How) = 0 then
How := 'P';
- case Uppercase(How)[1] of
+ case AnsiUpperCase(How)[1] of
'U':
WriteUniformGenerators(DSS, F, kW, PF, DoGenerators);
'R':
@@ -2823,20 +2099,34 @@ procedure MakeDistributedGenerators(DSS: TDSSContext; kW, PF: Double; How: Strin
end;
DSS.GlobalResult := Fname;
finally
- // FSWriteln(F, 'Set allowduplicates=no');
+ // FSWriteln(F, 'Set allowduplicates=no');
FreeAndNil(F);
SetLastResultFile(DSS, Fname);
end;
-
end;
-function GetDSSArray_Real(n: Integer; dbls: pDoubleArray): String;
+function GetDSSArray_Real(n: Integer; dbls: pDoubleArray; scale: Double): String;
var
i: Integer;
begin
+ if dbls = NIL then
+ begin
+ Result := '';
+ Exit;
+ end;
Result := '[';
- for i := 1 to n do
- Result := Result + Format(' %-.6g', [dbls^[i]]);
+
+ if scale = 1 then
+ begin
+ for i := 1 to n do
+ Result := Result + Format(' %-.6g', [dbls^[i]])
+ end
+ else
+ begin
+ for i := 1 to n do
+ Result := Result + Format(' %-.6g', [dbls^[i] / scale]);
+ end;
+
Result := Result + ']';
end;
@@ -2845,6 +2135,11 @@ function GetDSSArray_Single(n: Integer; sngs: pSingleArray): String;
i: Integer;
tmp: Double;
begin
+ if sngs = NIL then
+ begin
+ Result := '';
+ Exit;
+ end;
Result := '[';
for i := 1 to n do
begin
@@ -2854,31 +2149,27 @@ function GetDSSArray_Single(n: Integer; sngs: pSingleArray): String;
Result := Result + ']';
end;
-
function GetDSSArray_Integer(n: Integer; ints: pIntegerArray): String;
-
var
i: Integer;
-
begin
+ if ints = NIL then
+ begin
+ Result := '';
+ Exit;
+ end;
Result := '[';
for i := 1 to n do
Result := Result + Format(' %-.d', [ints^[i]]);
Result := Result + ']';
end;
-function CmulReal_im(const a: Complex; const Mult: Double): Complex; // Multiply only imaginary part by a real
-
-begin
- Result := cmplx(a.re, a.im * mult);
-end;
-
procedure CmulArray(pc: pcomplexarray; Multiplier: Double; size: Integer); // Multiply a complex array times a double
var
i: Integer;
begin
for i := 1 to size do
- pc^[i] := CMulReal(pc^[i], Multiplier);
+ pc[i] := pc[i] * Multiplier;
end;
function GetMaxCktElementSize(DSS: TDSSContext): Integer;
@@ -2891,29 +2182,11 @@ function GetMaxCktElementSize(DSS: TDSSContext): Integer;
with DSS.ActiveCircuit do
for i := 1 to NumDevices do
Result := max(result, TDSSCktElement(CktElements.Get(i)).Yorder);
-
end;
-(*
-FUNCTION IsValidNumericField(const NumberField:TEdit):Boolean;
-
-Var
- Code :Integer;
- Value :Double;
-
-Begin
- Result := TRUE;
- Val(NumberField.Text, Value, Code);
- If Code>0 Then Begin
- Beep;
- NumberField.SetFocus;
- Result := FALSE;
- End;
-End;
-*)
function GetUniqueNodeNumber(DSS: TDSSContext; const sBusName: String; StartNode: Integer): Integer;
-{To help avoid collisions of neutral numbers, this function returns a node number that is not being used,
- Starting at the StartNode value}
+// To help avoid collisions of neutral numbers, this function returns a node number that is not being used,
+// Starting at the StartNode value
var
iBusidx: Integer;
begin
@@ -2949,7 +2222,7 @@ function IsPathBetween(FromLine, ToLine: TPDElement): Boolean;
end;
procedure TraceAndEdit(DSS: TDSSContext; FromLine, ToLine: TPDElement; NPhases: Integer; EditStr: String);
-{Trace back up a tree and execute an edit command string}
+// Trace back up a tree and execute an edit command string
var
pLine: TPDElement;
begin
@@ -2959,7 +2232,7 @@ procedure TraceAndEdit(DSS: TDSSContext; FromLine, ToLine: TPDElement; NPhases:
if (pLine.NPhases = NPhases) or (Nphases = 0) then
begin
DSS.Parser.CmdString := EditStr;
- pLine.Edit; // Uses Parser
+ pLine.Edit(DSS.Parser); // Uses Parser
end;
if pLine = ToLine then
Break;
@@ -2968,7 +2241,7 @@ procedure TraceAndEdit(DSS: TDSSContext; FromLine, ToLine: TPDElement; NPhases:
end;
procedure GoForwardAndRephase(DSS: TDSSContext; FromLine: TPDElement; const PhaseString, EditStr, ScriptFileName: String; TransStop: Boolean);
-{Trace forward down a tree and Generate a script file to change the phase}
+// Trace forward down a tree and Generate a script file to change the phase
var
pPDelem: TPDElement;
pShuntObject: TDSSCktElement;
@@ -2980,10 +2253,9 @@ procedure GoForwardAndRephase(DSS: TDSSContext; FromLine: TPDElement; const Phas
XfmrLevel: Integer;
begin
-
pMeter := FromLine.MeterObj as TEnergyMeterObj;
- {Search for starting line in branchlist}
+ // Search for starting line in branchlist
pPDelem := pMeter.BranchList.first;
while pPDelem <> NIL do
begin
@@ -2994,10 +2266,10 @@ procedure GoForwardAndRephase(DSS: TDSSContext; FromLine: TPDElement; const Phas
pPDelem := pMeter.BranchList.GoForward;
end;
- {Error check}
+ // Error check
if pPDelem = NIL then
begin
- DoSimpleMsg(DSS, FromLine.ParentClass.Name + '.' + FromLine.Name + ' Not found in Meter Zone.', 723);
+ DoSimpleMsg(DSS, '"%s" not found in Meter Zone.', [FromLine.FullName], 723);
Exit;
end;
@@ -3005,72 +2277,64 @@ procedure GoForwardAndRephase(DSS: TDSSContext; FromLine: TPDElement; const Phas
FileName := DSS.OutputDirectory + DSS.CircuitName_ + ScriptFileName;
DSS.GlobalResult := FileName;
- Fout := TFileStream.Create(filename, fmCreate);
+ Fout := TBufferedFileStream.Create(filename, fmCreate);
pMeter.BranchList.StartHere;
pPDelem := pMeter.BranchList.GoForward;
while pPDelem <> NIL do
begin
- S := 'edit ' + pPDelem.ParentClass.Name + '.' + pPDElem.Name;
-
-{----------------LINES---------------------------------------------------}
-
+ S := 'edit "' + pPDelem.FullName + '"';
+ // ----------------LINES---------------------------------------------------
if IsLineElement(pPDelem) then
begin
-
for i := 1 to pPDElem.NTerms do
begin
S := S + Format(' Bus%d=%s%s', [i, StripExtension(pPDelem.GetBus(i)), PhaseString]);
- // Parser.CmdString := Format('Bus$d=%s%s',[i, StripExtension(pPDelem.GetBus(i)), PhaseString]);
- // pPDelem.Edit;
+ // Parser.CmdString := Format('Bus$d=%s%s',[i, StripExtension(pPDelem.GetBus(i)), PhaseString]);
+ // pPDelem.Edit;
end;
- {When we're done with that, we'll send the Edit string}
+ // When we're done with that, we'll send the Edit string
if Length(EditStr) > 0 then
begin
S := S + ' ' + EditStr;
- // Parser.CmdString := EditStr;
- // pPDelem.Edit; // Uses Parser
+ // Parser.CmdString := EditStr;
+ // pPDelem.Edit; // Uses Parser
end;
FSWriteln(Fout, S);
- {Now get all shunt objects connected to this branch}
+ // Now get all shunt objects connected to this branch
pShuntObject := pMeter.BranchList.FirstObject;
while pShuntObject <> NIL do
begin
- {1st Terminal Only}
+ // 1st Terminal Only
i := 1;
- S := 'edit ' + pShuntObject.ParentClass.Name + '.' + pShuntObject.Name;
+ S := 'edit "' + pShuntObject.FullName + '"';
S := S + Format(' Bus%d=%s%s', [i, StripExtension(pShuntObject.GetBus(i)), PhaseString]);
if Length(EditStr) > 0 then
S := S + ' ' + EditStr;
FSWriteln(Fout, S);
- // Parser.CmdString := Format('Bus$d=%s%s',[i, StripExtension(pShuntObject.GetBus(1)), PhaseString]);
- // pShuntObject.Edit;
+ // Parser.CmdString := Format('Bus$d=%s%s',[i, StripExtension(pShuntObject.GetBus(1)), PhaseString]);
+ // pShuntObject.Edit;
pShuntObject := pMeter.BranchList.NextObject
end;
pPDelem := pMeter.BranchList.GoForward;
- end {IsLine}
-
-{----------------TRANSFORMERS---------------------------------------------------}
-
+ end // IsLine
else
+ // ----------------TRANSFORMERS---------------------------------------------------
if IsTransformerElement(pPDELEM) then
begin
-
- {
- We'll stop at transformers and change only the primary winding.
- Then we'll cycle forward until the lexical level is less or we're done
- }
+ // We'll stop at transformers and change only the primary winding.
+ // Then we'll cycle forward until the lexical level is less or we're done
XFmrLevel := pMeter.BranchList.Level;
S := S + Format(' wdg=1 Bus=%s%s %s', [StripExtension(pPDelem.GetBus(1)), PhaseString, EditStr]);
if not TransStop then
S := S + Format(' wdg=2 Bus=%s%s %s', [StripExtension(pPDelem.GetBus(2)), PhaseString, EditStr]);
FSWriteln(Fout, S);
- {Be default Go forward in the tree until we bounce back up to a line section above the transformer}
+ // By default Go forward in the tree until we bounce back up to a line section above the transformer
if TransStop then
begin
repeat
@@ -3078,19 +2342,13 @@ procedure GoForwardAndRephase(DSS: TDSSContext; FromLine: TPDElement; const Phas
until (pPDelem = NIL) or (pMeter.BranchList.Level <= XfmrLevel);
end
else
- pPDelem := pMeter.BranchList.GoForward; {Then we get lines and loads beyond transformer}
+ pPDelem := pMeter.BranchList.GoForward; // Then we get lines and loads beyond transformer
end;
-
end;
-
finally
-
FreeAndNil(Fout);
FireOffEditor(DSS, FileName);
-
end;
-
-
end;
function MaxdblArrayValue(npts: Integer; dbls: pDoubleArray): Double;
@@ -3147,102 +2405,7 @@ function iMaxAbssngArrayValue(npts: Integer; sngs: pSingleArray): Integer;
end;
end;
-function InterpretLoadShapeClass(const s: String): Integer;
-var
- ss: String;
-begin
- ss := lowercase(s);
- Result := USENONE;
-
- case ss[1] of
- 'd':
- case ss[2] of
- 'a':
- Result := USEDAILY;
- 'u':
- Result := USEDUTY;
- end;
- 'y':
- Result := USEYEARLY;
- 'n':
- Result := USENONE;
- end;
-
-end;
-
-function InterpretEarthModel(const s: String): Integer;
-
-var
- ss: String;
-begin
- ss := lowercase(s);
- Result := SIMPLECARSON;
- case ss[1] of
- 'c':
- Result := SIMPLECARSON;
- 'f':
- Result := FULLCARSON;
- 'd':
- Result := DERI;
- end;
-
-end;
-
-function GetActiveLoadShapeClass(DSS: TDSSContext): String;
-
-begin
- case DSS.ActiveCircuit.ActiveLoadShapeClass of
- USEDAILY:
- Result := 'Daily';
- USEYEARLY:
- Result := 'Yearly';
- USEDUTY:
- Result := 'Duty';
- USENONE:
- Result := 'None';
- else
- Result := 'ERROR';
- end;
-
-end;
-
-function GetEarthModel(n: Integer): String;
-
-begin
- case n of
- SIMPLECARSON:
- Result := 'Carson';
- FULLCARSON:
- Result := 'FullCarson';
- DERI:
- Result := 'Deri';
- else
- Result := 'ERROR';
- end;
-
-end;
-
-
function InterpretColorName(DSS: TDSSContext; const s: String): Integer;
-const
- clAqua: Integer = $FFFF00;
- clBlack: Integer = $000000;
- clBlue: Integer = $FF0000;
- clDkGray: Integer = $808080;
- clFuchsia: Integer = $FF00FF;
- clGray: Integer = $808080;
- clGreen: Integer = $008000;
- clLime: Integer = $00FF00;
- clLtGray: Integer = $C0C0C0;
- clMaroon: Integer = $000080;
- clNavy: Integer = $800000;
- clOlive: Integer = $008080;
- clPurple: Integer = $800080;
- clRed: Integer = $0000FF;
- clSilver: Integer = $C0C0C0;
- clTeal: Integer = $808000;
- clWhite: Integer = $FFFFFF;
- clYellow: Integer = $00FFFF;
begin
Result := clBlue; // default color
try
@@ -3303,32 +2466,28 @@ function InterpretColorName(DSS: TDSSContext; const s: String): Integer;
Result := StrToInt(S);
except
On E: Exception do
- DoSimpleMsg(DSS, 'Invalid Color Specification: "' + S + '".', 724);
+ DoSimpleMsg(DSS, 'Invalid Color Specification: "%s".', [S], 724);
end;
end;
function MakeNewCktElemName(DSS: TDSSContext; const oldname: String): String;
begin
- SetObject(DSS, OldName); // set opject active
+ SetObject(DSS, OldName); // set object active
with DSS.ActiveDSSObject do
Result := Format('%s.%s%d', [ParentClass.Name, copy(ParentClass.Name, 1, 4), ClassIndex]);
end;
function ConstructElemName(DSS: TDSSContext; const Param: String): String;
-{Construct an element name, sustituting @var values if any}
-
+// Construct an element name, sustituting @var values if any
var
FClassName, FObjName: String;
-
begin
-
- ParseObjectClassandName(DSS, lowercase(param), FClassName, FObjName); // insert @var test
+ ParseObjectClassandName(DSS, AnsiLowerCase(param), FClassName, FObjName); // insert @var test
result := Format('%s.%s', [FClassName, FObjName]);
-
end;
procedure Obfuscate(DSS: TDSSContext);
-{Rename Buses and element names to generic names to remove identifiable names}
+// Rename Buses and element names to generic names to remove identifiable names
var
i, bref: Integer;
dotpos: Integer;
@@ -3344,22 +2503,18 @@ procedure Obfuscate(DSS: TDSSContext);
ControlUpDateStrings: TstringList;
ControlUpDatePtrs: Tlist;
- {-------------------------------------------------------------}
procedure RenameCktElem(pelem: TDSSCktElement); // local proc
begin
with pelem do
begin
Name := Format('%s%d', [copy(ParentClass.Name, 1, 4), ClassIndex]);
DSS.ActiveCircuit.DeviceList.Add(Name); // Make a new device list corresponding to the CktElements List
- pelem.Checked := TRUE;
+ Include(pelem.Flags, Flg.Checked);
end;
end;
- {-------------------------------------------------------------}
-
begin
-
-{Make sure buslist exists}
+ // Make sure buslist exists
if DSS.ActiveCircuit = NIL then
Exit;
@@ -3369,14 +2524,14 @@ procedure Obfuscate(DSS: TDSSContext);
begin
TempBusList := TBusHashListType.Create(BusList.Count);
- {Rename Buses}
+ // Rename Buses
for i := 1 to BusList.Count do
TempBusList.Add(Format('B_%d', [i]));
BusList.Free;
BusList := TempBusList; // Reassign
- {Rename the bus names in each circuit element before renaming the elements}
+ // Rename the bus names in each circuit element before renaming the elements
pCktElem := Cktelements.First;
while pCktElem <> NIL do
begin
@@ -3403,15 +2558,15 @@ procedure Obfuscate(DSS: TDSSContext);
end;
end;
DSS.Parser.CmdString := S;
- pCktElem.Edit;
+ pCktElem.Edit(DSS.Parser);
end;
pCktElem := CktElements.Next;
end;
- {Rename the circuit elements to generic values}
- {Have to catch the control elements and edit some of their parameters}
+ // Rename the circuit elements to generic values
+ // Have to catch the control elements and edit some of their parameters
- {first, make scripts to change the monitored element names in the controls to what they will be}
+ // first, make scripts to change the monitored element names in the controls to what they will be
ControlUpDateStrings := TStringList.Create;
ControlUpDatePtrs := TList.Create;
@@ -3467,7 +2622,7 @@ procedure Obfuscate(DSS: TDSSContext);
pCktElem := Cktelements.First;
while pCktElem <> NIL do
begin
- pCktElem.Checked := FALSE; // Initialize to not checked
+ Exclude(pCktElem.Flags, Flg.Checked); // Initialize to not checked
pCktElem := Cktelements.Next;
end;
@@ -3478,13 +2633,13 @@ procedure Obfuscate(DSS: TDSSContext);
pCktElem := Cktelements.First;
while pCktElem <> NIL do
begin
- if not pCktElem.Checked then
+ if not (Flg.Checked in pCktElem.Flags) then
begin
ElemClass := (pCktElem.DSSObjType and CLASSMASK);
RenameCktElem(pCktElem);
case ElemClass of
XFMR_ELEMENT:
- if pCktElem.HasControl then
+ if Flg.HasControl in pCktElem.Flags then
begin
pCtrlElem := pCktElem.ControlElementList.First;
while assigned(pCtrlElem) do
@@ -3492,40 +2647,34 @@ procedure Obfuscate(DSS: TDSSContext);
if (pCtrlElem.DSSObjType and CLASSMASK) = REG_CONTROL then
begin
DSS.Parser.CmdString := Format('Transformer=%s', [pCktElem.Name]);
- pCtrlElem.Edit;
+ pCtrlElem.Edit(DSS.Parser);
end;
pCtrlElem := pCktElem.ControlElementList.Next;
end;
end;
else
- {nada}
+ // nada
end;
end;
pCktElem := Cktelements.Next;
end;
-
- {Run the control update scripts now that everything is renamed}
-
+ // Run the control update scripts now that everything is renamed
for i := 0 to ControlUpDatePtrs.Count - 1 do
begin
pCktElem := ControlUpDatePtrs.Items[i];
DSS.Parser.CmdString := ControlUpDateStrings.Strings[i];
- pCktElem.Edit;
+ pCktElem.Edit(DSS.Parser);
end;
ControlUpDateStrings.Free;
ControlUpDatePtrs.Free;
-
- end; {With}
-
+ end; // With
end;
function QuadSolver(const a, b, c: Double): Double; // returns largest of two answers
-
var
Ans1, Ans2, MidTerm, a2: Double;
-
begin
Result := 0.0; // default return
if a = 0.0 then
@@ -3545,10 +2694,8 @@ function QuadSolver(const a, b, c: Double): Double; // returns largest of two an
else
Result := Ans2;
end;
-
end;
-{-------------------------------------------------------------------------------}
function GetOCPDeviceType(pElem: TDSSCktElement): Integer;
var
i: Integer;
@@ -3590,7 +2737,6 @@ function GetOCPDeviceTypeString(icode: Integer): String;
function GetNodeString(const Busname: String): String;
var
dotpos: Integer;
-
begin
dotpos := pos('.', BusName);
if dotpos = 0 then
@@ -3599,9 +2745,14 @@ function GetNodeString(const Busname: String): String;
Result := Copy(BusName, dotpos, length(BusName)); // preserve node designations if any
end;
-function AdjustInputFilePath(DSS: TDSSContext; const param: String): String;
+function AdjustInputFilePath(DSS: TDSSContext; param: String): String;
begin
- if (not DSS_CAPI_ALLOW_CHANGE_DIR) and FileExists(DSS.CurrentDSSDir + Param) {and (not FileExists(Param))} then
+{$IFDEF WINDOWS}
+ param := ReplaceStr(param, '/', PathDelim);
+{$ELSE}
+ param := ReplaceStr(param, '\', PathDelim);
+{$ENDIF}
+ if (not DSS_CAPI_ALLOW_CHANGE_DIR) and FileExists(DSS.CurrentDSSDir + Param) then // and (not FileExists(Param))
Result := DSS.CurrentDSSDir + Param
else
Result := Param;
@@ -3657,7 +2808,7 @@ procedure FSWriteLn(F: TFileStream; Ss: Array of String); inline; overload;
end;
end;
-procedure FSReadln(F: TFileStream; out S: String); // TODO: optimize?
+procedure FSReadln(F: TStream; out S: String); // TODO: optimize?
var
ch: AnsiChar;
begin
@@ -3697,4 +2848,268 @@ function SliceProps(props: pStringArray; count: Integer): ArrayOfString; // The
Result[i - 1] := props[i];
end;
+procedure DoSngFile(DSS: TDSSContext; var pA, pB: PDoubleArray; var NumPoints: Integer; OnlyLoadB: Boolean; const FileName: String; const ClassName: String; RoundA: Boolean);
+var
+ F: TStream = nil;
+ sA,
+ sB: Single;
+ i: Integer;
+begin
+ try
+ try
+ F := DSS.GetROFileStream(FileName);
+ except
+ DoSimpleMsg(DSS, 'Error opening file: "%s"', [FileName], 615);
+ FreeAndNil(F);
+ Exit;
+ end;
+
+ ReAllocmem(pB, Sizeof(Double) * NumPoints);
+ i := 0;
+ if not OnlyLoadB then
+ begin
+ ReAllocmem(pA, Sizeof(Double) * NumPoints);
+ while ((F.Position + 1) < F.Size) and (i < NumPoints) do
+ begin
+ Inc(i);
+
+ if F.Read(sA, SizeOf(sA)) <> SizeOf(sA) then
+ Break;
+
+ pA[i] := sA;
+
+ if F.Read(sB, SizeOf(sB)) <> SizeOf(sB) then
+ Break;
+
+ pB[i] := sB;
+ end;
+ end
+ else
+ begin
+ while ((F.Position + 1) < F.Size) and (i < NumPoints) do
+ begin
+ Inc(i);
+
+ if F.Read(sB, SizeOf(sB)) <> SizeOf(sB) then
+ Break;
+
+ pB[i] := sB;
+ end;
+ end;
+
+ FreeAndNil(F);
+ if i <> NumPoints then
+ NumPoints := i;
+
+ if RoundA then
+ for i := 1 to NumPoints do
+ pA[i] := Round(pA[i]);
+
+ except
+ DoSimpleMsg(DSS, 'Error Processing binary (single) %s File: "%s"', [ClassName, FileName], 616);
+ FreeAndNil(F);
+ Exit;
+ end;
+end;
+
+procedure DoDblFile(DSS: TDSSContext; var pA, pB: PDoubleArray; var NumPoints: Integer; OnlyLoadB: Boolean; const FileName: String; const ClassName: String; RoundA: Boolean);
+var
+ F: TStream = nil;
+ i: Integer;
+begin
+ try
+ try
+ F := DSS.GetROFileStream(FileName);
+ except
+ DoSimpleMsg(DSS, 'Error opening file: "%s"', [FileName], 615);
+ FreeAndNil(F);
+ Exit;
+ end;
+
+ ReAllocmem(pB, Sizeof(Double) * NumPoints);
+ i := 0;
+ if not OnlyLoadB then
+ begin
+ ReAllocmem(pA, Sizeof(Double) * NumPoints);
+ while ((F.Position + 1) < F.Size) and (i < NumPoints) do
+ begin
+ Inc(i);
+
+ if F.Read(pA[i], SizeOf(Double)) <> SizeOf(Double) then
+ Break;
+
+ if F.Read(pB[i], SizeOf(Double)) <> SizeOf(Double) then
+ Break;
+ end;
+ end
+ else
+ begin
+ while ((F.Position + 1) < F.Size) and (i < NumPoints) do
+ begin
+ Inc(i);
+
+ if F.Read(pB[i], SizeOf(Double)) <> SizeOf(Double) then
+ Break;
+ end;
+ end;
+
+ FreeAndNil(F);
+ if i <> NumPoints then
+ NumPoints := i;
+
+ if RoundA then
+ for i := 1 to NumPoints do
+ pA[i] := Round(pA[i]);
+
+ except
+ DoSimpleMsg(DSS, 'Error Processing binary (double) %s File: "%s"', [ClassName, FileName], 616);
+ FreeAndNil(F);
+ Exit;
+ end;
+end;
+
+procedure DoCSVFile(DSS: TDSSContext; var pA, pB: PDoubleArray; var NumPoints: Integer; OnlyLoadB: Boolean; const FileName: String; const ClassName: String; RoundA: Boolean);
+var
+ F: TStream = nil;
+ i: Integer;
+ s: String;
+begin
+ try
+ F := DSS.GetROFileStream(FileName);
+ except
+ DoSimpleMsg(DSS, 'Error opening file: "%s"', [FileName], 58613);
+ FreeAndNil(F);
+ Exit;
+ end;
+ try
+ ReAllocmem(pB, Sizeof(Double) * NumPoints);
+ i := 0;
+ if not OnlyLoadB then
+ begin
+ ReAllocmem(pA, Sizeof(Double) * NumPoints);
+ while ((F.Position + 1) < F.Size) and (i < NumPoints) do
+ begin
+ Inc(i);
+ FSReadln(F, s); // read entire line and parse with AuxParser
+ // AuxParser allows commas or white space
+ with DSS.AuxParser do
+ begin
+ CmdString := s;
+ NextParam;
+ pA[i] := DblValue;
+ NextParam;
+ pB[i] := DblValue;
+ end;
+ end;
+ end
+ else
+ begin
+ while ((F.Position + 1) < F.Size) and (i < NumPoints) do
+ begin
+ Inc(i);
+ FSReadln(F, s); // read entire line and parse with AuxParser
+ // AuxParser allows commas or white space
+ with DSS.AuxParser do
+ begin
+ CmdString := s;
+ NextParam;
+ pB[i] := DblValue;
+ end;
+ end;
+ end;
+
+ FreeAndNil(F);
+ NumPoints := i;
+
+ except
+ On E: Exception do
+ begin
+ DoSimpleMsg(DSS, 'Error Processing CSV File: "%s". %s', [FileName, E.Message], 58614);
+ FreeAndNil(F);
+ Exit;
+ end;
+ end;
+end;
+
+
+// Routine created to empty a recently created folder
+{$IFDEF MSWINDOWS}
+procedure DelFilesFromDir(Directory, FileMask: string; DelSubDirs: Boolean);
+var
+ SourceLst: string;
+ FOS: TSHFileOpStruct;
+begin
+ FillChar(FOS, SizeOf(FOS), 0);
+ FOS.wFunc := FO_DELETE;
+ SourceLst := Directory + PathDelim + FileMask + #0;
+ FOS.pFrom := PChar(SourceLst);
+ if not DelSubDirs then
+ FOS.fFlags := FOS.fFlags OR FOF_FILESONLY;
+ // Remove the next line if you want a confirmation dialog box
+ FOS.fFlags := FOS.fFlags OR FOF_NOCONFIRMATION;
+ // Add the next line for a "silent operation" (no progress box)
+ FOS.fFlags := FOS.fFlags OR FOF_SILENT;
+ SHFileOperation(FOS);
+end;
+{$ENDIF}
+{$IFDEF UNIX}
+procedure DeltreeDir(Directory: String);
+var
+ Info: TSearchRec;
+begin
+ if FindFirst(Directory + PathDelim + '*', faAnyFile and faDirectory, Info) = 0 then
+ begin
+ repeat
+ with Info do
+ begin
+ if (name = '.') or (name = '..') then
+ continue;
+ if (Attr and faDirectory) = faDirectory then
+ begin
+ DeltreeDir(Directory + PathDelim + Name)
+ end
+ else
+ begin
+ DeleteFile(Directory + PathDelim + Name);
+ end;
+ end;
+ until FindNext(info) <> 0;
+ end;
+ rmdir(Directory);
+end;
+
+procedure DelFilesFromDir(Directory, FileMask: String; DelSubDirs: Boolean);
+var
+ Info: TSearchRec;
+ flags: Longint;
+begin
+ if DelSubDirs then
+ flags := faAnyFile and faDirectory
+ else
+ flags := faAnyFile;
+
+ if FindFirst(Directory + PathDelim + FileMask, flags, Info) = 0 then
+ begin
+ repeat
+ with Info do
+ begin
+ if (name = '.') or (name = '..') then
+ continue;
+ if (Attr and faDirectory) = faDirectory then
+ begin
+ try
+ DeltreeDir(Directory + PathDelim + Name)
+ except
+ DSSMessageDlg('Could not remove directory ' + Directory + PathDelim + Name, True);
+ end;
+ end
+ else
+ begin
+ DeleteFile(Directory + PathDelim + Name);
+ end;
+ end;
+ until FindNext(info) <> 0;
+ end;
+end;{$ENDIF}
+
end.
diff --git a/src/Common/Ymatrix.pas b/src/Common/Ymatrix.pas
index 1ee0c89a5..095e743e6 100644
--- a/src/Common/Ymatrix.pas
+++ b/src/Common/Ymatrix.pas
@@ -7,22 +7,17 @@
----------------------------------------------------------
}
-{
- Unit to manage System Y matrix
-
- 6-11-00 Created from Solution.Pas
-}
interface
uses
- uComplex,
+ UComplex, DSSUcomplex,
ucMatrix,
SysUtils,
DSSClass;
-{Options for building Y matrix}
+// Options for building Y matrix
const
SERIESONLY = 1;
WHOLEMATRIX = 2;
@@ -68,7 +63,7 @@ procedure ReCalcAllYPrims(Ckt: TDSSCircuit);
with Ckt do
begin
if LogEvents then
- LogThisEvent(Ckt.DSS, 'Recalc All Yprims');
+ LogThisEvent(Ckt.DSS, _('Recalc All Yprims'));
pElem := CktElements.First;
while pElem <> NIL do
begin
@@ -76,7 +71,6 @@ procedure ReCalcAllYPrims(Ckt: TDSSCircuit);
pElem := CktElements.Next;
end;
end;
-
end;
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -89,7 +83,7 @@ procedure ReCalcInvalidYPrims(Ckt: TDSSCircuit);
with Ckt do
begin
if LogEvents then
- LogThisEvent(Ckt.DSS, 'Recalc Invalid Yprims');
+ LogThisEvent(Ckt.DSS, _('Recalc Invalid Yprims'));
{$IFDEF DSS_CAPI_INCREMENTAL_Y}
pElem := IncrCktElements.First;
@@ -122,11 +116,10 @@ procedure ResetSparseMatrix(var hY: NativeUint; size: Integer);
begin
-
if hY <> 0 then
begin
if DeleteSparseSet(hY) < 1 {Get rid of existing one beFore making a new one} then
- raise EEsolv32Problem.Create('Error Deleting System Y Matrix in ResetSparseMatrix. Problem with Sparse matrix solver.');
+ raise EEsolv32Problem.Create(_('Error Deleting System Y Matrix in ResetSparseMatrix. Problem with Sparse matrix solver.'));
hY := 0;
end;
@@ -135,7 +128,7 @@ procedure ResetSparseMatrix(var hY: NativeUint; size: Integer);
hY := NewSparseSet(Size);
if hY < 1 then
begin
- raise EEsolv32Problem.Create('Error Creating System Y Matrix. Problem WITH Sparse matrix solver.');
+ raise EEsolv32Problem.Create(_('Error Creating System Y Matrix. Problem WITH Sparse matrix solver.'));
end;
end;
@@ -334,7 +327,6 @@ procedure BuildYMatrix(DSS: TDSSContext; BuildOption: Integer; AllocateVI: Boole
CmatArray := NIL;
with DSS.ActiveCircuit, Solution do
begin
-
if PreserveNodeVoltages then
UpdateVBus; // Update voltage values stored with Bus object
@@ -391,7 +383,7 @@ procedure BuildYMatrix(DSS: TDSSContext; BuildOption: Integer; AllocateVI: Boole
if DSS.SolutionAbort then
begin
- DoSimpleMsg('Y matrix build aborted due to error in primitive Y calculations.', 11001);
+ DoSimpleMsg(DSS, _('Y matrix build aborted due to error in primitive Y calculations.'), 11001);
Exit; // Some problem occured building Yprims
end;
@@ -403,13 +395,13 @@ procedure BuildYMatrix(DSS: TDSSContext; BuildOption: Integer; AllocateVI: Boole
WHOLEMATRIX:
{$IFDEF DSS_CAPI_INCREMENTAL_Y}
if Incremental then
- LogThisEvent(DSS, 'Building Whole Y Matrix -- using incremental method')
+ LogThisEvent(DSS, _('Building Whole Y Matrix -- using incremental method'))
else
{$ENDIF}
- LogThisEvent(DSS, 'Building Whole Y Matrix');
+ LogThisEvent(DSS, _('Building Whole Y Matrix'));
SERIESONLY:
- LogThisEvent(DSS, 'Building Series Y Matrix');
+ LogThisEvent(DSS, _('Building Series Y Matrix'));
end;
// Add in Yprims for all devices
@@ -434,7 +426,7 @@ procedure BuildYMatrix(DSS: TDSSContext; BuildOption: Integer; AllocateVI: Boole
// new function adding primitive Y matrix to KLU system Y matrix
if CMatArray <> NIL then
if AddPrimitiveMatrix(hY, Yorder, PLongWord(@NodeRef[1]), @CMatArray[1]) < 1 then
- raise EEsolv32Problem.Create('Node index out of range adding to System Y Matrix')
+ raise EEsolv32Problem.Create(_('Node index out of range adding to System Y Matrix'))
end; // If Enabled
pElem := CktElements.Next;
end;
@@ -451,7 +443,7 @@ procedure BuildYMatrix(DSS: TDSSContext; BuildOption: Integer; AllocateVI: Boole
if AllocateVI then
begin
if LogEvents then
- LogThisEvent(DSS, 'ReAllocating Solution Arrays');
+ LogThisEvent(DSS, _('Reallocating Solution Arrays'));
ReAllocMem(NodeV, SizeOf(NodeV^[1]) * (NumNodes + 1)); // Allocate System Voltage array - allow for zero element
NodeV^[0] := CZERO;
ReAllocMem(Currents, SizeOf(Currents^[1]) * (NumNodes + 1)); // Allocate System current array
@@ -466,8 +458,8 @@ procedure BuildYMatrix(DSS: TDSSContext; BuildOption: Integer; AllocateVI: Boole
ErrorSaved := AllocMem(Sizeof(ErrorSaved^[1]) * NumNodes); // zero fill
NodeVBase := AllocMem(Sizeof(NodeVBase^[1]) * NumNodes); // zero fill
InitializeNodeVbase(DSS);
-{$IFDEF DSS_CAPI_PM}
- {A-Diakoptics vectors memory allocation}
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ // A-Diakoptics vectors memory allocation
ReAllocMem(Node_dV, SizeOf(Node_dV^[1]) * (NumNodes + 1)); // Allocate the partial solution voltage
ReAllocMem(Ic_Local, SizeOf(Ic_Local^[1]) * (NumNodes + 1)); // Allocate the Complementary currents
{$ENDIF}
@@ -483,9 +475,8 @@ procedure BuildYMatrix(DSS: TDSSContext; BuildOption: Integer; AllocateVI: Boole
SeriesYInvalid := FALSE; // SystemYChange unchanged
end;
- // Deleted RCD only done now on mode change
- // SolutionInitialized := False; //Require initialization of voltages if Y changed
-
+ // Deleted RCD only done now on mode change
+ // SolutionInitialized := False; //Require initialization of voltages if Y changed
if PreserveNodeVoltages then
RestoreNodeVfromVbus;
@@ -503,7 +494,6 @@ function CheckYMatrixforZeroes(DSS: TDSSContext): String;
nIslands, iCount, iFirst, p: Longword;
Cliques: array of Longword;
begin
-
Result := '';
with DSS.ActiveCircuit do
begin
@@ -514,7 +504,7 @@ function CheckYMatrixforZeroes(DSS: TDSSContext): String;
if Cabs(C) = 0.0 then
with MapNodeToBus^[i] do
begin
- Result := Result + Format('%sZero diagonal for bus %s, node %d', [CRLF, BusList.NameOfIndex(Busref), NodeNum]);
+ Result := Result + Format(_('%sZero diagonal for bus %s, node %d'), [CRLF, BusList.NameOfIndex(Busref), NodeNum]);
end;
end;
@@ -523,14 +513,14 @@ function CheckYMatrixforZeroes(DSS: TDSSContext): String;
if sCol > 0 then
with MapNodeToBus^[sCol] do
begin
- Result := Result + Format('%sMatrix singularity at bus %s, node %d', [CRLF, BusList.NameOfIndex(Busref), sCol]);
+ Result := Result + Format(_('%sMatrix singularity at bus %s, node %d'), [CRLF, BusList.NameOfIndex(Busref), sCol]);
end;
SetLength(Cliques, NumNodes);
nIslands := FindIslands(hY, NumNodes, @Cliques[0]);
if nIslands > 1 then
begin
- Result := Result + Format('%sFound %d electrical islands:', [CRLF, nIslands]);
+ Result := Result + Format(_('%sFound %d electrical islands:'), [CRLF, nIslands]);
for i := 1 to nIslands do
begin
iCount := 0;
@@ -546,12 +536,11 @@ function CheckYMatrixforZeroes(DSS: TDSSContext): String;
end;
with MapNodeToBus^[iFirst] do
begin
- Result := Result + Format('%s #%d has %d nodes, including bus %s (node %d)', [CRLF, i, iCount, BusList.NameOfIndex(Busref), iFirst]);
+ Result := Result + CRLF + Format(_(' #%d has %d nodes, including bus %s (node %d)'), [i, iCount, BusList.NameOfIndex(Busref), iFirst]);
end;
end;
end;
end;
-
end;
diff --git a/src/Parallel_Lib/cpucount.pas b/src/Common/cpucount.pas
similarity index 99%
rename from src/Parallel_Lib/cpucount.pas
rename to src/Common/cpucount.pas
index 7963ed4e8..b5af7cd20 100644
--- a/src/Parallel_Lib/cpucount.pas
+++ b/src/Common/cpucount.pas
@@ -4,7 +4,7 @@
interface
//returns number of cores: a computer with two hyperthreaded cores will report 4
function GetLogicalCpuCount: Integer;
-
+
implementation
{$IF defined(windows)}
@@ -51,7 +51,7 @@ function GetLogicalCpuCount: integer;
const
param: string = 'hw.logicalcpu';
var
- len: cint;
+ len: size_t;
t: size_t;
begin
len := sizeof(t);
diff --git a/src/ControlProxy.pas b/src/ControlProxy.pas
index 724a94eb8..ea5dcdc32 100644
--- a/src/ControlProxy.pas
+++ b/src/ControlProxy.pas
@@ -6,13 +6,12 @@ interface
uses
Classes,
- DSSGlobals,
ControlQueue,
ControlElem,
DSSClass,
sysutils,
Utilities,
- Ucomplex;
+ UComplex, DSSUcomplex;
type
TControlProxyObj = class(TControlElem)
@@ -26,13 +25,13 @@ TControlProxyObj = class(TControlElem)
procedure DoPendingAction(const Code, ProxyHdl: Integer); OVERRIDE; // Do the action that is pending from last sample
procedure Reset; OVERRIDE; // Reset to initial defined state
- procedure GetCurrents(Curr: pComplexArray); Override;
procedure RecalcElementData; Override;
end;
implementation
uses
+ DSSGlobals,
DSSClassDefs;
procedure TControlProxyObj.DoPendingAction(const Code, ProxyHdl: Integer);
@@ -48,17 +47,11 @@ procedure TControlProxyObj.DoPendingAction(const Code, ProxyHdl: Integer);
ActionList.Add(Action);
end;
-procedure TControlProxyObj.GetCurrents(Curr: pComplexArray);
-begin
- raise Exception.Create('This procedure should not be called');
-end;
-
procedure TControlProxyObj.RecalcElementData;
begin
raise Exception.Create('This procedure should not be called');
end;
-
procedure TControlProxyObj.ClearActionList;
begin
while PopAction do ; // spin until it is done
diff --git a/src/Controls/CapControl.pas b/src/Controls/CapControl.pas
index a571ecbd2..0eb51b0c0 100644
--- a/src/Controls/CapControl.pas
+++ b/src/Controls/CapControl.pas
@@ -7,27 +7,16 @@
----------------------------------------------------------
}
-{
- Change Log
- 2-14-00 Created
-
- 3-1-00 Added Voltage override
- 5/21/01 Fixed bug with number of phases
- 5/30/01 Eliminated extra event queue reports
-}
-
-{
- A CapControl is a control element that is connected to a terminal of another
- circuit element and controls a capacitor. The control is usually placed in the
- terminal of a line or transformer, although a voltage control device could be placed
- in the terminal of the capacitor it controls
-
- A CapControl is defined by a New command:
-
- New CapControl.Name=myname Element=devclass.name terminal=[ 1|2|...] Capacitor = name
-
- Capacitor to be controlled must already exist.
-}
+// A CapControl is a control element that is connected to a terminal of another
+// circuit element and controls a capacitor. The control is usually placed in the
+// terminal of a line or transformer, although a voltage control device could be placed
+// in the terminal of the capacitor it controls
+//
+// A CapControl is defined by a New command:
+//
+// New CapControl.Name=myname Element=devclass.name terminal=[ 1|2|...] Capacitor = name
+//
+// Capacitor to be controlled must already exist.
interface
@@ -40,50 +29,114 @@ interface
Bus,
DSSClass,
Arraydef,
- ucomplex,
+ UComplex, DSSUcomplex,
Capacitor,
utilities,
- CapControlVars,
CapUserControl;
type
+{$SCOPEDENUMS ON}
+ TCapControlProp = (
+ INVALID = 0,
+ element = 1,
+ terminal = 2,
+ capacitor = 3,
+ typ = 4,
+ PTratio = 5,
+ CTratio = 6,
+ ONsetting = 7,
+ OFFsetting = 8,
+ Delay = 9,
+ VoltOverride = 10,
+ Vmax = 11,
+ Vmin = 12,
+ DelayOFF = 13,
+ DeadTime = 14,
+ CTPhase = 15,
+ PTPhase = 16,
+ VBus = 17,
+ EventLog = 18,
+ UserModel = 19,
+ UserData = 20,
+ pctMinkvar = 21,
+ Reset = 22
+ );
+{$SCOPEDENUMS OFF}
+
+ ECapControlType = (
+ CURRENTCONTROL,
+ VOLTAGECONTROL,
+ KVARCONTROL,
+ TIMECONTROL,
+ PFCONTROL,
+ USERCONTROL
+ );
+
+ // Fixed record structure for Public CapControl variables
+ TCapControlVars = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}Packed{$ENDIF} Record
+ FCTPhase,
+ FPTPhase :Integer; // "ALL" is -1
+
+ ON_Value,
+ OFF_Value,
+ PFON_Value,
+ PFOFF_Value,
+ CTRatio,
+ PTRatio,
+ ONDelay,
+ OFFDelay,
+ DeadTime,
+ LastOpenTime: Double;
+
+ Voverride: LongBool;
+ VoverrideEvent: Boolean;
+ VoverrideBusSpecified: Boolean; // Added 8-11-11
+
+ VOverrideBusIndex :Integer;
+
+ Vmax: Double;
+ Vmin: Double;
+ FPendingChange: EControlAction;
+ ShouldSwitch: Boolean; // True: action is pending
+ Armed: Boolean; // Control is armed for switching unless reset
+ PresentState: EControlAction;
+ InitialState: EControlAction;
+
+ SampleP: Complex; // two 64-bit numbers, kW, kvar
+ SampleV: Double;
+ SampleCurr: Double;
+
+ NumCapSteps: Integer;
+ AvailableSteps: Integer; // available steps in controlled capacitor
+ LastStepInService: Integer; // Change this to force an update of cap states
+
+ VOverrideBusName: String;
+ CapacitorName: String;
+ ControlActionHandle: Integer;
+ CondOffset: Integer; // Offset for monitored terminal
+ end;
-
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TCapControl = class(TControlClass)
- PRIVATE
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const CapControlName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
-
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TCapControlObj = class(TControlElem)
-{$IFDEF DSS_CAPI}
PUBLIC
-{$ELSE}
- PROTECTED
-{$ENDIF}
procedure Set_Enabled(Value: Boolean); OVERRIDE;
PRIVATE
ControlType: ECapControlType;
-
ControlVars: TCapControlVars;
-
ControlledCapacitor: TCapacitorObj;
-
cBuffer: pComplexArray; // Complexarray buffer
IsUserModel: Boolean;
UserModel: TCapUserControl;
+ UserModelNameStr, UserModelEditStr: String;
FpctMinkvar: Double;
@@ -94,31 +147,23 @@ TCapControlObj = class(TControlElem)
procedure GetControlVoltage(var ControlVoltage: Double);
procedure GetControlCurrent(var ControlCurrent: Double);
procedure GetBusVoltages(pBus: TDSSBus; Buff: pComplexArray);
-
-
PUBLIC
-
constructor Create(ParClass: TDSSClass; const CapControlName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
procedure RecalcElementData; OVERRIDE;
- procedure CalcYPrim; OVERRIDE; // Always Zero for a CapControl
procedure Sample; OVERRIDE; // Sample control quantities and set action times in Control Queue
procedure DoPendingAction(const Code, ProxyHdl: Integer); OVERRIDE; // Do the action that is pending from last sample
procedure Reset; OVERRIDE; // Reset to initial defined state
-
- procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present value of terminal Curr
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
property This_Capacitor: TCapacitorObj READ Get_Capacitor; // Pointer to controlled Capacitor
property PendingChange: EControlAction READ Get_PendingChange WRITE Set_PendingChange;
- // for CIM export, which doesn't yet use the delays, CT, PT, and voltage override
+ // for CIM export, which doesn't yet use the delays, CT, PT, and voltage override
property CapControlType: ECapControlType READ ControlType WRITE ControlType;
property OnValue: Double READ ControlVars.ON_Value;
property OffValue: Double READ ControlVars.OFF_Value;
@@ -130,17 +175,14 @@ TCapControlObj = class(TControlElem)
property OffDelayVal: Double READ ControlVars.OffDelay;
property VminVal: Double READ ControlVars.Vmin;
property VmaxVal: Double READ ControlVars.Vmax;
- property UseVoltageOverride: Boolean READ ControlVars.Voverride;
+ property UseVoltageOverride: LongBool READ ControlVars.Voverride;
property DeadTimeVal: Double READ ControlVars.DeadTime;
property PTPhase: Integer READ ControlVars.FPTPhase;
end;
-
-{--------------------------------------------------------------------------}
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Circuit,
@@ -152,409 +194,258 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TCapControlObj;
+ TProp = TCapControlProp;
const
+ NumPropsThisClass = Ord(High(TProp));
AVGPHASES = -1;
MAXPHASE = -2;
MINPHASE = -3;
- NumPropsThisClass = 22;
-
+var
+ PropInfo: Pointer = NIL;
+ TypeEnum: TDSSEnum;
-{--------------------------------------------------------------------------}
-constructor TCapControl.Create(dssContext: TDSSContext); // Creates superstructure for all CapControl objects
+constructor TCapControl.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
-
- Class_name := 'CapControl';
- DSSClassType := DSSClassType + CAP_CONTROL;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ TypeEnum := TDSSEnum.Create('CapControl: Type', True, 1, 1,
+ ['Current', 'Voltage', 'kvar', 'Time', 'PowerFactor'{'UserControl'}],
+ [ord(CURRENTCONTROL), ord(VOLTAGECONTROL), ord(KVARCONTROL), ord(TIMECONTROL), ord(PFCONTROL) {, ord(USERCONTROL)}]);
+ end;
+ inherited Create(dssContext, CAP_CONTROL, 'CapControl');
end;
-{--------------------------------------------------------------------------}
destructor TCapControl.Destroy;
-
begin
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TCapControl.DefineProperties;
+procedure DoReset(Obj: TObj);
begin
+ // force a reset
+ Obj.Reset;
+ //PropertyValue[22] := 'n'; // so it gets reported properly
+end;
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
-
- PropertyName[1] := 'element';
- PropertyName[2] := 'terminal';
- PropertyName[3] := 'capacitor';
- PropertyName[4] := 'type';
- PropertyName[5] := 'PTratio';
- PropertyName[6] := 'CTratio';
- PropertyName[7] := 'ONsetting';
- PropertyName[8] := 'OFFsetting';
- PropertyName[9] := 'Delay';
- PropertyName[10] := 'VoltOverride';
- PropertyName[11] := 'Vmax';
- PropertyName[12] := 'Vmin';
- PropertyName[13] := 'DelayOFF';
- PropertyName[14] := 'DeadTime';
- PropertyName[15] := 'CTPhase';
- PropertyName[16] := 'PTPhase';
- PropertyName[17] := 'VBus';
- PropertyName[18] := 'EventLog';
- PropertyName[19] := 'UserModel';
- PropertyName[20] := 'UserData';
- PropertyName[21] := 'pctMinkvar';
- PropertyName[22] := 'Reset';
-
-
- PropertyHelp[1] := 'Full object name of the circuit element, typically a line or transformer, ' +
- 'to which the capacitor control''s PT and/or CT are connected.' +
- 'There is no default; must be specified.';
- PropertyHelp[2] := 'Number of the terminal of the circuit element to which the CapControl is connected. ' +
- '1 or 2, typically. Default is 1.';
- PropertyHelp[3] := 'Name of Capacitor element which the CapControl controls. No Default; Must be specified.' +
- 'Do not specify the full object name; "Capacitor" is assumed for ' +
- 'the object class. Example:' + CRLF + CRLF +
- 'Capacitor=cap1';
- PropertyHelp[4] := '{Current | voltage | kvar | PF | time } Control type. Specify the ONsetting and OFFsetting ' +
- 'appropriately with the type of control. (See help for ONsetting)';
- PropertyHelp[5] := 'Ratio of the PT that converts the monitored voltage to the control voltage. ' +
- 'Default is 60. If the capacitor is Wye, the 1st phase line-to-neutral voltage is monitored. Else, the line-to-line ' +
- 'voltage (1st - 2nd phase) is monitored.';
- PropertyHelp[6] := 'Ratio of the CT from line amps to control ampere setting for current and kvar control types. ';
- PropertyHelp[7] := 'Value at which the control arms to switch the capacitor ON (or ratchet up a step). ' + CRLF + CRLF +
- 'Type of Control:' + CRLF + CRLF +
- 'Current: Line Amps / CTratio' + CRLF +
- 'Voltage: Line-Neutral (or Line-Line for delta) Volts / PTratio' + CRLF +
- 'kvar: Total kvar, all phases (3-phase for pos seq model). This is directional. ' + CRLF +
- 'PF: Power Factor, Total power in monitored terminal. Negative for Leading. ' + CRLF +
- 'Time: Hrs from Midnight as a floating point number (decimal). 7:30am would be entered as 7.5.';
- PropertyHelp[8] := 'Value at which the control arms to switch the capacitor OFF. (See help for ONsetting)' +
- 'For Time control, is OK to have Off time the next day ( < On time)';
- PropertyHelp[9] := 'Time delay, in seconds, from when the control is armed before it sends out the switching ' +
- 'command to turn ON. The control may reset before the action actually occurs. ' +
- 'This is used to determine which capacity control will act first. Default is 15. You may specify any ' +
- 'floating point number to achieve a model of whatever condition is necessary.';
- PropertyHelp[10] := '{Yes | No} Default is No. Switch to indicate whether VOLTAGE OVERRIDE is to be considered. ' +
- 'Vmax and Vmin must be set to reasonable values if this property is Yes.';
- PropertyHelp[11] := 'Maximum voltage, in volts. If the voltage across the capacitor divided by the PTRATIO is greater ' +
- 'than this voltage, the capacitor will switch OFF regardless of other control settings. ' +
- 'Default is 126 (goes with a PT ratio of 60 for 12.47 kV system).';
- PropertyHelp[12] := 'Minimum voltage, in volts. If the voltage across the capacitor divided by the PTRATIO is less ' +
- 'than this voltage, the capacitor will switch ON regardless of other control settings. ' +
- 'Default is 115 (goes with a PT ratio of 60 for 12.47 kV system).';
- PropertyHelp[13] := 'Time delay, in seconds, for control to turn OFF when present state is ON. Default is 15.';
- PropertyHelp[14] := 'Dead time after capacitor is turned OFF before it can be turned back ON. Default is 300 sec.';
- PropertyHelp[15] := 'Number of the phase being monitored for CURRENT control or one of {AVG | MAX | MIN} for all phases. Default=1. ' +
- 'If delta or L-L connection, enter the first or the two phases being monitored [1-2, 2-3, 3-1]. ' +
- 'Must be less than the number of phases. Does not apply to kvar control which uses all phases by default.';
- PropertyHelp[16] := 'Number of the phase being monitored for VOLTAGE control or one of {AVG | MAX | MIN} for all phases. Default=1. ' +
- 'If delta or L-L connection, enter the first or the two phases being monitored [1-2, 2-3, 3-1]. ' +
- 'Must be less than the number of phases. Does not apply to kvar control which uses all phases by default.';
- PropertyHelp[17] := 'Name of bus to use for voltage override function. Default is bus at monitored terminal. ' +
- 'Sometimes it is useful to monitor a bus in another location to emulate various DMS control algorithms.';
- PropertyHelp[18] := '{Yes/True* | No/False} Default is YES for CapControl. Log control actions to Eventlog.';
- PropertyHelp[19] := 'Name of DLL containing user-written CapControl model, overriding the default model. Set to "none" to negate previous setting. ';
- PropertyHelp[20] := 'String (in quotes or parentheses if necessary) that gets passed to the user-written CapControl model Edit function for defining the data required for that model. ';
- PropertyHelp[21] := 'For PF control option, min percent of total bank kvar at which control will close capacitor switch. Default = 50.';
- PropertyHelp[22] := '{Yes | No} If Yes, forces Reset of this CapControl.';
+procedure TCapControl.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
+begin
+ NumProperties := NumPropsThisClass;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo, False);
+
+ // object references
+ PropertyType[ord(TProp.capacitor)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.capacitor)] := ptruint(@obj.FControlledElement);
+ PropertyOffset2[ord(TProp.capacitor)] := ptruint(DSS.CapacitorClass);
+ PropertyWriteFunction[ord(TProp.capacitor)] := @SetControlledElement;
+ PropertyFlags[ord(TProp.capacitor)] := [TPropertyFlag.WriteByFunction, TPropertyFlag.CheckForVar]; // will automatically substitute @var value
+
+ PropertyType[ord(TProp.element)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.element)] := ptruint(@obj.FMonitoredElement);
+ PropertyOffset2[ord(TProp.element)] := 0;
+ PropertyWriteFunction[ord(TProp.element)] := @SetMonitoredElement;
+ PropertyFlags[ord(TProp.element)] := [TPropertyFlag.WriteByFunction];
+ //PropertyFlags[ord(TProp.element)] := [TPropertyFlag.CheckForVar]; // not required for general cktelements
+
+ // enum properties
+ PropertyType[ord(TProp.typ)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.typ)] := ptruint(@obj.ControlType);
+ PropertyOffset2[ord(TProp.typ)] := PtrInt(TypeEnum);
+
+ PropertyType[ord(TProp.PTphase)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.PTphase)] := ptruint(@obj.ControlVars.FPTPhase);
+ PropertyOffset2[ord(TProp.PTphase)] := PtrInt(DSS.MonPhaseEnum);
+
+ PropertyType[ord(TProp.CTPhase)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.CTPhase)] := ptruint(@obj.ControlVars.FCTPhase);
+ PropertyOffset2[ord(TProp.CTPhase)] := PtrInt(DSS.MonPhaseEnum);
+
+ // string properties
+ PropertyType[ord(TProp.VBus)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.VBus)] := ptruint(@obj.ControlVars.VOverrideBusName);
+
+ PropertyType[ord(TProp.UserModel)] := TPropertyType.StringProperty;
+ PropertyType[ord(TProp.UserData)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.UserModel)] := ptruint(@obj.UserModelNameStr);
+ PropertyOffset[ord(TProp.UserData)] := ptruint(@obj.UserModelEditStr);
+
+ // boolean properties
+ PropertyType[ord(TProp.VoltOverride)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.EventLog)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.VoltOverride)] := ptruint(@obj.ControlVars.Voverride);
+ PropertyOffset[ord(TProp.EventLog)] := ptruint(@obj.ShowEventLog);
+
+ // integer properties
+ PropertyType[ord(TProp.terminal)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.terminal)] := ptruint(@obj.ElementTerminal);
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.PTratio)] := ptruint(@obj.ControlVars.PTRatio);
+ PropertyOffset[ord(TProp.CTratio)] := ptruint(@obj.ControlVars.CTRatio);
+ PropertyOffset[ord(TProp.ONsetting)] := ptruint(@obj.ControlVars.ON_Value);
+ PropertyOffset[ord(TProp.OFFsetting)] := ptruint(@obj.ControlVars.OFF_Value);
+ PropertyOffset[ord(TProp.Delay)] := ptruint(@obj.ControlVars.ONDelay);
+ PropertyOffset[ord(TProp.Vmax)] := ptruint(@obj.ControlVars.Vmax);
+ PropertyOffset[ord(TProp.Vmin)] := ptruint(@obj.ControlVars.Vmin);
+ PropertyOffset[ord(TProp.DelayOFF)] := ptruint(@obj.ControlVars.OFFDelay);
+ PropertyOffset[ord(TProp.DeadTime)] := ptruint(@obj.ControlVars.DeadTime);
+ PropertyOffset[ord(TProp.pctMinkvar)] := ptruint(@obj.FpctMinKvar);
+
+ // boolean action
+ PropertyType[ord(TProp.Reset)] := TPropertyType.BooleanActionProperty;
+ PropertyOffset[ord(TProp.Reset)] := ptruint(@DoReset);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ inherited DefineProperties;
end;
-{--------------------------------------------------------------------------}
-function TCapControl.NewObject(const ObjName: String): Integer;
+function TCapControl.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new CapControl and add it to CapControl class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TCapControlObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-{--------------------------------------------------------------------------}
-function TCapControl.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
+procedure TCapControlObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
-
- // continue parsing WITH contents of Parser
- DSS.ActiveCapControlObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveCapControlObj;
-
- Result := 0;
-
- with DSS.ActiveCapControlObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 352);
- 1:
- ElementName := ConstructElemName(DSS, lowercase(param)); // substitute @var value if any
- 2:
- ElementTerminal := Parser.IntValue;
- 3:
- ControlVars.CapacitorName := 'capacitor.' + param; // will automatically substitute @var value
- 4:
- case lowercase(param)[1] of
- 'c':
- ControlType := CURRENTCONTROL;
- 'v':
- ControlType := VOLTAGECONTROL;
- 'k':
- ControlType := KVARCONTROL;
- 't':
- ControlType := TIMECONTROL;
- 'p':
- ControlType := PFCONTROL;
+ // PF Controller changes
+ if ControlType = PFCONTROL then // TODO: check -- is this correct for all below??
+ with ControlVars do
+ case Idx of
+ ord(TProp.typ):
+ begin
+ PFON_Value := 0.95; // defaults
+ PFOFF_Value := 1.05;
+ end;
+ ord(TProp.ONsetting):
+ begin
+ if (ON_Value >= -1.0) and (ON_Value <= 1.0) then
+ begin
+ if ON_Value < 0.0 then
+ PFON_Value := 2.0 + ON_Value
+ else
+ PFON_Value := ON_Value;
+ end
else
- DoSimpleMsg(Format('Unrecognized CapControl Type: "%s" (Capcontrol.%s)', [param, DSS.ActiveCapControlObj.name]), 352);
+ begin
+ DoSimpleMsg('Invalid PF ON value for "%s"', [FullName], 353);
end;
- 5:
- ControlVars.PTRatio := Parser.DblValue;
- 6:
- ControlVars.CTRatio := Parser.DblValue;
- 7:
- ControlVars.ON_Value := Parser.DblValue;
- 8:
- ControlVars.OFF_Value := Parser.DblValue;
- 9:
- ControlVars.ONDelay := Parser.DblValue;
- 10:
- ControlVars.Voverride := InterpretYesNo(param);
- 11:
- ControlVars.Vmax := Parser.DblValue;
- 12:
- ControlVars.Vmin := Parser.DblValue;
- 13:
- ControlVars.OFFDelay := Parser.DblValue;
- 14:
- ControlVars.DeadTime := Parser.DblValue;
- 15:
- if CompareTextShortest(param, 'avg') = 0 then
- ControlVars.FCTPhase := AVGPHASES
- else
- if CompareTextShortest(param, 'max') = 0 then
- ControlVars.FCTPhase := MAXPHASE
- else
- if CompareTextShortest(param, 'min') = 0 then
- ControlVars.FCTPhase := MINPHASE
- else
- ControlVars.FCTPhase := max(1, Parser.IntValue);
- 16:
- if CompareTextShortest(param, 'avg') = 0 then
- ControlVars.FPTPhase := AVGPHASES
- else
- if CompareTextShortest(param, 'max') = 0 then
- ControlVars.FPTPhase := MAXPHASE
- else
- if CompareTextShortest(param, 'min') = 0 then
- ControlVars.FPTPhase := MINPHASE
- else
- ControlVars.FPTPhase := max(1, Parser.IntValue);
- 17:
+ end;
+ ord(TProp.OFFsetting):
begin
- ControlVars.VoverrideBusSpecified := TRUE;
- ControlVars.VOverrideBusName := Param;
+ if (OFF_Value >= -1.0) and (OFF_Value <= 1.0) then
+ begin
+ if OFF_Value < 0.0 then
+ PFOFF_Value := 2.0 + OFF_Value
+ else
+ PFOFF_Value := OFF_Value;
+ end
+ else
+ begin
+ DoSimpleMsg('Invalid PF OFF value for "%s"', [FullName], 35301);
+ end;
end;
- 18:
- ShowEventLog := InterpretYesNo(param);
- 19:
- UserModel.Name := Parser.StrValue; // Connect to user written model
- 20:
- if UserModel.Exists then
- UserModel.Edit := Parser.StrValue; // Send edit string to user model
- 21:
- FpctMinKvar := Parser.DblValue;
- 22:
- if InterpretYesNo(Param) then
- begin // force a reset
- Reset;
- PropertyValue[22] := 'n'; // so it gets reported properly
+ ord(TProp.CTPhase):
+ if FCTPhase > FNphases then
+ begin
+ DoSimpleMsg('Error: Monitored phase (%d) must be less than or equal to number of phases (%d). ', [FCTPhase, FNphases], 35302);
+ FCTPhase := 1;
end;
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveCapControlObj, ParamPointer - NumPropsthisClass)
- end;
-
-
- {PF Controller changes}
- if ControlType = PFCONTROL then
- with ControlVars do
- case ParamPointer of
- 1:
- PropertyValue[1] := ElementName; // Synch up with change
- 4:
- begin
- PFON_Value := 0.95; // defaults
- PFOFF_Value := 1.05;
- end;
-
- 7:
- begin
- if (ON_Value >= -1.0) and (ON_Value <= 1.0) then
- begin
- if ON_Value < 0.0 then
- PFON_Value := 2.0 + ON_Value
- else
- PFON_Value := ON_Value;
- end
- else
- begin
- DoSimpleMsg('Invalid PF ON value for CapControl.' + DSS.ActiveCapControlObj.Name, 353);
- end;
- end;
- 8:
- begin
- if (OFF_Value >= -1.0) and (OFF_Value <= 1.0) then
- begin
- if OFF_Value < 0.0 then
- PFOFF_Value := 2.0 + OFF_Value
- else
- PFOFF_Value := OFF_Value;
- end
- else
- begin
- DoSimpleMsg('Invalid PF OFF value for CapControl.' + DSS.ActiveCapControlObj.Name, 35301);
- end;
- end;
-
- 15:
- if FCTPhase > FNphases then
- begin
- DoSimpleMsg(Format('Error: Monitored phase(%d) must be less than or equal to number of phases(%d). ', [FCTPhase, FNphases]), 35302);
- FCTPhase := 1;
- end;
-
- 16:
- if FPTPhase > FNphases then
- begin
- DoSimpleMsg(Format('Error: Monitored phase(%d) must be less than or equal to number of phases(%d). ', [FPTPhase, FNphases]), 35303);
- FPTPhase := 1;
- end;
+ ord(TProp.PTPhase):
+ if FPTPhase > FNphases then
+ begin
+ DoSimpleMsg('Error: Monitored phase (%d) must be less than or equal to number of phases (%d). ', [FPTPhase, FNphases], 35303);
+ FPTPhase := 1;
end;
-
- case ParamPointer of
- 19:
- IsUserModel := UserModel.Exists;
end;
- if IsUserModel then
- ControlType := USERCONTROL;
-
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ case Idx of
+ ord(TProp.Capacitor):
+ if ControlledElement <> NIL then
+ ControlVars.CapacitorName := 'capacitor.' + ControlledElement.Name;
+ ord(TProp.VBus):
+ begin
+ ControlVars.VOverrideBusName := AnsiLowerCase(ControlVars.VOverrideBusName);
+ ControlVars.VoverrideBusSpecified := TRUE;
end;
-
- RecalcElementData;
+ ord(TProp.UserModel):
+ begin
+ UserModel.Name := UserModelNameStr; // Connect to user written model
+ IsUserModel := UserModel.Exists;
+ end;
+ ord(TProp.UserData):
+ if UserModel.Exists then
+ UserModel.Edit := UserModelEditStr; // Send edit string to user model
end;
+ if IsUserModel then
+ ControlType := USERCONTROL;
+
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-{--------------------------------------------------------------------------}
-function TCapControl.MakeLike(const CapControlName: String): Integer;
+procedure TCapControlObj.MakeLike(OtherPtr: Pointer);
var
- OtherCapControl: TCapControlObj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this CapControl name in the present collection}
- OtherCapControl := Find(CapControlName);
- if OtherCapControl <> NIL then
- with DSS.ActiveCapControlObj do
- begin
-
- NPhases := OtherCapControl.Fnphases;
- NConds := OtherCapControl.Fnconds; // Force Reallocation of terminal stuff
-
- ElementName := OtherCapControl.ElementName;
- ControlVars.CapacitorName := OtherCapControl.ControlVars.CapacitorName;
- ControlledElement := OtherCapControl.ControlledElement; // Pointer to target circuit element
- MonitoredElement := OtherCapControl.MonitoredElement; // Pointer to target circuit element
-
- ElementTerminal := OtherCapControl.ElementTerminal;
- with ControlVars do
- begin
- PTRatio := OtherCapControl.ControlVars.PTRatio;
- CTRatio := OtherCapControl.ControlVars.CTRatio;
- ControlType := OtherCapControl.ControlType;
- PresentState := OtherCapControl.ControlVars.PresentState;
- ShouldSwitch := OtherCapControl.ControlVars.ShouldSwitch;
- CondOffset := OtherCapControl.ControlVars.CondOffset;
-
- ON_Value := OtherCapControl.ControlVars.ON_Value;
- OFF_Value := OtherCapControl.ControlVars.OFF_Value;
- PFON_Value := OtherCapControl.ControlVars.PFON_Value;
- PFOFF_Value := OtherCapControl.ControlVars.PFOFF_Value;
-
- FCTPhase := OtherCapControl.ControlVars.FCTPhase;
- FPTPhase := OtherCapControl.ControlVars.FPTPhase;
-
- Voverride := OtherCapControl.ControlVars.Voverride;
- VoverrideBusSpecified := OtherCapControl.ControlVars.VoverrideBusSpecified; // Added 8-11-11
- VOverrideBusName := OtherCapControl.ControlVars.VOverrideBusName;
- end;
-
- UserModel.Name := OtherCapControl.UserModel.Name; // Connect to user written models
- IsUserModel := OtherCapControl.IsUserModel;
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
- FpctMinkvar := OtherCapControl.FpctMinkvar;
+ ControlVars.CapacitorName := Other.ControlVars.CapacitorName;
+ ControlledElement := Other.ControlledElement; // Pointer to target circuit element
+ MonitoredElement := Other.MonitoredElement; // Pointer to target circuit element
- ShowEventLog := OtherCapControl.ShowEventLog;
+ ElementTerminal := Other.ElementTerminal;
+ with ControlVars do
+ begin
+ PTRatio := Other.ControlVars.PTRatio;
+ CTRatio := Other.ControlVars.CTRatio;
+ ControlType := Other.ControlType;
+ PresentState := Other.ControlVars.PresentState;
+ ShouldSwitch := Other.ControlVars.ShouldSwitch;
+ CondOffset := Other.ControlVars.CondOffset;
+
+ ON_Value := Other.ControlVars.ON_Value;
+ OFF_Value := Other.ControlVars.OFF_Value;
+ PFON_Value := Other.ControlVars.PFON_Value;
+ PFOFF_Value := Other.ControlVars.PFOFF_Value;
+
+ FCTPhase := Other.ControlVars.FCTPhase;
+ FPTPhase := Other.ControlVars.FPTPhase;
+
+ Voverride := Other.ControlVars.Voverride;
+ VoverrideBusSpecified := Other.ControlVars.VoverrideBusSpecified; // Added 8-11-11
+ VOverrideBusName := Other.ControlVars.VOverrideBusName;
+ end;
+ UserModel.Name := Other.UserModel.Name; // Connect to user written models
+ UserModelNameStr := Other.UserModelNameStr;
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherCapControl.PropertyValue[i];
+ IsUserModel := Other.IsUserModel;
- end
- else
- DoSimpleMsg('Error in CapControl MakeLike: "' + CapControlName + '" Not Found.', 360);
+ FpctMinkvar := Other.FpctMinkvar;
+ ShowEventLog := Other.ShowEventLog;
end;
-
-{==========================================================================}
-{ TCapControlObj }
-{==========================================================================}
-
-
-{--------------------------------------------------------------------------}
constructor TCapControlObj.Create(ParClass: TDSSClass; const CapControlName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(CapControlName);
+ Name := AnsiLowerCase(CapControlName);
DSSObjType := ParClass.DSSClassType;
- NPhases := 3; // Directly set conds and phases
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 1; // this forces allocation of terminals and conductors
// in base class
@@ -594,7 +485,6 @@ constructor TCapControlObj.Create(ParClass: TDSSClass; const CapControlName: Str
PublicDataStruct := @ControlVars; // So User-written models can access
PublicDataSize := Sizeof(TCapControlVars);
- ElementName := '';
ControlledElement := NIL;
ElementTerminal := 1;
ControlVars.CapacitorName := '';
@@ -604,6 +494,7 @@ constructor TCapControlObj.Create(ParClass: TDSSClass; const CapControlName: Str
IsUserModel := FALSE;
UserModel := TCapUserControl.Create(DSS); // Inits handles, FID
+ UserModelNameStr := '';
ControlVars.ControlActionHandle := 0;
@@ -611,16 +502,11 @@ constructor TCapControlObj.Create(ParClass: TDSSClass; const CapControlName: Str
DSSObjType := ParClass.DSSClassType; //cap_CONTROL;
- InitPropertyValues(0);
-
// RecalcElementData;
-
end;
-
destructor TCapControlObj.Destroy;
begin
- ElementName := '';
ControlVars.CapacitorName := '';
if Assigned(cBuffer) then
ReallocMem(cBuffer, 0);
@@ -632,118 +518,91 @@ destructor TCapControlObj.Destroy;
inherited Destroy;
end;
-
-{--------------------------------------------------------------------------}
procedure TCapControlObj.RecalcElementData;
-
-var
- DevIndex: Integer;
-
begin
+ // Check for existence of capacitor
-{Check for existence of capacitor}
+ // 5-21-01 RCD moved this section ahead of monitored element so Nphases gets defined first
-// 5-21-01 RCD moved this section ahead of monitored element so Nphases gets defined first
+ if ControlledElement = NIL then
+ raise Exception.Create(Format(_('CapControl "%s": Capacitor is not set, aborting.'), [Name]));
- Devindex := GetCktElementIndex(ControlVars.CapacitorName); // Global function
- if DevIndex > 0 then
- begin // Both capacitor and monitored element must already exist
- ControlledElement := ActiveCircuit.CktElements.Get(DevIndex);
- ControlledCapacitor := This_Capacitor;
- Nphases := ControlledElement.NPhases; // Force number of phases to be same Added 5/21/01 RCD
- Nconds := FNphases;
- ControlledElement.ActiveTerminalIdx := 1; // Make the 1 st terminal active
- // Get control synched up with capacitor
- with ControlledCapacitor do
- if ControlVars.AvailableSteps = Numsteps then
- ControlledElement.Closed[0] := FALSE
- else
- ControlledElement.Closed[0] := TRUE;
- if ControlledElement.Closed[0] // Check state of phases of active terminal
- then
- ControlVars.PresentState := CTRL_CLOSE
+ if MonitoredElement = NIL then
+ raise Exception.Create(Format(_('CapControl "%s": Element is not set, aborting.'), [Name]));
+
+ // Both capacitor and monitored element must already exist
+ ControlledCapacitor := This_Capacitor;
+ FNphases := ControlledElement.NPhases; // Force number of phases to be same Added 5/21/01 RCD
+ Nconds := FNphases;
+ ControlledElement.ActiveTerminalIdx := 1; // Make the 1 st terminal active
+ // Get control synched up with capacitor
+ with ControlledCapacitor do
+ if ControlVars.AvailableSteps = Numsteps then
+ ControlledElement.Closed[0] := FALSE
else
- ControlVars.PresentState := CTRL_OPEN;
- end
+ ControlledElement.Closed[0] := TRUE;
+
+ if ControlledElement.Closed[0] // Check state of phases of active terminal
+ then
+ ControlVars.PresentState := CTRL_CLOSE
else
- begin
- ControlledElement := NIL; // element not found
- DoErrorMsg('CapControl: "' + Self.Name + '"', 'Capacitor Element "' + ControlVars.CapacitorName + '" Not Found.',
- ' Element must be defined previously.', 361);
- end;
+ ControlVars.PresentState := CTRL_OPEN;
ControlVars.InitialState := ControlVars.PresentState;
-{Check for existence of monitored element}
- Devindex := GetCktElementIndex(ElementName); // Global function
- if DevIndex > 0 then
+ if ElementTerminal > MonitoredElement.Nterms then
begin
- MonitoredElement := ActiveCircuit.CktElements.Get(DevIndex);
- if ElementTerminal > MonitoredElement.Nterms then
- begin
- DoErrorMsg('CapControl.' + Name + ':',
- 'Terminal no. "' + '" does not exist.',
- 'Re-specify terminal no.', 362);
- end
- else
- begin
- // Sets name of i-th terminal's connected bus in CapControl's buslist
- Setbus(1, MonitoredElement.GetBus(ElementTerminal));
- // Allocate a buffer bigenough to hold everything from the monitored element
- ReAllocMem(cBuffer, SizeOF(cbuffer^[1]) * MonitoredElement.Yorder);
- ControlVars.CondOffset := (ElementTerminal - 1) * MonitoredElement.NConds; // for speedy sampling
- end;
+ DoErrorMsg(FullName,
+ Format(_('Terminal no. "%s" does not exist.'), [ElementTerminal]),
+ _('Re-specify terminal no.'), 362);
end
else
- DoSimpleMsg('Monitored Element in CapControl.' + Name + ' does not exist:"' + ElementName + '"', 363);
+ begin
+ // Sets name of i-th terminal's connected bus in CapControl's buslist
+ Setbus(1, MonitoredElement.GetBus(ElementTerminal));
+ // Allocate a buffer bigenough to hold everything from the monitored element
+ ReAllocMem(cBuffer, SizeOF(cbuffer^[1]) * MonitoredElement.Yorder);
+ ControlVars.CondOffset := (ElementTerminal - 1) * MonitoredElement.NConds; // for speedy sampling
+ end;
- {Alternative override bus}
+ // Alternative override bus
if ControlVars.VoverrideBusSpecified then
with ControlVars do
begin
VOverrideBusIndex := ActiveCircuit.BusList.Find(VOverrideBusName);
if VOverrideBusIndex = 0 then
begin
- DoSimpleMsg(Format('CapControl.%s: Voltage override Bus "%s" not found. Did you wait until buses were defined? Reverting to default.', [Name, VOverrideBusName]), 10361);
+ DoSimpleMsg('%s: Voltage override Bus "%s" not found. Did you wait until buses were defined? Reverting to default.', [FullName, VOverrideBusName], 10361);
VoverrideBusSpecified := FALSE;
end;
end;
- // User model property update, if necessary
+ // User model property update, if necessary
if Usermodel.Exists then
UserModel.UpdateModel; // Checks for existence and Selects
end;
-procedure TCapControlObj.MakePosSequence;
+procedure TCapControlObj.MakePosSequence();
begin
if ControlledElement <> NIL then
begin
Enabled := ControlledElement.Enabled;
- Nphases := ControlledElement.NPhases;
+ FNphases := ControlledElement.NPhases;
Nconds := FNphases;
end;
if MonitoredElement <> NIL then
begin
Setbus(1, MonitoredElement.GetBus(ElementTerminal));
- // Allocate a buffer bigenough to hold everything from the monitored element
+ // Allocate a buffer bigenough to hold everything from the monitored element
ReAllocMem(cBuffer, SizeOF(cbuffer^[1]) * MonitoredElement.Yorder);
ControlVars.CondOffset := (ElementTerminal - 1) * MonitoredElement.NConds; // for speedy sampling
end;
inherited;
end;
-{--------------------------------------------------------------------------}
-procedure TCapControlObj.CalcYPrim;
-begin
- // leave YPrims as nil and they will be ignored
- // Yprim is zeroed when created. Leave it as is.
- // IF YPrim=nil THEN YPrim := TcMatrix.CreateMatrix(Yorder);
-end;
-
-{--------------------------------------------------------------------------}
procedure TCapControlObj.GetBusVoltages(pBus: TDSSBus; Buff: pComplexArray);
var
j: Integer;
@@ -755,14 +614,10 @@ procedure TCapControlObj.GetBusVoltages(pBus: TDSSBus; Buff: pComplexArray);
end;
procedure TCapControlObj.GetControlCurrent(var ControlCurrent: Double);
-
// Get current to control on based on type of control specified.
-
var
i: Integer;
-
begin
-
with ControlVars do
case FCTphase of
AVGPHASES:
@@ -787,66 +642,27 @@ procedure TCapControlObj.GetControlCurrent(var ControlCurrent: Double);
ControlCurrent := ControlCurrent / CTRatio;
end;
else
- {Just use one phase because that's what most controls do.}
+ // Just use one phase because that's what most controls do.
ControlCurrent := Cabs(Cbuffer^[FCTphase]) / CTRatio; // monitored phase only
end;
-
-
end;
-procedure TCapControlObj.GetCurrents(Curr: pComplexArray);
-var
- i: Integer;
-begin
-
- for i := 1 to Fnconds do
- Curr^[i] := CZERO;
-
-end;
-
-{--------------------------------------------------------------------------}
-procedure TCapControlObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
- if Complete then
- begin
- FSWriteln(F);
- end;
-
-end;
-
-
-{--------------------------------------------------------------------------}
procedure TCapControlObj.DoPendingAction(const Code, ProxyHdl: Integer);
-
begin
-
ControlledElement.ActiveTerminalIdx := 1; // Set active terminal of capacitor to terminal 1
- {Allow user control to do something}
+ // Allow user control to do something
case ControlType of
USERCONTROL:
if UserModel.Exists then
begin
UserModel.DoPending(Code, ProxyHdl);
- // If control action changes last step in service, force update of Yprim and Fstates array
+ // If control action changes last step in service, force update of Yprim and Fstates array
ControlledCapacitor.LastStepInService := ControlVars.LastStepInService;
- // Usermodel could override Pending change so the rest of this procedure is ignored.
+ // Usermodel could override Pending change so the rest of this procedure is ignored.
end;
end;
-
with ControlVars do
case PendingChange of
CTRL_OPEN:
@@ -855,7 +671,6 @@ procedure TCapControlObj.DoPendingAction(const Code, ProxyHdl: Integer);
begin
if PresentState = CTRL_CLOSE then
begin
-
ControlledElement.Closed[0] := FALSE; // Open all phases of active terminal
ControlledCapacitor.SubtractStep;
@@ -899,7 +714,7 @@ procedure TCapControlObj.DoPendingAction(const Code, ProxyHdl: Integer);
end;
end;
else
- {Do Nothing for NONE if the control has reset}
+ // Do Nothing for NONE if the control has reset
end;
with ControlVars do
@@ -911,9 +726,7 @@ procedure TCapControlObj.DoPendingAction(const Code, ProxyHdl: Integer);
end;
procedure TCapControlObj.GetControlVoltage(var ControlVoltage: Double);
-
// Get Voltage used for voltage control based on specified options
-
var
i: Integer;
@@ -949,20 +762,18 @@ procedure TCapControlObj.GetControlVoltage(var ControlVoltage: Double);
ControlVoltage := ControlVoltage / PTRatio;
end;
else
- {Just use one phase because that's what most controls do.}
- // Use L-L aB if capacitor is delta connected!!
+ // Just use one phase because that's what most controls do.
+ // Use L-L aB if capacitor is delta connected!!
case TCapacitorObj(ControlledElement).Connection of
- 1:
- ControlVoltage := Cabs(Csub(cBuffer^[FPTPhase], cBuffer^[NextDeltaPhase(FPTPhase)])) / PTRatio; // Delta
+ TCapacitorConnection.Delta:
+ ControlVoltage := Cabs(cBuffer^[FPTPhase] - cBuffer^[NextDeltaPhase(FPTPhase)]) / PTRatio;
else
ControlVoltage := Cabs(cBuffer^[FPTPhase]) / PTRatio; // Wye - Default
end;
end;
end;
-{--------------------------------------------------------------------------}
procedure TCapControlObj.Sample;
-
var
CurrTest,
Vtest,
@@ -972,7 +783,6 @@ procedure TCapControlObj.Sample;
PF: Double;
Sabs: Double;
-
function PF1to2(const Spower: Complex): Double; // return PF in range of 1 to 2
begin
Sabs := Cabs(Spower);
@@ -984,9 +794,7 @@ procedure TCapControlObj.Sample;
Result := 2.0 - Result;
end;
-
begin
-
ControlledElement.ActiveTerminalIdx := 1;
if ControlledElement.Closed[0] // Check state of phases of active terminal
then
@@ -994,11 +802,11 @@ procedure TCapControlObj.Sample;
else
ControlVars.PresentState := CTRL_OPEN;
- with MonitoredElement, ControlVars do
+ with MonitoredElement, ControlVars do
begin
ShouldSwitch := FALSE;
- // First Check voltage override
+ // First Check voltage override
if Voverride then
if ControlType <> VOLTAGECONTROL then
begin // Don't bother for voltage control
@@ -1032,18 +840,13 @@ procedure TCapControlObj.Sample;
AppendtoEventLog('Capacitor.' + ControlledElement.Name, Format('High Voltage Override: %.8g V', [Vtest]));
end;
end;
-
-
end;
-
if not ShouldSwitch then // Else skip other control evaluations
case ControlType of
-
- CURRENTCONTROL: {Current}
+ CURRENTCONTROL:
begin
-
- // Check largest Current of all phases of monitored element
+ // Check largest Current of all phases of monitored element
MonitoredElement.GetCurrents(cBuffer);
GetControlCurrent(CurrTest);
@@ -1076,10 +879,9 @@ procedure TCapControlObj.Sample;
else // Reset
PendingChange := CTRL_NONE;
end;
-
end;
- VOLTAGECONTROL: {Voltage}
+ VOLTAGECONTROL:
begin
MonitoredElement.GetTermVoltages(ElementTerminal, cBuffer);
@@ -1113,12 +915,11 @@ procedure TCapControlObj.Sample;
end;
end;
end;
-
end;
- KVARCONTROL: {kvar}
+ KVARCONTROL:
begin
- //----MonitoredElement.ActiveTerminalIdx := ElementTerminal;
+ //----MonitoredElement.ActiveTerminalIdx := ElementTerminal;
S := MonitoredElement.Power[ElementTerminal];
Q := S.im * 0.001; // kvar
@@ -1149,15 +950,12 @@ procedure TCapControlObj.Sample;
else // Reset
PendingChange := CTRL_NONE;
end;
-
end;
-
- {User Control}
USERCONTROL:
if UserModel.Exists then // selects the model associated with this control
begin
- // Load up test data into the public data record
- SampleP := CmulReal(MonitoredElement.Power[ElementTerminal], 0.001); // kW kvar
+ // Load up test data into the public data record
+ SampleP := MonitoredElement.Power[ElementTerminal] * 0.001; // kW kvar
MonitoredElement.GetTermVoltages(ElementTerminal, cBuffer);
GetControlVoltage(SampleV);
@@ -1170,18 +968,13 @@ procedure TCapControlObj.Sample;
LastStepInService := ControlledCapacitor.LastStepInService;
UserModel.Sample; // Sets the switching flags
-
end;
-
-
- TIMECONTROL: {time}
- {7-8-10 NormalizeToTOD Algorithm modified to close logic hole between 11 PM and midnight}
+ TIMECONTROL:
begin
with ActiveCircuit.Solution do
begin
NormalizedTime := NormalizeToTOD(DynaVars.intHour, DynaVars.t);
end;
- { 1/28/09 Code modified to accommodate OFF_Value < ON_Value }
case PresentState of
CTRL_OPEN:
if OFF_Value > ON_Value then
@@ -1253,8 +1046,8 @@ procedure TCapControlObj.Sample;
S := MonitoredElement.Power[ElementTerminal];
PF := PF1to2(S);
- {PF is in range of 0 .. 2; Leading is 1..2}
- {When turning on make sure there is at least half the kvar of the bank}
+ // PF is in range of 0 .. 2; Leading is 1..2
+ // When turning on make sure there is at least half the kvar of the bank
case PresentState of
CTRL_OPEN:
@@ -1296,7 +1089,6 @@ procedure TCapControlObj.Sample;
if PendingChange = CTRL_CLOSE then
begin
if (Solution.DynaVars.t + Solution.DynaVars.intHour * 3600.0 - LastOpenTime) < DeadTime then // delay the close operation
- {2-6-09 Added ONDelay to Deadtime so that all caps do not close back in at same time}
TimeDelay := Max(ONDelay, (Deadtime + ONDelay) - (Solution.DynaVars.t + Solution.DynaVars.intHour * 3600.0 - LastOpenTime))
else
TimeDelay := ONDelay;
@@ -1321,9 +1113,7 @@ procedure TCapControlObj.Sample;
function TCapControlObj.Get_Capacitor: TCapacitorObj;
begin
-
Result := ControlledElement as TCapacitorObj;
-
end;
@@ -1337,9 +1127,7 @@ function TCapControlObj.NormalizeToTOD(h: Integer; sec: Double): Double;
// Resulting time should be 0:00+ to 24:00 inclusive.
var
HourOfDay: Integer;
-
begin
-
if h > 24 then
HourOfDay := (h - ((h - 1) div 24) * 24) // creates numbers 1..24
else
@@ -1350,10 +1138,8 @@ function TCapControlObj.NormalizeToTOD(h: Integer; sec: Double): Double;
// If the TOD is at least slightly greater than 24:00 wrap around to 0:00
if Result - 24.0 > Epsilon then
Result := Result - 24.0; // Wrap around
-
end;
-
procedure TCapControlObj.Reset;
begin
PendingChange := CTRL_NONE;
@@ -1372,35 +1158,6 @@ procedure TCapControlObj.Reset;
end;
end;
-procedure TCapControlObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := ''; //'element';
- PropertyValue[2] := '1'; //'terminal';
- PropertyValue[3] := '';
- PropertyValue[4] := 'current';
- PropertyValue[5] := '60';
- PropertyValue[6] := '60';
- PropertyValue[7] := '300';
- PropertyValue[8] := '200';
- PropertyValue[9] := '15';
- PropertyValue[10] := 'NO';
- PropertyValue[11] := '126';
- PropertyValue[12] := '115';
- PropertyValue[13] := '15';
- PropertyValue[14] := '300';
- PropertyValue[15] := '1';
- PropertyValue[16] := '1';
- PropertyValue[17] := '';
- PropertyValue[18] := 'YES';
- PropertyValue[19] := '';
- PropertyValue[20] := '';
- PropertyValue[21] := '50';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
procedure TCapControlObj.Set_PendingChange(const Value: EControlAction);
begin
ControlVars.FPendingChange := Value;
@@ -1414,6 +1171,5 @@ procedure TCapControlObj.Set_Enabled(Value: Boolean);
FEnabled := Value;
end;
-initialization
-
+finalization TypeEnum.Free;
end.
diff --git a/src/Controls/CapControlVars.pas b/src/Controls/CapControlVars.pas
deleted file mode 100644
index d8f17930f..000000000
--- a/src/Controls/CapControlVars.pas
+++ /dev/null
@@ -1,82 +0,0 @@
-unit CapControlVars;
-{
- ----------------------------------------------------------
- Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
- All rights reserved.
- ----------------------------------------------------------
-}
-{Header file for CapControlVars}
-
-interface
-
-{ For user DLL, import Definitions of control actions directly }
-
-
-{$IFDEF USER_DLL}
-Uses ucomplex;
-{$INCLUDE ControlActionDefs.txt}
-{$ELSE}
-Uses ucomplex, ControlElem;
-{$ENDIF}
-
-Type
-
-
- ECapControlType = (
- CURRENTCONTROL,
- VOLTAGECONTROL,
- KVARCONTROL,
- TIMECONTROL,
- PFCONTROL,
- USERCONTROL
- );
-
-
- {Fixed record structure for Public CapControl variables}
- TCapControlVars = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}Packed{$ENDIF} Record
-
- FCTPhase,
- FPTPhase :Integer; // "ALL" is -1
-
- ON_Value,
- OFF_Value,
- PFON_Value,
- PFOFF_Value,
- CTRatio,
- PTRatio,
- ONDelay,
- OFFDelay,
- DeadTime,
- LastOpenTime :Double;
-
- Voverride :Boolean;
- VoverrideEvent :Boolean;
- VoverrideBusSpecified :Boolean; // Added 8-11-11
-
- VOverrideBusIndex :Integer;
-
- Vmax :Double;
- Vmin :Double;
- FPendingChange :EControlAction;
- ShouldSwitch :Boolean; // True: action is pending
- Armed :Boolean; // Control is armed for switching unless reset
- PresentState :EControlAction;
- InitialState :EControlAction;
-
- SampleP :Complex; // two 64-bit numbers, kW, kvar
- SampleV :Double;
- SampleCurr :Double;
-
- NumCapSteps : Integer;
- AvailableSteps : Integer; // available steps in controlled capacitor
- LastStepInService : Integer; // Change this to force an update of cap states
-
- VOverrideBusName : String;
- CapacitorName : String;
- ControlActionHandle : Integer;
- CondOffset : Integer; // Offset for monitored terminal
- End;
-
- implementation
-
-end.
diff --git a/src/Controls/CapUserControl.pas b/src/Controls/CapUserControl.pas
index 88058cfa4..19dc81901 100644
--- a/src/Controls/CapUserControl.pas
+++ b/src/Controls/CapUserControl.pas
@@ -8,27 +8,24 @@
Interface to user-written CapControl DLL
}
-
-
-{$M+}
{
----------------------------------------------------------
Copyright (c) 2012, Electric Power Research Institute, Inc.
All rights reserved.
----------------------------------------------------------
-
- 3-31-12
- Converted from GenUserModel.Pas
-
}
interface
-USES CapControlVars, Dynamics, DSSCallBackRoutines, ucomplex, Arraydef, DSSClass;
+USES
+ Dynamics,
+ DSSCallBackRoutines,
+ UComplex,
+ DSSUcomplex,
+ Arraydef,
+ DSSClass;
TYPE
-
-
TCapUserControl = class(TObject)
private
FHandle: NativeUInt; // Handle to DLL containing user model
@@ -51,8 +48,6 @@ TCapUserControl = class(TObject)
procedure Set_Edit(const Value: String);
function Get_Exists: Boolean;
- protected
-
public
DSS: TDSSContext;
@@ -72,23 +67,18 @@ TCapUserControl = class(TObject)
property Name:String read Fname write Set_Name;
property Edit:String write Set_Edit;
property Exists:Boolean read Get_Exists;
- published
-
end;
-
-
implementation
-Uses DSSGlobals, {$IFDEF FPC}dynlibs{$ELSE}Windows{$ENDIF}, Sysutils, DSSHelper;
+Uses DSSGlobals, dynlibs, Sysutils, DSSHelper;
-{ TCapUserControl }
function TCapUserControl.CheckFuncError(Addr: Pointer; FuncName: String): Pointer;
begin
If Addr=nil then
Begin
- DoSimpleMsg(DSS, 'CapControl User Model Does Not Have Required Function: ' + FuncName, 569);
+ DoSimpleMsg(DSS, 'CapControl User Model Does Not Have Required Function: "%s"', [FuncName], 569);
FuncError := True;
End;
Result := Addr;
@@ -100,12 +90,10 @@ constructor TCapUserControl.Create(dssContext: TDSSContext);
FID := 0;
Fhandle := 0;
FName := '';
-
end;
destructor TCapUserControl.Destroy;
begin
-
Try
If FID <> 0 Then FDelete(FID); // Clean up all memory associated with this instance
Finally
@@ -114,7 +102,6 @@ destructor TCapUserControl.Destroy;
inherited;
-
end;
procedure TCapUserControl.DoPending(const Code, ProxyHdl: integer);
@@ -140,14 +127,10 @@ function TCapUserControl.Get_Exists: Boolean;
Else Result := False;
end;
-
procedure TCapUserControl.Sample;
// Sample the cap control
-
begin
-
If FID <> 0 Then FSample;
-
end;
procedure TCapUserControl.Select;
@@ -155,7 +138,6 @@ procedure TCapUserControl.Select;
Fselect(FID);
end;
-
procedure TCapUserControl.Set_Edit(const Value: String);
begin
If FID <> 0 Then FEdit(pAnsichar(AnsiString(Value)), Length(Value));
@@ -164,7 +146,6 @@ procedure TCapUserControl.Set_Edit(const Value: String);
procedure TCapUserControl.Set_Name(const Value:String);
begin
-
{If Model already points to something, then free it}
IF FHandle <> 0 Then
@@ -189,7 +170,7 @@ procedure TCapUserControl.Set_Name(const Value:String);
End;
If FHandle = 0 Then
- DoSimpleMsg(DSS, 'CapControl User Model ' + Value + ' Load Library Failed. DSS Directory = '+DSSDirectory, 570)
+ DoSimpleMsg(DSS, 'CapControl User Model %s Load Library Failed. DSS Directory = "%s"', [Value, DSSDirectory], 570)
Else
Begin
FName := Value;
@@ -206,7 +187,7 @@ procedure TCapUserControl.Set_Name(const Value:String);
If FuncError Then Begin
If not FreeLibrary(FHandle) then
- DoSimpleMsg(DSS, 'Error Freeing DLL: '+Fname, 10570); // decrement the reference count
+ DoSimpleMsg(DSS, 'Error Freeing DLL: "%s"', [Fname], 10570); // decrement the reference count
FID := 0;
FHandle := 0;
FName := '';
@@ -217,12 +198,9 @@ procedure TCapUserControl.Set_Name(const Value:String);
End;
end;
-
-
procedure TCapUserControl.UpdateModel;
begin
If FID <> 0 Then FUpdateModel;
-
end;
end.
diff --git a/src/Controls/ControlActionDefs.txt b/src/Controls/ControlActionDefs.txt
deleted file mode 100644
index 8303bc190..000000000
--- a/src/Controls/ControlActionDefs.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-
- TYPE
-
- EControlAction = (
- CTRL_NONE,
- CTRL_OPEN,
- CTRL_CLOSE,
- CTRL_RESET,
- CTRL_LOCK,
- CTRL_UNLOCK,
- CTRL_TAPUP,
- CTRL_TAPDOWN);
\ No newline at end of file
diff --git a/src/Controls/ControlClass.pas b/src/Controls/ControlClass.pas
index d100fc6d4..d79577ae8 100644
--- a/src/Controls/ControlClass.pas
+++ b/src/Controls/ControlClass.pas
@@ -6,12 +6,7 @@
All rights reserved.
----------------------------------------------------------
}
-{
- Base for control classes
-}
-
-{$M+}
-
+// Base for control classes
interface
uses
@@ -20,21 +15,13 @@ interface
type
TControlClass = class(TCktElementClass)
- PRIVATE
-
PROTECTED
- procedure ClassEdit(const ActiveControlObj: Pointer; const ParamPointer: Integer);
- procedure ClassMakeLike(const OtherObj: Pointer);
-
- procedure CountProperties; // Add no. of intrinsic properties
- procedure DefineProperties; // Add Properties of this class to propName
+ procedure CountPropertiesAndAllocate; override;
+ procedure DefineProperties; override;
PUBLIC
- NumControlClassProps: Integer;
- constructor Create(dssContext: TDSSContext);
+ constructor Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
destructor Destroy; OVERRIDE;
- PUBLISHED
-
end;
@@ -42,76 +29,34 @@ implementation
uses
ControlElem,
- ParserDel,
DSSClassDefs,
DSSGlobals;
-constructor TControlClass.Create(dssContext: TDSSContext);
-begin
+const
+ NumPropsThisClass = 0;
- inherited Create(dssContext);
- NumControlClassProps := 0;
- DSSClassType := CTRL_ELEMENT;
+constructor TControlClass.Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
+begin
+ inherited Create(dssContext, DSSClsType or CTRL_ELEMENT, DSSClsName);
+ ClassParents.Add('ControlClass');
end;
destructor TControlClass.Destroy;
-
begin
inherited Destroy;
end;
-procedure TControlClass.CountProperties;
+procedure TControlClass.CountPropertiesAndAllocate;
begin
- NumProperties := NumProperties + NumControlClassProps;
- inherited CountProperties;
+ NumProperties := NumProperties + NumPropsThisClass;
+ inherited CountPropertiesAndAllocate;
end;
procedure TControlClass.DefineProperties;
-
-// Define the properties for the base power delivery element class
-
begin
- // no properties
- // PropertyName^[ActiveProperty + 1] := 'propname';
- // PropertyHelp^[ActiveProperty + 1] := 'prop help';
-
- ActiveProperty := ActiveProperty + NumControlClassProps;
-
+ // no properties
+ ActiveProperty := ActiveProperty + NumPropsThisClass;
inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TControlClass.ClassEdit(const ActiveControlObj: Pointer; const ParamPointer: Integer);
-begin
- // continue parsing with contents of Parser
- if ParamPointer > 0 then
- with TControlElem(ActiveControlObj) do
- begin
-
- //CASE ParamPointer OF
- //1: BaseFrequency := Parser.Dblvalue;
- //ELSE
- inherited ClassEdit(ActiveControlObj, ParamPointer - NumControlClassProps)
- //END;
- end;
-
-end;
-
-procedure TControlClass.ClassMakeLike(const OtherObj: Pointer);
-
-//Var
-// OtherControlObj : TControlElem;
-begin
-
-// OtherControlObj := TControlElem(OtherObj);
- TControlElem.Create(OtherObj);
-
- //With TPCElement(ActiveDSSObject) Do
- //Begin
- // value:= OtherControlObj.value;
- //End;
-
-end;
-
-
end.
diff --git a/src/Controls/ControlElem.pas b/src/Controls/ControlElem.pas
index f015b3a1b..2aa73d136 100644
--- a/src/Controls/ControlElem.pas
+++ b/src/Controls/ControlElem.pas
@@ -13,24 +13,31 @@ interface
uses
CktElement,
Bus,
- ucomplex,
+ UComplex, DSSUcomplex,
DSSClass;
-{$INCLUDE ControlActionDefs.txt}
-
type
+{$PUSH}
+{$Z4} // keep enums as int32 values
+ EControlAction = (
+ CTRL_NONE,
+ CTRL_OPEN,
+ CTRL_CLOSE,
+ CTRL_RESET,
+ CTRL_LOCK,
+ CTRL_UNLOCK,
+ CTRL_TAPUP,
+ CTRL_TAPDOWN
+ );
+{$POP}
TControlElem = class(TDSSCktElement)
PRIVATE
- FControlledElement: TDSSCktElement;
- FMonitoredElement: TDSSCktElement;
- procedure Set_ControlledElement(const Value: TDSSCktElement); // Pointer to target circuit element
procedure RemoveSelfFromControlelementList(CktElem: TDSSCktElement);
- procedure Set_MonitoredElement(const Value: TDSSCktElement);
PUBLIC
-
- ElementName: String;
+ FControlledElement: TDSSCktElement;
+ FMonitoredElement: TDSSCktElement;
ElementTerminal: Integer;
ControlledBusName: String; // If different than terminal
ControlledBus: TDSSBus;
@@ -38,20 +45,26 @@ TControlElem = class(TDSSCktElement)
MonitorVarIndex: Integer;
TimeDelay,
DblTraceParameter: Double;
- ShowEventLog: Boolean;
+ ShowEventLog: LongBool;
constructor Create(ParClass: TDSSClass);
destructor Destroy; OVERRIDE;
+ procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Always Zero
+ procedure CalcYPrim; OVERRIDE; // Always Zero
+
procedure Sample; VIRTUAL; // Sample control quantities and set action times in Control Queue
procedure DoPendingAction(const Code, ProxyHdl: Integer); VIRTUAL; // Do the action that is pending from last sample
procedure Reset; VIRTUAL;
-
+ procedure Set_ControlledElement(const Value: TDSSCktElement); // Pointer to target circuit element
+ procedure Set_MonitoredElement(const Value: TDSSCktElement);
property ControlledElement: TDSSCktElement READ FControlledElement WRITE Set_ControlledElement;
property MonitoredElement: TDSSCktElement READ FMonitoredElement WRITE Set_MonitoredElement;
-
end;
+procedure SetMonitoredElement(obj: TControlElem; el: TDSSCktElement);
+procedure SetControlledElement(obj: TControlElem; el: TDSSCktElement);
+
const
USER_BASE_ACTION_CODE = 100;
@@ -63,6 +76,16 @@ implementation
Sysutils,
DSSPointerList;
+procedure SetMonitoredElement(obj: TControlElem; el: TDSSCktElement);
+begin
+ obj.Set_MonitoredElement(el);
+end;
+
+procedure SetControlledElement(obj: TControlElem; el: TDSSCktElement);
+begin
+ obj.Set_ControlledElement(el);
+end;
+
constructor TControlElem.Create(ParClass: TDSSClass);
begin
inherited Create(ParClass);
@@ -83,7 +106,7 @@ destructor TControlElem.Destroy;
procedure TControlElem.DoPendingAction;
begin
// virtual function - should be overridden
- DoSimpleMsg('Programming Error: Reached base class for DoPendingAction.' + CRLF + 'Device: ' + DSSClassName + '.' + Name, 460);
+ DoSimpleMsg('Programming Error: Reached base class for DoPendingAction.' + CRLF + 'Device: ' + FullName, 460);
end;
procedure TControlElem.RemoveSelfFromControlElementList(CktElem: TDSSCktElement);
@@ -111,26 +134,24 @@ procedure TControlElem.RemoveSelfFromControlElementList(CktElem: TDSSCktElement)
procedure TControlElem.Reset;
begin
- DoSimpleMsg('Programming Error: Reached base class for Reset.' + CRLF + 'Device: ' + DSSClassName + '.' + Name, 461);
+ DoSimpleMsg('Programming Error: Reached base class for Reset.' + CRLF + 'Device: ' + FullName, 461);
end;
procedure TControlElem.Sample;
begin
// virtual function - should be overridden
- DoSimpleMsg('Programming Error: Reached base class for Sample.' + CRLF + 'Device: ' + DSSClassName + '.' + Name, 462);
+ DoSimpleMsg('Programming Error: Reached base class for Sample.' + CRLF + 'Device: ' + FullName, 462);
end;
-
procedure TControlElem.Set_ControlledElement(const Value: TDSSCktElement);
begin
-
try
// Check for reassignment of Controlled element and remove from list
if Assigned(FControlledElement) then
with FControlledElement do
begin
if ControlElementList.Count = 1 then
- HasControl := FALSE;
+ Exclude(Flags, Flg.HasControl);
RemoveSelfFromControlElementList(FControlledElement);
end;
finally
@@ -138,7 +159,7 @@ procedure TControlElem.Set_ControlledElement(const Value: TDSSCktElement);
if Assigned(FControlledElement) then
with FControlledElement do
begin
- HasControl := TRUE;
+ Include(Flags, Flg.HasControl);
ControlElementList.Add(Self);
end;
end;
@@ -148,7 +169,23 @@ procedure TControlElem.Set_MonitoredElement(const Value: TDSSCktElement);
begin
FMonitoredElement := Value;
if Assigned(FMonitoredElement) then
- FMonitoredElement.IsMonitored := TRUE;
+ Include(FMonitoredElement.Flags, Flg.IsMonitored);
end;
+procedure TControlElem.GetCurrents(Curr: pComplexArray);
+var
+ i: Integer;
+begin
+ for i := 1 to Fnconds do
+ Curr^[i] := CZERO;
+end;
+
+procedure TControlElem.CalcYPrim;
+begin
+ // leave YPrims as nil and they will be ignored
+ // Yprim is zeroed when created. Leave it as is.
+ // IF YPrim=nil THEN YPrim := TcMatrix.CreateMatrix(Yorder);
+end;
+
+
end.
diff --git a/src/Controls/ESPVLControl.pas b/src/Controls/ESPVLControl.pas
index 9f99e3bcb..1c51a1182 100644
--- a/src/Controls/ESPVLControl.pas
+++ b/src/Controls/ESPVLControl.pas
@@ -18,7 +18,6 @@
New ESPVLControl.Name=myname Element=devclass.name terminal=[ 1|2|...] StorageList = (gen1 gen2 ...)
-
}
interface
@@ -30,102 +29,95 @@ interface
CktElement,
DSSClass,
Arraydef,
- ucomplex,
+ UComplex, DSSUcomplex,
utilities,
DSSPointerList,
Classes,
Loadshape;
type
+{$SCOPEDENUMS ON}
+ TESPVLControlProp = (
+ INVALID = 0,
+ Element = 1,
+ Terminal = 2,
+ Typ = 3,
+ kWBand = 4,
+ kvarlimit = 5,
+ LocalControlList = 6,
+ LocalControlWeights = 7,
+ PVSystemList = 8,
+ PVSystemWeights = 9,
+ StorageList = 10,
+ StorageWeights = 11
+ // Forecast = 12 -- Unused
+ );
+{$SCOPEDENUMS OFF}
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TESPVLControl = class(TControlClass)
- PRIVATE
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const ESPVLControlName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TESPVLControlObj = class(TControlElem)
PRIVATE
Ftype: Integer; {1=System controller; 2=Local controller}
- {System Controller Variables}
+ {System Controller Variables}
- // Local Controllers under supervision of System Controller
+ // Local Controllers under supervision of System Controller
FLocalControlListSize: Integer;
FLocalControlNameList: TStringList;
FLocalControlPointerList: TDSSPointerList;
FLocalControlWeights: pDoubleArray;
+ {Local Controller Variables}
- {Local Controller Variables}
-
- // PVSystems under supervision of this Local Controller
+ // PVSystems under supervision of this Local Controller
FPVsystemListSize: Integer;
FPVsystemNameList: TStringList;
FPVsystemPointerList: TDSSPointerList;
FPVSystemWeights: pDoubleArray;
- // Storage Devices under supervision of this Local Controller
+ // Storage Devices under supervision of this Local Controller
FStorageListSize: Integer;
FStorageNameList: TStringList;
FStoragePointerList: TDSSPointerList;
FStorageWeights: pDoubleArray;
-// dead band control parameters
+ // dead band control parameters
FkWLimit,
FkWBand,
HalfkWBand,
FkvarLimit,
TotalWeight: Double;
-
- // YearlyShape :String; // ='fixed' means no variation on all the time
- // YearlyShapeObj :TLoadShapeObj; // Shape for this Storage element
-// DailyForecastShape: String; // Daily (24 HR) Storage element shape
-// DailyForecasstShapeObj: TLoadShapeObj; // Daily Storage element Shape for this load
- // DutyShape :String; // Duty cycle load shape for changes typically less than one hour
- // DutyShapeObj :TLoadShapeObj; // Shape for this Storage element
-
-// LoadShapeMult: Complex;
-
-
PUBLIC
constructor Create(ParClass: TDSSClass; const ESPVLControlName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
procedure RecalcElementData; OVERRIDE;
- procedure CalcYPrim; OVERRIDE; // Always Zero for a ESPVLControl
procedure Sample; OVERRIDE; // Sample control quantities and set action times in Control Queue
procedure DoPendingAction(const Code, ProxyHdl: Integer); OVERRIDE; // Do the action that is pending from last sample
procedure Reset; OVERRIDE; // Reset to initial defined state
- procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present value of terminal Curr
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
function MakeLocalControlList: Boolean;
end;
-{--------------------------------------------------------------------------}
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Circuit,
@@ -138,260 +130,154 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TESPVLControlObj;
+ TProp = TESPVLControlProp;
const
+ NumPropsThisClass = Ord(High(TProp));
+var
+ TypeEnum: TDSSEnum;
+ PropInfo: Pointer = NIL;
- NumPropsThisClass = 12;
-
-
-{--------------------------------------------------------------------------}
-constructor TESPVLControl.Create(dssContext: TDSSContext); // Creates superstructure for all ESPVLControl objects
+constructor TESPVLControl.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
-
- Class_name := 'ESPVLControl';
- DSSClassType := DSSClassType + ESPVL_CONTROL;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ TypeEnum := TDSSEnum.Create('ESPVLControl: Type', True, 1, 1,
+ ['SystemController', 'LocalController'], [1, 2]);
+ end;
+ inherited Create(dssContext, ESPVL_CONTROL, 'ESPVLControl');
end;
-{--------------------------------------------------------------------------}
destructor TESPVLControl.Destroy;
-
begin
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TESPVLControl.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
-
- PropertyName[1] := 'Element';
- PropertyName[2] := 'Terminal';
- PropertyName[3] := 'Type';
- PropertyName[4] := 'kWBand';
- PropertyName[5] := 'kvarlimit';
- PropertyName[6] := 'LocalControlList';
- PropertyName[7] := 'LocalControlWeights';
- PropertyName[8] := 'PVSystemList';
- PropertyName[9] := 'PVSystemWeights';
- PropertyName[10] := 'StorageList';
- PropertyName[11] := 'StorageWeights';
- PropertyName[12] := 'Forecast';
-
- PropertyHelp[1] := 'Full object name of the circuit element, typically a line or transformer, ' +
- 'which the control is monitoring. There is no default; must be specified.';
- PropertyHelp[2] := 'Number of the terminal of the circuit element to which the ESPVLControl control is connected. ' +
- '1 or 2, typically. Default is 1. Make sure you have the direction on the power matching the sign of kWLimit.';
- PropertyHelp[3] := 'Type of controller. 1= System Controller; 2= Local controller. ';
- PropertyHelp[4] := 'Bandwidth (kW) of the dead band around the target limit.' +
- 'No dispatch changes are attempted if the power in the monitored terminal stays within this band.';
- PropertyHelp[5] := 'Max kvar to be delivered through the element. Uses same dead band as kW.';
- PropertyHelp[6] := 'Array list of ESPVLControl local controller objects to be dispatched by System Controller. ' +
- 'If not specified, all ESPVLControl devices with type=local in the circuit not attached to another ' +
- 'controller are assumed to be part of this controller''s fleet.';
- PropertyHelp[7] := 'Array of proportional weights corresponding to each ESPVLControl local controller in the LocalControlList.';
- ;
- PropertyHelp[8] := 'Array list of PVSystem objects to be dispatched by a Local Controller. ';
- PropertyHelp[9] := 'Array of proportional weights corresponding to each PVSystem in the PVSystemList.';
- ;
- PropertyHelp[10] := 'Array list of Storage objects to be dispatched by Local Controller. ';
- PropertyHelp[11] := 'Array of proportional weights corresponding to each Storage object in the StorageControlList.';
- PropertyHelp[12] := 'Loadshape object containing daily forecast.';
- ;
-
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
-end;
-
-{--------------------------------------------------------------------------}
-function TESPVLControl.NewObject(const ObjName: String): Integer;
-begin
- // Make a new ESPVLControl and add it to ESPVLControl class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TESPVLControlObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
-end;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
-{--------------------------------------------------------------------------}
-function TESPVLControl.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
- i: Integer;
+ // object references
+ PropertyType[ord(TProp.element)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.element)] := ptruint(@obj.FMonitoredElement);
+ PropertyOffset2[ord(TProp.element)] := 0;
+ PropertyWriteFunction[ord(TProp.element)] := @SetMonitoredElement;
+ PropertyFlags[ord(TProp.element)] := [TPropertyFlag.WriteByFunction];//[TPropertyFlag.CheckForVar]; // not required for general cktelements
-begin
+ // enum properties
+ PropertyType[ord(TProp.typ)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.typ)] := ptruint(@obj.Ftype);
+ PropertyOffset2[ord(TProp.typ)] := PtrInt(TypeEnum);
- // continue parsing WITH contents of Parser
- DSS.ActiveESPVLControlObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveESPVLControlObj;
+ // string lists
+ PropertyType[ord(TProp.PVSystemList)] := TPropertyType.StringListProperty;
+ PropertyOffset[ord(TProp.PVSystemList)] := ptruint(@obj.FPVSystemNameList);
- Result := 0;
+ PropertyType[ord(TProp.LocalControlList)] := TPropertyType.StringListProperty;
+ PropertyOffset[ord(TProp.LocalControlList)] := ptruint(@obj.FLocalControlNameList);
- with DSS.ActiveESPVLControlObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 364);
- 1:
- ElementName := lowercase(param);
- 2:
- ElementTerminal := Parser.IntValue;
- 3:
- case Lowercase(Param)[1] of
- 's':
- Ftype := 1; {for System Controller}
- 'l':
- Ftype := 2; {for Local Controller}
- end;
-
-
- 4:
- FkWBand := Parser.DblValue;
- 5:
- FkvarLimit := Parser.DblValue;
- 6:
- InterpretTStringListArray(Param, FLocalControlNameList);
- 7:
- begin
- FLocalControlListSize := FLocalControlNameList.count;
- if FLocalControlListSize > 0 then
- begin
- Reallocmem(FLocalControlWeights, Sizeof(FLocalControlWeights^[1]) * FLocalControlListSize);
- FLocalControlListSize := InterpretDblArray(Param, FLocalControlListSize, FLocalControlWeights);
- end;
- end;
- 8:
- InterpretTStringListArray(Param, FPVSystemNameList);
- 9:
- begin
- FPVSystemListSize := FPVSystemNameList.count;
- if FPVSystemListSize > 0 then
- begin
- Reallocmem(FPVSystemWeights, Sizeof(FPVSystemWeights^[1]) * FPVSystemListSize);
- FPVSystemListSize := InterpretDblArray(Param, FPVSystemListSize, FPVSystemWeights);
- end;
- end;
- 10:
- InterpretTStringListArray(Param, FStorageNameList);
- 11:
- begin
- FStorageListSize := FStorageNameList.count;
- if FStorageListSize > 0 then
- begin
- Reallocmem(FStorageWeights, Sizeof(FStorageWeights^[1]) * FStorageListSize);
- FStorageListSize := InterpretDblArray(Param, FStorageListSize, FStorageWeights);
- end;
- end;
+ PropertyType[ord(TProp.StorageList)] := TPropertyType.StringListProperty;
+ PropertyOffset[ord(TProp.StorageList)] := ptruint(@obj.FStorageNameList);
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveESPVLControlObj, ParamPointer - NumPropsthisClass)
- end;
+ // double arrays
+ PropertyType[ord(TProp.LocalControlWeights)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.LocalControlWeights)] := ptruint(@obj.FLocalControlWeights);
+ PropertyOffset2[ord(TProp.LocalControlWeights)] := ptruint(@obj.FLocalControlListSize); // FLocalControlNameList.count
- // Side Effects
- case ParamPointer of
- 6:
- begin // levelize the list
- FLocalControlPointerList.Clear; // clear this for resetting on first sample
- FLocalControlListSize := FLocalControlNameList.count;
- Reallocmem(FLocalControlWeights, Sizeof(FLocalControlWeights^[1]) * FLocalControlListSize);
- for i := 1 to FLocalControlListSize do
- FLocalControlWeights^[i] := 1.0;
- end;
- else
+ PropertyType[ord(TProp.PVSystemWeights)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.PVSystemWeights)] := ptruint(@obj.FPVSystemWeights);
+ PropertyOffset2[ord(TProp.PVSystemWeights)] := ptruint(@obj.FPVSystemListSize);
- end;
+ PropertyType[ord(TProp.StorageWeights)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.StorageWeights)] := ptruint(@obj.FStorageWeights);
+ PropertyOffset2[ord(TProp.StorageWeights)] := ptruint(@obj.FStorageListSize);
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
+ // integer properties
+ PropertyType[ord(TProp.Terminal)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Terminal)] := ptruint(@obj.ElementTerminal);
- RecalcElementData;
- end;
+ // double properties (default type)
+ PropertyOffset[ord(TProp.kWBand)] := ptruint(@obj.FkWBand);
+ PropertyOffset[ord(TProp.kvarlimit)] := ptruint(@obj.FkvarLimit);
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
end;
+function TESPVLControl.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
+begin
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
+end;
-{--------------------------------------------------------------------------}
-function TESPVLControl.MakeLike(const ESPVLControlName: String): Integer;
+procedure TESPVLControlObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- OtherESPVLControl: TESPVLControlObj;
i: Integer;
begin
- Result := 0;
- {See if we can find this ESPVLControl name in the present collection}
- OtherESPVLControl := Find(ESPVLControlName);
- if OtherESPVLControl <> NIL then
- with DSS.ActiveESPVLControlObj do
+ case Idx of
+ 8:
begin
+ FPVSystemListSize := FPVSystemNameList.count;
+ if FPVSystemWeights <> NIL then
+ Reallocmem(FPVSystemWeights, Sizeof(Double) * FPVSystemListSize);
+ end;
+ 10:
+ begin
+ FStorageListSize := FStorageNameList.count;
+ if FStorageWeights <> NIL then
+ Reallocmem(FStorageWeights, Sizeof(Double) * FStorageListSize);
+ end;
+ 6:
+ begin // levelize the list
+ FLocalControlPointerList.Clear; // clear this for resetting on first sample
+ FLocalControlListSize := FLocalControlNameList.count;
+ Reallocmem(FLocalControlWeights, Sizeof(FLocalControlWeights^[1]) * FLocalControlListSize);
+ for i := 1 to FLocalControlListSize do
+ FLocalControlWeights^[i] := 1.0;
+ end;
+ end;
- NPhases := OtherESPVLControl.Fnphases;
- NConds := OtherESPVLControl.Fnconds; // Force Reallocation of terminal stuff
-
- ElementName := OtherESPVLControl.ElementName;
- ControlledElement := OtherESPVLControl.ControlledElement; // Pointer to target circuit element
- MonitoredElement := OtherESPVLControl.MonitoredElement; // Pointer to target circuit element
-
- ElementTerminal := OtherESPVLControl.ElementTerminal;
-
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherESPVLControl.PropertyValue[i];
-
- end
- else
- DoSimpleMsg('Error in ESPVLControl MakeLike: "' + ESPVLControlName + '" Not Found.', 370);
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
+procedure TESPVLControlObj.MakeLike(OtherPtr: Pointer);
+var
+ Other: TObj;
+begin
+ inherited MakeLike(OtherPtr);
+
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
-{==========================================================================}
-{ TESPVLControlObj }
-{==========================================================================}
+ // ControlledElement := Other.ControlledElement; // Pointer to target circuit element
+ MonitoredElement := Other.MonitoredElement; // Pointer to target circuit element
+ ElementTerminal := Other.ElementTerminal;
+end;
-{--------------------------------------------------------------------------}
constructor TESPVLControlObj.Create(ParClass: TDSSClass; const ESPVLControlName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(ESPVLControlName);
+ Name := AnsiLowerCase(ESPVLControlName);
DSSObjType := ParClass.DSSClassType;
- NPhases := 3; // Directly set conds and phases
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 1; // this forces allocation of terminals and conductors
// in base class
-
- ElementName := '';
ControlledElement := NIL; // not used in this control
ElementTerminal := 1;
MonitoredElement := NIL;
@@ -415,126 +301,61 @@ constructor TESPVLControlObj.Create(ParClass: TDSSClass; const ESPVLControlName:
FkWBand := 100.0;
TotalWeight := 1.0;
HalfkWBand := FkWBand / 2.0;
- InitPropertyValues(0);
FkvarLimit := FkWLimit / 2.0;
-
// RecalcElementData;
-
end;
destructor TESPVLControlObj.Destroy;
begin
- ElementName := '';
inherited Destroy;
end;
-{--------------------------------------------------------------------------}
procedure TESPVLControlObj.RecalcElementData;
-
-var
- DevIndex: Integer;
-
begin
+ {Check for existence of monitored element}
+ if MonitoredElement = NIL then
+ begin
+ DoSimpleMsg('Monitored Element in "%s" is not set', [FullName], 372);
+ Exit;
+ end;
-
-{Check for existence of monitored element}
-
- Devindex := GetCktElementIndex(ElementName); // Global function
- if DevIndex > 0 then
+ if ElementTerminal > MonitoredElement.Nterms then
begin
- MonitoredElement := ActiveCircuit.CktElements.Get(DevIndex);
- if ElementTerminal > MonitoredElement.Nterms then
- begin
- DoErrorMsg('ESPVLControl: "' + Name + '"',
- 'Terminal no. "' + '" does not exist.',
- 'Re-specify terminal no.', 371);
- end
- else
- begin
- // Sets name of i-th terminal's connected bus in ESPVLControl's buslist
- Setbus(1, MonitoredElement.GetBus(ElementTerminal));
- end;
+ DoErrorMsg(Format(_('ESPVLControl: "%s"'), [Name]),
+ Format(_('Terminal no. "%d" does not exist.'), [ElementTerminal]),
+ _('Re-specify terminal no.'), 371);
end
else
- DoSimpleMsg('Monitored Element in ESPVLControl.' + Name + ' does not exist:"' + ElementName + '"', 372);
-
-
+ begin
+ // Sets name of i-th terminal's connected bus in ESPVLControl's buslist
+ Setbus(1, MonitoredElement.GetBus(ElementTerminal));
+ end;
end;
-procedure TESPVLControlObj.MakePosSequence;
+procedure TESPVLControlObj.MakePosSequence();
begin
if MonitoredElement <> NIL then
begin
- Nphases := ControlledElement.NPhases;
+ FNphases := ControlledElement.NPhases;
Nconds := FNphases;
Setbus(1, MonitoredElement.GetBus(ElementTerminal));
end;
inherited;
end;
-{--------------------------------------------------------------------------}
-procedure TESPVLControlObj.CalcYPrim;
-begin
- // leave YPrims as nil and they will be ignored
- // Yprim is zeroed when created. Leave it as is.
- // IF YPrim=nil THEN YPrim := TcMatrix.CreateMatrix(Yorder);
-end;
-
-
-{--------------------------------------------------------------------------}
-procedure TESPVLControlObj.GetCurrents(Curr: pComplexArray);
-var
- i: Integer;
-begin
-
- for i := 1 to Fnconds do
- Curr^[i] := CZERO;
-
-end;
-
-{--------------------------------------------------------------------------}
-procedure TESPVLControlObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
- if Complete then
- begin
- FSWriteln(F);
- end;
-
-end;
-
-
-{--------------------------------------------------------------------------}
procedure TESPVLControlObj.DoPendingAction;
begin
-
- {Do Nothing}
+ {Do Nothing}
end;
-{--------------------------------------------------------------------------}
procedure TESPVLControlObj.Sample;
-
var
i: Integer;
PDiff: Double;
- // QDiff: Double;
S: Complex;
Gen: TGeneratorObj;
- // GenkWChanged, Genkvarchanged: Boolean;
GenkW: Double;
- // Genkvar: Double;
begin
// If list is not define, go make one from all generators in circuit
if FLocalControlPointerList.Count = 0 then
@@ -542,19 +363,10 @@ procedure TESPVLControlObj.Sample;
if FLocalControlListSize > 0 then
begin
-
- //----MonitoredElement.ActiveTerminalIdx := ElementTerminal;
S := MonitoredElement.Power[ElementTerminal]; // Power in active terminal
PDiff := S.re * 0.001 - FkWLimit;
- // QDiff := S.im * 0.001 - FkvarLimit;
-
- // Redispatch the vars.
-
- // GenkWChanged := FALSE;
- // GenkvarChanged := FALSE;
-
if Abs(PDiff) > HalfkWBand then
begin // Redispatch Generators
// PDiff is kW needed to get back into band
@@ -566,91 +378,41 @@ procedure TESPVLControlObj.Sample;
if GenkW <> Gen.kWBase then
begin
Gen.kWBase := GenkW;
- // GenkWChanged := TRUE;
end;
end;
end;
- (*
- If Abs(QDiff) > HalfkWBand Then Begin // Redispatch Generators
- // QDiff is kvar needed to get back into band
- For i := 1 to FLocalControlListSize Do Begin
- Gen := FLocalControlPointerList.Get(i);
- // compute new dispatch value for this generator ...
- Genkvar := Max(0.0, (Gen.kvarBase + QDiff *(FWeights^[i]/TotalWeight)));
- If Genkvar <> Gen.kvarBase Then Begin
- Gen.kvarBase := Genkvar;
- Genkvarchanged := TRUE;
- End;
- End;
- End;
-
- If GenkWChanged or Genkvarchanged Then // Only push onto controlqueue if there has been a change
- With ActiveCircuit, ActiveCircuit.Solution Do Begin
- LoadsNeedUpdating := TRUE; // Force recalc of power parms
- // Push present time onto control queue to force re solve at new dispatch value
- ControlQueue.Push(DynaVars.intHour, DynaVars.t, 0, 0, Self);
- End;
- *)
-
{Else just continue}
end;
-
-
-end;
-
-
-procedure TESPVLControlObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := ''; //'element';
- PropertyValue[2] := '1'; //'terminal';
- PropertyValue[3] := '8000';
- PropertyValue[4] := '100';
- PropertyValue[5] := '0';
- PropertyValue[6] := '';
- PropertyValue[7] := '';
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
end;
function TESPVLControlObj.MakeLocalControlList: Boolean;
-
var
pESPVLControl: TESPVLControlObj;
i: Integer;
-
begin
-
Result := FALSE;
if Ftype = 1 then
begin // only for System controller
-
-
if FLocalControlListSize > 0 then
begin // Name list is defined - Use it
-
for i := 1 to FLocalControlListSize do
begin
- pESPVLControl := DSS.ESPVLControlClass.Find(FLocalControlNameList.Strings[i - 1]);
+ pESPVLControl := ParentClass.Find(FLocalControlNameList.Strings[i - 1]);
if Assigned(pESPVLControl) and pESPVLControl.Enabled then
- FLocalControlPointerList.New := pESPVLControl;
+ FLocalControlPointerList.Add(pESPVLControl);
end;
-
end
else
begin
- {Search through the entire circuit for enabled generators and add them to the list}
-
- for i := 1 to DSS.ESPVLControlClass.ElementCount do
+ {Search through the entire circuit for enabled generators and add them to the list}
+ for i := 1 to ParentClass.ElementCount do
begin
- pESPVLControl := DSS.ESPVLControlClass.ElementList.Get(i);
+ pESPVLControl := ParentClass.ElementList.Get(i);
if pESPVLControl.Enabled then
- FLocalControlPointerList.New := pESPVLControl;
+ FLocalControlPointerList.Add(pESPVLControl);
end;
- {Allocate uniform weights}
+ {Allocate uniform weights}
FLocalControlListSize := FLocalControlPointerList.Count;
Reallocmem(FLocalControlWeights, Sizeof(FLocalControlWeights^[1]) * FLocalControlListSize);
for i := 1 to FLocalControlListSize do
@@ -658,7 +420,7 @@ function TESPVLControlObj.MakeLocalControlList: Boolean;
end;
- // Add up total weights ??????
+ // Add up total weights ??????
TotalWeight := 0.0;
for i := 1 to FLocalControlListSize do
TotalWeight := TotalWeight + FLocalControlWeights^[i];
@@ -668,15 +430,10 @@ function TESPVLControlObj.MakeLocalControlList: Boolean;
end;
end;
-
procedure TESPVLControlObj.Reset;
begin
// inherited;
-
end;
-
-initialization
-
-
+finalization TypeEnum.Free;
end.
diff --git a/src/Controls/ExpControl.pas b/src/Controls/ExpControl.pas
index b16da5113..8d6b0fc89 100644
--- a/src/Controls/ExpControl.pas
+++ b/src/Controls/ExpControl.pas
@@ -13,33 +13,50 @@
INTERFACE
uses
- {$IFDEF FPC}gqueue{$ELSE}System.Generics.Collections{$ENDIF}, Command,
- ControlClass, ControlElem, CktElement, DSSClass, PVSystem, Arraydef, ucomplex,
+ gqueue, Command,
+ ControlClass, ControlElem, CktElement, DSSClass, PVSystem2, Arraydef, UComplex, DSSUcomplex,
utilities, Dynamics, DSSPointerList, Classes, StrUtils;
type
+{$SCOPEDENUMS ON}
+ TExpControlProp = (
+ INVALID = 0,
+ PVSystemList = 1,
+ Vreg = 2,
+ Slope = 3,
+ VregTau = 4,
+ Qbias = 5,
+ VregMin = 6,
+ VregMax = 7,
+ QmaxLead = 8,
+ QmaxLag = 9,
+ EventLog = 10,
+ DeltaQ_factor = 11,
+ PreferQ = 12,
+ Tresponse = 13,
+ DERList = 14
+ );
+{$SCOPEDENUMS OFF}
+
TExpControl = class(TControlClass)
protected
- PROCEDURE DefineProperties;
- FUNCTION MakeLike(const ExpControlName:String):Integer;Override;
+ procedure DefineProperties; override;
public
constructor Create(dssContext: TDSSContext);
destructor Destroy; override;
- FUNCTION Edit:Integer; override; // uses global parser
- FUNCTION NewObject(const ObjName:String):Integer; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; override;
PROCEDURE UpdateAll;
end;
TExpControlObj = class(TControlElem)
private
ControlActionHandle: Integer;
- ControlledElement: Array of TPVSystemObj; // list of pointers to controlled PVSystem elements
+ ControlledElement: Array of TPVSystem2Obj; // list of pointers to controlled PVSystem elements
MonitoredElement : TDSSCktElement; // First PVSystem element for now
// PVSystemList information
FListSize:Integer;
- FPVSystemNameList:TStringList;
FPVSystemPointerList: TDSSPointerList;
// working storage for each PV system under management
@@ -57,333 +74,264 @@ TExpControlObj = class(TControlElem)
// user-supplied parameters (also PVSystemList and EventLog)
FVregInit: Double;
- FSlope: Double;
- FVregTau: Double;
FQbias: Double;
- FVregMin: Double;
- FVregMax: Double;
- FQmaxLead: Double;
- FQmaxLag: Double;
FdeltaQ_factor: Double;
FVoltageChangeTolerance: Double; // no user adjustment
FVarChangeTolerance: Double; // no user adjustment
- FPreferQ: Boolean;
+ FPreferQ: LongBool;
- FTresponse, FOpenTau: Double;
+ FOpenTau: Double;
PROCEDURE Set_PendingChange(Value: Integer;DevIndex: Integer);
FUNCTION Get_PendingChange(DevIndex: Integer):Integer;
- FUNCTION ReturnElementsList:String;
PROCEDURE UpdateExpControl(i:integer);
public
+ FPVSystemNameList, DERNameList: TStringList;
+ QmaxLead: Double;
+ QmaxLag: Double;
+ VregMin: Double;
+ VregMax: Double;
+ VregTau: Double;
+ QVSlope: Double; // originally FSlope
+ Tresponse: Double;
constructor Create(ParClass:TDSSClass; const ExpControlName:String);
destructor Destroy; override;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
- PROCEDURE Set_Enabled(Value:Boolean);Override;
- PROCEDURE MakePosSequence; Override; // Make a positive Sequence Model
- PROCEDURE RecalcElementData; Override;
- PROCEDURE CalcYPrim; Override; // Always Zero for an ExpControl
+ // PROCEDURE Set_Enabled(Value: Boolean);Override;
+ PROCEDURE MakePosSequence(); Override; // Make a positive Sequence Model
+ PROCEDURE RecalcElementData; Override;
// Sample control quantities and set action times in Control Queue
- PROCEDURE Sample; Override;
+ PROCEDURE Sample; Override;
// Do the action that is pending from last sample
- PROCEDURE DoPendingAction(Const Code, ProxyHdl:Integer); Override;
-
- PROCEDURE Reset; Override; // Reset to initial defined state
-
- PROCEDURE GetCurrents(Curr: pComplexArray); Override;
+ PROCEDURE DoPendingAction(Const Code, ProxyHdl: Integer); Override;
- PROCEDURE InitPropertyValues(ArrayOffset:Integer);Override;
- PROCEDURE DumpProperties(F: TFileStream; Complete:Boolean);Override;
+ PROCEDURE Reset; Override; // Reset to initial defined state
- FUNCTION MakePVSystemList:Boolean;
- FUNCTION GetPropertyValue(Index:Integer):String;Override;
+ FUNCTION MakePVSystemList:Boolean;
- Property PendingChange[DevIndex: Integer]:Integer Read Get_PendingChange Write Set_PendingChange;
+ Property PendingChange[DevIndex: Integer]: Integer Read Get_PendingChange Write Set_PendingChange;
end;
-{--------------------------------------------------------------------------}
IMPLEMENTATION
USES
- ParserDel, Sysutils, DSSClassDefs, DSSGlobals, Circuit, uCmatrix, MathUtil, Math,
+ Sysutils, DSSClassDefs, DSSGlobals, Circuit, uCmatrix, MathUtil, Math,
DSSHelper,
DSSObjectHelper,
TypInfo;
-CONST
- NumPropsThisClass = 13;
-
+type
+ TObj = TExpControlObj;
+ TProp = TExpControlProp;
+ TPVSystemObj = TPVSystem2Obj;
+const
+ NumPropsThisClass = Ord(High(TProp));
NONE = 0;
CHANGEVARLEVEL = 1;
+var
+ PropInfo: Pointer = NIL;
-{--------------------------------------------------------------------------}
-constructor TExpControl.Create(dssContext: TDSSContext); // Creates superstructure for all ExpControl objects
-Begin
- Inherited Create(dssContext);
- Class_name := 'ExpControl';
- DSSClassType := DSSClassType + EXP_CONTROL;
- DefineProperties;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
-End;
+constructor TExpControl.Create(dssContext: TDSSContext);
+begin
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
-{--------------------------------------------------------------------------}
-destructor TExpControl.Destroy;
+ inherited Create(dssContext, EXP_CONTROL, 'ExpControl');
+end;
+destructor TExpControl.Destroy;
Begin
-
- Inherited Destroy;
+ Inherited Destroy;
End;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PROCEDURE TExpControl.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
Begin
-
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
- PropertyName[1] := 'PVSystemList';
- PropertyName[2] := 'Vreg';
- PropertyName[3] := 'Slope';
- PropertyName[4] := 'VregTau';
- PropertyName[5] := 'Qbias';
- PropertyName[6] := 'VregMin';
- PropertyName[7] := 'VregMax';
- PropertyName[8] := 'QmaxLead';
- PropertyName[9] := 'QmaxLag';
- PropertyName[10] := 'EventLog';
- PropertyName[11] := 'DeltaQ_factor';
- PropertyName[12] := 'PreferQ';
- PropertyName[13] := 'Tresponse';
-
- PropertyHelp[1] := 'Array list of PVSystems to be controlled.'+CRLF+CRLF+
- 'If not specified, all PVSystems in the circuit are assumed to be controlled by this ExpControl.';
- PropertyHelp[2] := 'Per-unit voltage at which reactive power is zero; defaults to 1.0.'+CRLF+CRLF+
- 'This may dynamically self-adjust when VregTau > 0, limited by VregMin and VregMax.'+
- 'If imput as 0, Vreg will be initialized from a snapshot solution with no inverter Q.'+
- 'The equilibrium point of reactive power is also affected by Qbias';
- PropertyHelp[3] := 'Per-unit reactive power injection / per-unit voltage deviation from Vreg; defaults to 50.'+CRLF+CRLF+
- 'Unlike InvControl, base reactive power is constant at the inverter kva rating.';
- PropertyHelp[4] := 'Time constant for adaptive Vreg. Defaults to 1200 seconds.'+CRLF+CRLF+
- 'When the control injects or absorbs reactive power due to a voltage deviation from the Q=0 crossing of the volt-var curve, '+
- 'the Q=0 crossing will move toward the actual terminal voltage with this time constant. '+
- 'Over time, the effect is to gradually bring inverter reactive power to zero as the grid voltage changes due to non-solar effects. '+
- 'If zero, then Vreg stays fixed. ' +
- 'IEEE1547-2018 requires adjustability from 300s to 5000s';
- PropertyHelp[5] := 'Equilibrium per-unit reactive power when V=Vreg; defaults to 0.'+CRLF+CRLF+
- 'Enter > 0 for lagging (capacitive) bias, < 0 for leading (inductive) bias.';
- PropertyHelp[6] := 'Lower limit on adaptive Vreg; defaults to 0.95 per-unit';
- PropertyHelp[7] := 'Upper limit on adaptive Vreg; defaults to 1.05 per-unit';
- PropertyHelp[8] := 'Limit on leading (inductive) reactive power injection, in per-unit of base kva; defaults to 0.44.'+
- 'For Category A inverters per P1547/D7, set this value to 0.25.'+CRLF+CRLF+
- 'Regardless of QmaxLead, the reactive power injection is still '+
- 'limited by dynamic headroom when actual real power output exceeds 0%';
- PropertyHelp[9] := 'Limit on lagging (capacitive) reactive power injection, in per-unit of base kva; defaults to 0.44.'+CRLF+CRLF+
- 'For Category A inverters per P1547/D7, set this value to 0.25.'+
- 'Regardless of QmaxLag, the reactive power injection is still '+
- 'limited by dynamic headroom when actual real power output exceeds 0%';
- PropertyHelp[10] := '{Yes/True | No/False*} Default is No for ExpControl. Log control actions to Eventlog.';
- PropertyHelp[11] := 'Convergence parameter; Defaults to 0.7. '+CRLF+CRLF+
- 'Sets the maximum change (in per unit) from the prior var output level to the desired var output level during each control iteration. '+
- 'If numerical instability is noticed in solutions such as var sign changing from one control iteration to the next and voltages oscillating between two values with some separation, '+
- 'this is an indication of numerical instability (use the EventLog to diagnose). '+
- 'If the maximum control iterations are exceeded, and no numerical instability is seen in the EventLog of via monitors, then try increasing the value of this parameter to reduce the number '+
- 'of control iterations needed to achieve the control criteria, and move to the power flow solution.';
- PropertyHelp[12] := '{Yes/True | No/False*} Default is No for ExpControl.' + CRLF + CRLF +
- 'Curtails real power output as needed to meet the reactive power requirement. ' +
- 'IEEE1547-2018 requires Yes, but the default is No for backward compatibility of OpenDSS models.';
- PropertyHelp[13] := 'Open-loop response time for changes in Q.' + CRLF + CRLF +
- 'The value of Q reaches 90% of the target change within Tresponse, which ' +
- 'corresponds to a low-pass filter having tau = Tresponse / 2.3026. ' +
- 'The behavior is similar to LPFTAU in InvControl, but here the response time is ' +
- 'input instead of the time constant. ' +
- 'IEEE1547-2018 default is 10s for Catagory A and 5s for Category B, ' +
- 'adjustable from 1s to 90s for both categories. However, the default is 0 for ' +
- 'backward compatibility of OpenDSS models.';
-
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-End;
-
-{--------------------------------------------------------------------------}
-FUNCTION TExpControl.NewObject(const ObjName:String):Integer;
-Begin
- // Make a new ExpControl and add it to ExpControl class list
- WITH ActiveCircuit Do
- Begin
- ActiveCktElement := TExpControlObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- End;
+ Numproperties := NumPropsThisClass;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // string lists
+ PropertyType[ord(TProp.PVSystemList)] := TPropertyType.StringListProperty;
+ PropertyOffset[ord(TProp.PVSystemList)] := ptruint(@obj.FPVSystemNameList);
+
+ PropertyType[ord(TProp.DERList)] := TPropertyType.StringListProperty;
+ PropertyOffset[ord(TProp.DERList)] := ptruint(@obj.DERNameList);
+
+ // boolean properties
+ PropertyType[ord(TProp.EventLog)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.PreferQ)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.EventLog)] := ptruint(@obj.ShowEventLog);
+ PropertyOffset[ord(TProp.PreferQ)] := ptruint(@obj.FPreferQ);
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.Qbias)] := ptruint(@obj.FQbias);
+ PropertyOffset[ord(TProp.DeltaQ_factor)] := ptruint(@obj.FdeltaQ_factor);
+
+ // advanced doubles
+ PropertyOffset[ord(TProp.Vreg)] := ptruint(@obj.FVregInit);
+ PropertyFlags[ord(TProp.Vreg)] := [TPropertyFlag.IgnoreInvalid, TPropertyFlag.NonNegative];
+
+ PropertyOffset[ord(TProp.Slope)] := ptruint(@obj.QVSlope);
+ PropertyFlags[ord(TProp.Slope)] := [TPropertyFlag.IgnoreInvalid, TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ PropertyOffset[ord(TProp.VregTau)] := ptruint(@obj.VregTau);
+ PropertyFlags[ord(TProp.VregTau)] := [TPropertyFlag.IgnoreInvalid, TPropertyFlag.NonNegative];
+
+ PropertyOffset[ord(TProp.VregMin)] := ptruint(@obj.VregMin);
+ PropertyFlags[ord(TProp.VregMin)] := [TPropertyFlag.IgnoreInvalid, TPropertyFlag.NonNegative];
+
+ PropertyOffset[ord(TProp.VregMax)] := ptruint(@obj.VregMax);
+ PropertyFlags[ord(TProp.VregMax)] := [TPropertyFlag.IgnoreInvalid, TPropertyFlag.NonNegative];
+
+ PropertyOffset[ord(TProp.QmaxLead)] := ptruint(@obj.QmaxLead);
+ PropertyFlags[ord(TProp.QmaxLead)] := [TPropertyFlag.IgnoreInvalid, TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ PropertyOffset[ord(TProp.QmaxLag)] := ptruint(@obj.QmaxLag);
+ PropertyFlags[ord(TProp.QmaxLag)] := [TPropertyFlag.IgnoreInvalid, TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ PropertyOffset[ord(TProp.Tresponse)] := ptruint(@obj.Tresponse);
+ PropertyFlags[ord(TProp.Tresponse)] := [TPropertyFlag.IgnoreInvalid, TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
End;
-{--------------------------------------------------------------------------}
-FUNCTION TExpControl.Edit:Integer;
-VAR
- ParamPointer:Integer;
- ParamName:String;
- Param:String;
-
+FUNCTION TExpControl.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
+begin
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
+end;
-Begin
- DSS.ActiveExpControlObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveExpControlObj;
- Result := 0;
- WITH DSS.ActiveExpControlObj Do Begin
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- WHILE Length(Param)>0 Do Begin
- IF Length(ParamName) = 0 THEN Inc(ParamPointer)
- ELSE ParamPointer := CommandList.GetCommand(ParamName);
-
- If (ParamPointer>0) and (ParamPointer<=NumProperties)
- THEN PropertyValue[ParamPointer]:= Param;
-
- CASE ParamPointer OF
- 0: DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name +'.'+ Name + '"', 364);
- 1: begin
- InterpretTStringListArray(Param, FPVSystemNameList);
- FPVSystemPointerList.Clear; // clear this for resetting on first sample
- FListSize := FPVSystemNameList.count;
- end;
- 2: If Parser.DblValue >= 0 then FVregInit := Parser.DblValue;
- 3: If Parser.DblValue > 0 then FSlope := Parser.DblValue;
- 4: If Parser.DblValue >= 0 then FVregTau := Parser.DblValue; // zero means fixed Vreg
- 5: FQbias := Parser.DblValue;
- 6: If Parser.DblValue > 0 then FVregMin := Parser.DblValue;
- 7: If Parser.DblValue > 0 then FVregMax := Parser.DblValue;
- 8: If Parser.DblValue >= 0 then FQmaxLead := Parser.DblValue;
- 9: If Parser.DblValue >= 0 then FQmaxLag := Parser.DblValue;
- 10: ShowEventLog := InterpretYesNo(param);
- 11: FdeltaQ_factor := Parser.DblValue;
- 12: FPreferQ := InterpretYesNo(param);
- 13: if Parser.DblValue >= 0 then FTresponse := Parser.DblValue;
- ELSE
- // Inherited parameters
- ClassEdit( DSS.ActiveExpControlObj, ParamPointer - NumPropsthisClass)
- End;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- End;
- RecalcElementData;
- End;
-End;
+procedure TExpControlObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
+var
+ i: Integer;
+begin
+ case Idx of
+ ord(TProp.PVSystemList):
+ begin
+ FPVSystemPointerList.Clear; // clear this for resetting on first sample
+ FListSize := FPVSystemNameList.count;
+ DERNameList.Clear;
+ for i := 0 to FListSize - 1 do
+ DerNameList.Add('PVSystem.' + FPVSystemNameList[i]);
+ end;
+ ord(TProp.DERList):
+ begin
+ FPVSystemPointerList.Clear; // clear this for resetting on first sample
+ FListSize := DERNameList.count;
+ FPVSystemNameList.Clear;
+ for i := 0 to FListSize - 1 do
+ FPVSystemNameList.Add(StripClassName(DERNameList[i]));
+ end;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
-FUNCTION TExpControl.MakeLike(const ExpControlName:String):Integer;
+procedure TExpControlObj.MakeLike(OtherPtr: Pointer);
VAR
- OtherExpControl:TExpControlObj;
- i, j:Integer;
+ Other: TObj;
+ i: Integer;
Begin
- Result := 0;
- {See if we can find this ExpControl name in the present collection}
- OtherExpControl := Find(ExpControlName);
- IF OtherExpControl<>Nil THEN
- WITH DSS.ActiveExpControlObj Do Begin
-
- NPhases := OtherExpControl.Fnphases;
- NConds := OtherExpControl.Fnconds; // Force Reallocation of terminal stuff
-
- for i := 1 to FPVSystemPointerList.Count do begin
- ControlledElement[i] := OtherExpControl.ControlledElement[i];
- FWithinTol[i] := OtherExpControl.FWithinTol[i];
- end;
-
- FListSize := OtherExpControl.FListSize;
- FVoltageChangeTolerance := OtherExpControl.FVoltageChangeTolerance;
- FVarChangeTolerance := OtherExpControl.FVarChangeTolerance;
- FVregInit := OtherExpControl.FVregInit;
- FSlope := OtherExpControl.FSlope;
- FVregTau := OtherExpControl.FVregTau;
- FQbias := OtherExpControl.FQbias;
- FVregMin := OtherExpControl.FVregMin;
- FVregMax := OtherExpControl.FVregMax;
- FQmaxLead := OtherExpControl.FQmaxLead;
- FQmaxLag := OtherExpControl.FQmaxLag;
- FdeltaQ_factor := OtherExpControl.FdeltaQ_factor;
- FPreferQ := OtherExpControl.FPreferQ;
- For j := 1 to ParentClass.NumProperties Do PropertyValue[j] := OtherExpControl.PropertyValue[j];
-
- End
- ELSE DoSimpleMsg('Error in ExpControl MakeLike: "' + ExpControlName + '" Not Found.', 370);
-
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds:= Other.Fnconds; // Force Reallocation of terminal stuff
+ for i := 1 to FPVSystemPointerList.Count do
+ begin
+ ControlledElement[i] := Other.ControlledElement[i];
+ FWithinTol[i]:= Other.FWithinTol[i];
+ end;
+ FListSize := Other.FListSize;
+ FVoltageChangeTolerance := Other.FVoltageChangeTolerance;
+ FVarChangeTolerance := Other.FVarChangeTolerance;
+ FVregInit := Other.FVregInit;
+ QVSlope := Other.QVSlope;
+ VregTau := Other.VregTau;
+ FQbias := Other.FQbias;
+ VregMin := Other.VregMin;
+ VregMax := Other.VregMax;
+ QmaxLead:= Other.QmaxLead;
+ QmaxLag := Other.QmaxLag;
+ FdeltaQ_factor := Other.FdeltaQ_factor;
+ FPreferQ := Other.FPreferQ;
End;
-{==========================================================================}
-{ TExpControlObj }
-{==========================================================================}
-
constructor TExpControlObj.Create(ParClass:TDSSClass; const ExpControlName:String);
-
Begin
Inherited Create(ParClass);
- Name := LowerCase(ExpControlName);
+ Name := AnsiLowerCase(ExpControlName);
DSSObjType := ParClass.DSSClassType;
-
- ElementName := '';
-
- {
- Control elements are zero current sources that attach to a terminal of a
- power-carrying device, but do not alter voltage or current flow.
- Define a default number of phases and conductors here and update in
- RecalcElementData routine if necessary. This allocates arrays for voltages
- and currents and gives more direct access to the values, if needed
- }
- NPhases := 3; // Directly set conds and phases
+
+ // Control elements are zero current sources that attach to a terminal of a
+ // power-carrying device, but do not alter voltage or current flow.
+ // Define a default number of phases and conductors here and update in
+ // RecalcElementData routine if necessary. This allocates arrays for voltages
+ // and currents and gives more direct access to the values, if needed
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 1; // this forces allocation of terminals and conductors
// This general feature should not be used for ExpControl,
// because it controls more than one PVSystem
- ShowEventLog := FALSE;
-
- ControlledElement := nil;
- FPVSystemNameList := nil;
- FPVSystemPointerList := nil;
- cBuffer := nil;
- FPriorVpu := nil;
- FPresentVpu := nil;
- FPendingChange := nil;
- FLastIterQ := nil;
- FLastStepQ := nil;
- FTargetQ := nil;
- FWithinTol := nil;
-
- FVoltageChangeTolerance :=0.0001; // per-unit
- FVarChangeTolerance :=0.0001; // per-unit
-
- FPVSystemNameList := TSTringList.Create;
+ ShowEventLog := FALSE;
+
+ ControlledElement := nil;
+ FPVSystemNameList := nil;
+ FPVSystemPointerList := nil;
+ cBuffer := nil;
+ FPriorVpu := nil;
+ FPresentVpu := nil;
+ FPendingChange := nil;
+ FLastIterQ := nil;
+ FLastStepQ := nil;
+ FTargetQ := nil;
+ FWithinTol := nil;
+
+ FVoltageChangeTolerance:=0.0001; // per-unit
+ FVarChangeTolerance:=0.0001; // per-unit
+
+ FPVSystemNameList := TStringList.Create;
+ DERNameList := TStringList.Create;
FPVSystemPointerList := TDSSPointerList.Create(20); // Default size and increment
+
// user parameters for dynamic Vreg
FVregInit := 1.0; // 0 means to find it during initialization
- FSlope := 50.0;
- FVregTau := 1200.0;
+ QVSlope := 50.0;
+ VregTau := 1200.0;
FVregs := nil;
FQbias := 0.0;
- FVregMin := 0.95;
- FVregMax := 1.05;
- FQmaxLead := 0.44;
- FQmaxLag := 0.44;
+ VregMin := 0.95;
+ VregMax := 1.05;
+ QmaxLead := 0.44;
+ QmaxLag := 0.44;
FdeltaQ_factor := 0.7; // only on control iterations, not the final solution
FPreferQ := FALSE;
- FTresponse := 0.0;
+ Tresponse := 0.0;
FOpenTau := 0.0;
//generic for control
FPendingChange := nil;
-
- InitPropertyValues(0);
End;
destructor TExpControlObj.Destroy;
Begin
- ElementName := '';
+ FreeAndNil(FPVSystemPointerList);
+ FreeAndNil(FPVSystemNameList);
+ FreeAndNil(DERNameList);
Finalize(ControlledElement);
Finalize(cBuffer);
Finalize(FPriorVpu);
@@ -402,7 +350,7 @@ destructor TExpControlObj.Destroy;
i :Integer;
maxord :Integer;
Begin
- FOpenTau := FTresponse / 2.3026;
+ FOpenTau := Tresponse / 2.3026;
IF FPVSystemPointerList.Count = 0 Then MakePVSystemList;
IF FPVSystemPointerList.Count > 0 Then begin
@@ -415,67 +363,37 @@ destructor TExpControlObj.Destroy;
for i := 1 to FPVSystemPointerList.Count do begin
// User ControlledElement[] as the pointer to the PVSystem elements
ControlledElement[i] := TPVSystemObj(FPVSystemPointerList.Get(i)); // pointer to i-th PVSystem
- Nphases := ControlledElement[i].NPhases; // TEMC TODO - what if these are different sizes (same concern exists with InvControl)
+ FNphases := ControlledElement[i].NPhases; // TEMC TODO - what if these are different sizes (same concern exists with InvControl)
Nconds := Nphases;
if (ControlledElement[i] = nil) then
- DoErrorMsg('ExpControl: "' + Self.Name + '"',
- 'Controlled Element "' + FPVSystemNameList.Strings[i-1] + '" Not Found.',
- ' PVSystem object must be defined previously.', 361);
+ DoErrorMsg(Format(_('ExpControl: "%s"'), [Self.Name]),
+ Format(_('Controlled Element "%s" not found.'), [FPVSystemNameList.Strings[i-1]]),
+ _('PVSystem object must be defined previously.'), 361);
if ControlledElement[i].Yorder > maxord then maxord := ControlledElement[i].Yorder;
ControlledElement[i].ActiveTerminalIdx := 1; // Make the 1 st terminal active
end;
if maxord > 0 then SetLength(cBuffer, SizeOF(Complex) * maxord);
End;
-procedure TExpControlObj.MakePosSequence;
+procedure TExpControlObj.MakePosSequence();
// *** This assumes the PVSystem devices have already been converted to pos seq
begin
IF FPVSystemPointerList.Count = 0 Then RecalcElementData;
// TEMC - from here to inherited was copied from InvControl
- Nphases := 3;
+ FNphases := 3;
Nconds := 3;
Setbus(1, MonitoredElement.GetBus(ElementTerminal));
IF FPVSystemPointerList.Count > 0 Then Begin
- {Setting the terminal of the ExpControl device to same as the 1st PVSystem element}
- { This sets it to a realistic value to avoid crashes later }
+ // Setting the terminal of the ExpControl device to same as the 1st PVSystem element
+ // This sets it to a realistic value to avoid crashes later
MonitoredElement := TDSSCktElement(FPVSystemPointerList.Get(1)); // Set MonitoredElement to 1st PVSystem in lise
Setbus(1, MonitoredElement.Firstbus);
- Nphases := MonitoredElement.NPhases;
+ FNphases := MonitoredElement.NPhases;
Nconds := Nphases;
End;
inherited;
end;
-PROCEDURE TExpControlObj.CalcYPrim;
-Begin
-End;
-
-PROCEDURE TExpControlObj.GetCurrents(Curr: pComplexArray);
-Var
- i:Integer;
-Begin
-// Control is a zero current source
- For i := 1 to Fnconds Do Curr^[i] := CZERO;
-End;
-
-PROCEDURE TExpControlObj.DumpProperties(F: TFileStream; Complete:Boolean);
-VAR
- i:Integer;
-Begin
- Inherited DumpProperties(F,Complete);
-
- WITH ParentClass Do
- For i := 1 to NumProperties Do
- Begin
- FSWriteln(F,'~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- End;
-
- If Complete THEN
- Begin
- FSWriteln(F);
- End;
-End;
-
PROCEDURE TExpControlObj.DoPendingAction;
VAR
i :Integer;
@@ -500,7 +418,7 @@ procedure TExpControlObj.MakePosSequence;
if (FWithinTol[i]=False) then begin
// look up Qpu from the slope crossing at Vreg, and add the bias
- Qpu := -FSlope * (FPresentVpu[i] - FVregs[i]) + FQbias;
+ Qpu := -QVSlope * (FPresentVpu[i] - FVregs[i]) + FQbias;
If ShowEventLog Then AppendtoEventLog('ExpControl.' + Self.Name+','+PVSys.Name,
Format(' Setting Qpu= %.5g at FVreg= %.5g, Vpu= %.5g', [Qpu, FVregs[i],FPresentVpu[i]]));
end;
@@ -513,8 +431,8 @@ procedure TExpControlObj.MakePosSequence;
Qmaxpu := Sqrt(1 - Sqr(PVSys.PresentkW/Qbase)); // dynamic headroom
if Qmaxpu > Qinvmaxpu then Qmaxpu := Qinvmaxpu;
if Abs(Qpu) > Qmaxpu then Qpu := QmaxPu * Sign(Qpu);
- if Qpu < -FQmaxLead then Qpu := -FQmaxLead;
- if Qpu > FQmaxLag then Qpu := FQmaxLag;
+ if Qpu < -QmaxLead then Qpu := -QmaxLead;
+ if Qpu > QmaxLag then Qpu := QmaxLag;
FTargetQ[i] := Qbase * Qpu;
if FPreferQ then begin
Plimit := Qbase * Sqrt (1 - Qpu * Qpu);
@@ -527,10 +445,12 @@ procedure TExpControlObj.MakePosSequence;
end;
// put FTargetQ through the low-pass open-loop filter
- if FOpenTau > 0.0 then begin
- dt := ActiveCircuit.Solution.Dynavars.h;
- FTargetQ[i] := FLastStepQ[i] + (FTargetQ[i] - FLastStepQ[i]) * (1 - Exp (-dt / FOpenTau)); // TODO - precalculate?
- end;
+ if FOpenTau > 0.0 then
+ if (ActiveCircuit.Solution.ControlMode <> CTRLSTATIC) then
+ begin
+ dt := ActiveCircuit.Solution.Dynavars.h;
+ FTargetQ[i] := FLastStepQ[i] + (FTargetQ[i] - FLastStepQ[i]) * (1 - Exp (-dt / FOpenTau)); // TODO - precalculate?
+ end;
// only move the non-bias component by deltaQ_factor in this control iteration
DeltaQ := FTargetQ[i] - FLastIterQ[i];
@@ -547,7 +467,6 @@ procedure TExpControlObj.MakePosSequence;
Set_PendingChange(NONE,i);
end
end;
-
end;
PROCEDURE TExpControlObj.Sample;
@@ -574,13 +493,25 @@ procedure TExpControlObj.MakePosSequence;
FPresentVpu[i] := (Vpresent / PVSys.NPhases) / (basekV * 1000.0);
// if initializing with Vreg=0 in static mode, we want to FIND Vreg
if (ActiveCircuit.Solution.ControlMode = CTRLSTATIC) and (FVregInit <= 0.0) then
+ begin
FVregs[i] := FPresentVpu[i];
+ if FVregs[i] < VregMin then
+ begin
+ FVregs[i] := VregMin;
+ FVregInit := 0.01; // don't let it outside the band
+ end;
+ if FVregs[i] > VregMax then
+ begin
+ FVregs[i] := VregMax;
+ FVregInit := 0.01; // don't let it outside the band
+ end;
+ end;
// both errors are in per-unit
Verr := Abs(FPresentVpu[i] - FPriorVpu[i]);
Qerr := Abs(PVSys.Presentkvar - FTargetQ[i]) / PVSys.kVARating;
// process the sample
if (PVSys.InverterON = FALSE) and (PVSys.VarFollowInverter = TRUE) then begin // not injecting
- if (FVregTau > 0.0) and (FVregs[i] <= 0.0) then
+ if (VregTau > 0.0) and (FVregs[i] <= 0.0) then
FVregs[i] := FPresentVpu[i]; // wake up to the grid voltage, otherwise track it while not injecting
continue;
end;
@@ -602,24 +533,6 @@ procedure TExpControlObj.MakePosSequence;
end; {If FlistSize}
end;
-procedure TExpControlObj.InitPropertyValues(ArrayOffset: Integer);
-begin
- PropertyValue[1] := ''; // PVSystem list
- PropertyValue[2] := '1'; // initial Vreg
- PropertyValue[3] := '50'; // slope
- PropertyValue[4] := '1200.0'; // VregTau
- PropertyValue[5] := '0'; // Q bias
- PropertyValue[6] := '0.95'; // Vreg min
- PropertyValue[7] := '1.05'; // Vreg max
- PropertyValue[8] := '0.44'; // Qmax leading
- PropertyValue[9] := '0.44'; // Qmax lagging
- PropertyValue[10] := 'no'; // write event log?
- PropertyValue[11] := '0.7'; // DeltaQ_factor
- PropertyValue[12] := 'no'; // PreferQ
- PropertyValue[13] := '0'; // TResponse
- inherited InitPropertyValues(NumPropsThisClass);
-end;
-
Function TExpControlObj.MakePVSystemList:Boolean;
VAR
PVSysClass:TDSSClass;
@@ -638,17 +551,29 @@ procedure TExpControlObj.InitPropertyValues(ArrayOffset: Integer);
SetLength(FTargetQ,FListSize+1);
SetLength(FWithinTol, FListSize+1);
SetLength(FVregs, FListSize+1);
- For i := 1 to FListSize Do Begin
+ For i := 1 to FListSize Do
+ begin
PVSys := PVSysClass.Find(FPVSystemNameList.Strings[i-1]);
- If Assigned(PVSys) and PVSys.Enabled Then FPVSystemPointerList.New := PVSys;
- End;
- End Else Begin
- {Search through the entire circuit for enabled pvsysten objects and add them to the list}
- For i := 1 to PVSysClass.ElementCount Do Begin
+ if Assigned(PVSys) and PVSys.Enabled then
+ begin
+ FPVSystemPointerList.Add(PVSys);
+ PVSys.AVRmode := True;
+ end;
+ end;
+ end
+ else
+ begin
+ // Search through the entire circuit for enabled pvsysten objects and add them to the list
+ For i := 1 to PVSysClass.ElementCount Do
+ begin
PVSys := PVSysClass.ElementList.Get(i);
- If PVSys.Enabled Then FPVSystemPointerList.New := PVSys;
+ If PVSys.Enabled Then
+ begin
+ FPVSystemPointerList.Add(PVSys);
+ PVSys.AVRmode := True;
+ end;
FPVSystemNameList.Add(PVSys.Name);
- End;
+ end;
FListSize := FPVSystemPointerList.Count;
SetLength(ControlledElement,FListSize+1);
@@ -665,9 +590,10 @@ procedure TExpControlObj.InitPropertyValues(ArrayOffset: Integer);
End; {Else}
//Initialize arrays
- For i := 1 to FlistSize Do begin
-// PVSys := PVSysClass.Find(FPVSystemNameList.Strings[i-1]);
-// Set_NTerms(PVSys.NTerms); // TODO - what is this for?
+ For i := 1 to FlistSize Do
+ begin
+ // PVSys := PVSysClass.Find(FPVSystemNameList.Strings[i-1]);
+ // Set_NTerms(PVSys.NTerms); // TODO - what is this for?
FPriorVpu[i] := 0.0;
FPresentVpu[i] := 0.0;
FLastIterQ[i] := -1.0;
@@ -676,7 +602,7 @@ procedure TExpControlObj.InitPropertyValues(ArrayOffset: Integer);
FWithinTol[i] := False;
FVregs[i] := FVregInit;
FPendingChange[i] := NONE;
- end; {For}
+ end;
RecalcElementData;
If FPVSystemPointerList.Count>0 Then Result := TRUE;
end;
@@ -686,48 +612,11 @@ procedure TExpControlObj.Reset;
// inherited;
end;
-FUNCTION TExpControlObj.GetPropertyValue(Index: Integer): String;
-Begin
- Result := '';
- CASE Index of
- 1 : Result := ReturnElementsList;
- 2 : Result := Format('%.6g', [FVregInit]);
- 3 : Result := Format('%.6g', [FSlope]);
- 4 : Result := Format('%.6g', [FVregTau]);
- 5 : Result := Format('%.6g', [FQbias]);
- 6 : Result := Format('%.6g', [FVregMin]);
- 7 : Result := Format('%.6g', [FVregMax]);
- 8 : Result := Format('%.6g', [FQmaxLead]);
- 9 : Result := Format('%.6g', [FQmaxLag]);
- 11 : Result := Format('%.6g', [FdeltaQ_factor]);
- 12 : Result := StrYorN(FPreferQ);
- 13 : Result := Format('%.6g', [FTresponse]);
- // 10 skipped, EventLog always went to the default handler
- ELSE // take the generic handler
- Result := Inherited GetPropertyValue(index);
- END;
-End;
-
-FUNCTION TExpControlObj.ReturnElementsList: String;
-VAR
- i :Integer;
-Begin
- If FListSize=0 Then Begin
- Result := '';
- Exit;
- End;
- Result := '['+ FPVSystemNameList.Strings[0];
- For i := 1 to FListSize-1 Do Begin
- Result := Result + ', ' + FPVSystemNameList.Strings[i];
- End;
- Result := Result + ']'; // terminate the array
-End;
-
-procedure TExpControlObj.Set_Enabled(Value: Boolean);
-begin
- inherited;
- {Reset controlled PVSystems to original PF}
-end;
+//procedure TExpControlObj.Set_Enabled(Value: Boolean);
+//begin
+// inherited;
+// // Reset controlled PVSystems to original PF
+//end;
procedure TExpControlObj.Set_PendingChange(Value: Integer;DevIndex: Integer);
begin
@@ -744,15 +633,15 @@ procedure TExpControlObj.UpdateExpControl(i:integer);
for j := 1 to FPVSystemPointerList.Count do begin
PVSys := ControlledElement[j];
FLastStepQ[j] := PVSys.Presentkvar;
- if FVregTau > 0.0 then begin
+ if VregTau > 0.0 then begin
dt := ActiveCircuit.Solution.Dynavars.h;
Verr := FPresentVpu[j] - FVregs[j];
- FVregs[j] := FVregs[j] + Verr * (1 - Exp (-dt / FVregTau)); // TODO - precalculate?
+ FVregs[j] := FVregs[j] + Verr * (1 - Exp (-dt / VregTau)); // TODO - precalculate?
end else begin
Verr := 0.0;
end;
- if FVregs[j] < FVregMin then FVregs[j] := FVregMin;
- if FVregs[j] > FVregMax then FVregs[j] := FVregMax;
+ if FVregs[j] < VregMin then FVregs[j] := VregMin;
+ if FVregs[j] > VregMax then FVregs[j] := VregMax;
PVSys.Set_Variable(5,FVregs[j]);
If ShowEventLog Then AppendtoEventLog('ExpControl.' + Self.Name+','+PVSys.Name,
Format(' Setting new Vreg= %.5g Vpu=%.5g Verr=%.5g',
@@ -775,8 +664,4 @@ procedure TExpControlObj.UpdateExpControl(i:integer);
If Enabled Then UpdateExpControl(i);
End;
-INITIALIZATION
-
-Finalization
-
-end.
+end.
\ No newline at end of file
diff --git a/src/Controls/GenDispatcher.pas b/src/Controls/GenDispatcher.pas
index da95f5a53..7514716e6 100644
--- a/src/Controls/GenDispatcher.pas
+++ b/src/Controls/GenDispatcher.pas
@@ -6,16 +6,12 @@
All rights reserved.
----------------------------------------------------------
}
-{
- A GenDispatcher is a control element that is connected to a terminal of another
- circuit element and sends dispatch kW signals to a set of generators it controls
-
- A GenDispatcher is defined by a New command:
-
- New GenDispatcher.Name=myname Element=devclass.name terminal=[ 1|2|...] CapacitorList = (gen1 gen2 ...)
-
-
-}
+// A GenDispatcher is a control element that is connected to a terminal of another
+// circuit element and sends dispatch kW signals to a set of generators it controls
+//
+// A GenDispatcher is defined by a New command:
+//
+// New GenDispatcher.Name=myname Element=devclass.name terminal=[ 1|2|...] CapacitorList = (gen1 gen2 ...)
interface
@@ -26,33 +22,39 @@ interface
CktElement,
DSSClass,
Arraydef,
- ucomplex,
+ UComplex, DSSUcomplex,
utilities,
DSSPointerList,
Classes;
type
+{$SCOPEDENUMS ON}
+ TGenDispatcherProp = (
+ INVALID = 0,
+ Element = 1,
+ Terminal = 2,
+ kWLimit = 3,
+ kWBand = 4,
+ kvarlimit = 5,
+ GenList = 6,
+ Weights = 7
+ );
+{$SCOPEDENUMS OFF}
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TGenDispatcher = class(TControlClass)
PRIVATE
PROTECTED
- procedure DefineProperties;
- function MakeLike(const GenDispatcherName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TGenDispatcherObj = class(TControlElem)
PRIVATE
-
FkWLimit,
FkWBand,
HalfkWBand,
@@ -64,31 +66,24 @@ TGenDispatcherObj = class(TControlElem)
FWeights: pDoubleArray;
PUBLIC
-
constructor Create(ParClass: TDSSClass; const GenDispatcherName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
procedure RecalcElementData; OVERRIDE;
- procedure CalcYPrim; OVERRIDE; // Always Zero for a GenDispatcher
procedure Sample; OVERRIDE; // Sample control quantities and set action times in Control Queue
procedure DoPendingAction(const Code, ProxyHdl: Integer); OVERRIDE; // Do the action that is pending from last sample
procedure Reset; OVERRIDE; // Reset to initial defined state
- procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present value of terminal Curr
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
function MakeGenList: Boolean;
end;
-{--------------------------------------------------------------------------}
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Circuit,
@@ -101,220 +96,118 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TGenDispatcherObj;
+ TProp = TGenDispatcherProp;
const
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
- NumPropsThisClass = 7;
-
-
-{--------------------------------------------------------------------------}
-constructor TGenDispatcher.Create(dssContext: TDSSContext); // Creates superstructure for all GenDispatcher objects
+constructor TGenDispatcher.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
-
- Class_name := 'GenDispatcher';
- DSSClassType := DSSClassType + GEN_CONTROL;
-
- DefineProperties;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, GEN_CONTROL, 'GenDispatcher');
end;
-{--------------------------------------------------------------------------}
destructor TGenDispatcher.Destroy;
-
begin
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TGenDispatcher.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
-
- PropertyName[1] := 'Element';
- PropertyName[2] := 'Terminal';
- PropertyName[3] := 'kWLimit';
- PropertyName[4] := 'kWBand';
- PropertyName[5] := 'kvarlimit';
- PropertyName[6] := 'GenList';
- PropertyName[7] := 'Weights';
-
- PropertyHelp[1] := 'Full object name of the circuit element, typically a line or transformer, ' +
- 'which the control is monitoring. There is no default; must be specified.';
- PropertyHelp[2] := 'Number of the terminal of the circuit element to which the GenDispatcher control is connected. ' +
- '1 or 2, typically. Default is 1. Make sure you have the direction on the power matching the sign of kWLimit.';
- PropertyHelp[3] := 'kW Limit for the monitored element. The generators are dispatched to hold the power in band.';
- PropertyHelp[4] := 'Bandwidth (kW) of the dead band around the target limit.' +
- 'No dispatch changes are attempted if the power in the monitored terminal stays within this band.';
- PropertyHelp[5] := 'Max kvar to be delivered through the element. Uses same dead band as kW.';
- PropertyHelp[6] := 'Array list of generators to be dispatched. If not specified, all generators in the circuit are assumed dispatchable.';
- PropertyHelp[7] := 'Array of proportional weights corresponding to each generator in the GenList.' +
- ' The needed kW to get back to center band is dispatched to each generator according to these weights. ' +
- 'Default is to set all weights to 1.0.';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // string list
+ PropertyType[ord(TProp.GenList)] := TPropertyType.StringListProperty;
+ PropertyOffset[ord(TProp.GenList)] := ptruint(@obj.FGeneratorNameList);
+
+ // double arrays
+ PropertyType[ord(TProp.Weights)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.Weights)] := ptruint(@obj.FWeights);
+ PropertyOffset2[ord(TProp.Weights)] := ptruint(@obj.FListSize);
+
+ // object reference
+ PropertyType[ord(TProp.Element)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.Element)] := ptruint(@obj.FMonitoredElement);
+ PropertyOffset2[ord(TProp.Element)] := 0;
+ PropertyWriteFunction[ord(TProp.Element)] := @SetMonitoredElement;
+ PropertyFlags[ord(TProp.Element)] := [TPropertyFlag.WriteByFunction];//[TPropertyFlag.CheckForVar]; // not required for general cktelements
+
+ // integer properties
+ PropertyType[ord(TProp.Terminal)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Terminal)] := ptruint(@obj.ElementTerminal);
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.kWLimit)] := ptruint(@obj.FkWLimit);
+ PropertyOffset[ord(TProp.kWBand)] := ptruint(@obj.FkWBand);
+ PropertyOffset[ord(TProp.kvarlimit)] := ptruint(@obj.FkvarLimit);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ inherited DefineProperties;
end;
-{--------------------------------------------------------------------------}
-function TGenDispatcher.NewObject(const ObjName: String): Integer;
+function TGenDispatcher.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new GenDispatcher and add it to GenDispatcher class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TGenDispatcherObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-{--------------------------------------------------------------------------}
-function TGenDispatcher.Edit: Integer;
+procedure TGenDispatcherObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
i: Integer;
-
begin
-
- // continue parsing WITH contents of Parser
- DSS.ActiveGenDispatcherObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveGenDispatcherObj;
-
- Result := 0;
-
- with DSS.ActiveGenDispatcherObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 364);
- 1:
- ElementName := lowercase(param);
- 2:
- ElementTerminal := Parser.IntValue;
- 3:
- FkWLimit := Parser.DblValue;
- 4:
- FkWBand := Parser.DblValue;
- 5:
- FkvarLimit := Parser.DblValue;
- 6:
- InterpretTStringListArray(Param, FGeneratorNameList);
- 7:
- begin
- FListSize := FGeneratorNameList.count;
- if FListSize > 0 then
- begin
- Reallocmem(FWeights, Sizeof(FWeights^[1]) * FListSize);
- FListSize := InterpretDblArray(Param, FListSize, FWeights);
- end;
- end;
-
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveGenDispatcherObj, ParamPointer - NumPropsthisClass)
- end;
-
- case ParamPointer of
- 4:
- HalfkWBand := FkWBand / 2.0;
- 6:
- begin // levelize the list
- FGenPointerList.Clear; // clear this for resetting on first sample
- FListSize := FGeneratorNameList.count;
- Reallocmem(FWeights, Sizeof(FWeights^[1]) * FListSize);
- for i := 1 to FListSize do
- FWeights^[i] := 1.0;
- end;
- else
-
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ case Idx of
+ 4:
+ HalfkWBand := FkWBand / 2.0;
+ 6:
+ begin // levelize the list
+ FGenPointerList.Clear; // clear this for resetting on first sample
+ FListSize := FGeneratorNameList.count;
+ Reallocmem(FWeights, Sizeof(FWeights^[1]) * FListSize);
+ for i := 1 to FListSize do
+ FWeights^[i] := 1.0;
end;
-
- RecalcElementData;
end;
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-
-{--------------------------------------------------------------------------}
-function TGenDispatcher.MakeLike(const GenDispatcherName: String): Integer;
+procedure TGenDispatcherObj.MakeLike(OtherPtr: Pointer);
var
- OtherGenDispatcher: TGenDispatcherObj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this GenDispatcher name in the present collection}
- OtherGenDispatcher := Find(GenDispatcherName);
- if OtherGenDispatcher <> NIL then
- with DSS.ActiveGenDispatcherObj do
- begin
-
- NPhases := OtherGenDispatcher.Fnphases;
- NConds := OtherGenDispatcher.Fnconds; // Force Reallocation of terminal stuff
-
- ElementName := OtherGenDispatcher.ElementName;
- ControlledElement := OtherGenDispatcher.ControlledElement; // Pointer to target circuit element
- MonitoredElement := OtherGenDispatcher.MonitoredElement; // Pointer to target circuit element
-
- ElementTerminal := OtherGenDispatcher.ElementTerminal;
-
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherGenDispatcher.PropertyValue[i];
-
- end
- else
- DoSimpleMsg('Error in GenDispatcher MakeLike: "' + GenDispatcherName + '" Not Found.', 370);
-
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
+ // ControlledElement := Other.ControlledElement; // Pointer to target circuit element
+ MonitoredElement := Other.MonitoredElement; // Pointer to target circuit element
+ ElementTerminal := Other.ElementTerminal;
end;
-
-{==========================================================================}
-{ TGenDispatcherObj }
-{==========================================================================}
-
-
-{--------------------------------------------------------------------------}
constructor TGenDispatcherObj.Create(ParClass: TDSSClass; const GenDispatcherName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(GenDispatcherName);
+ Name := AnsiLowerCase(GenDispatcherName);
DSSObjType := ParClass.DSSClassType;
- NPhases := 3; // Directly set conds and phases
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 1; // this forces allocation of terminals and conductors
// in base class
-
- ElementName := '';
ControlledElement := NIL; // not used in this control
ElementTerminal := 1;
MonitoredElement := NIL;
@@ -327,40 +220,26 @@ constructor TGenDispatcherObj.Create(ParClass: TDSSClass; const GenDispatcherNam
FkWBand := 100.0;
TotalWeight := 1.0;
HalfkWBand := FkWBand / 2.0;
- InitPropertyValues(0);
FkvarLimit := FkWLimit / 2.0;
-
// RecalcElementData;
-
end;
destructor TGenDispatcherObj.Destroy;
begin
- ElementName := '';
inherited Destroy;
end;
-{--------------------------------------------------------------------------}
procedure TGenDispatcherObj.RecalcElementData;
-
-var
- DevIndex: Integer;
-
begin
-
-
-{Check for existence of monitored element}
-
- Devindex := GetCktElementIndex(ElementName); // Global function
- if DevIndex > 0 then
+ {Check for existence of monitored element}
+ if MonitoredElement <> NIL then
begin
- MonitoredElement := ActiveCircuit.CktElements.Get(DevIndex);
if ElementTerminal > MonitoredElement.Nterms then
begin
- DoErrorMsg('GenDispatcher: "' + Name + '"',
- 'Terminal no. "' + '" does not exist.',
- 'Re-specify terminal no.', 371);
+ DoErrorMsg(Format(_('GenDispatcher: "%s"'), [Name]),
+ Format(_('Terminal no. "%d" does not exist.'), [ElementTerminal]),
+ _('Re-specify terminal no.'), 371);
end
else
begin
@@ -369,75 +248,26 @@ procedure TGenDispatcherObj.RecalcElementData;
end;
end
else
- DoSimpleMsg('Monitored Element in GenDispatcher.' + Name + ' does not exist:"' + ElementName + '"', 372);
-
-
+ DoSimpleMsg('Monitored Element in %s is not set', [FullName], 372);
end;
-procedure TGenDispatcherObj.MakePosSequence;
+procedure TGenDispatcherObj.MakePosSequence();
begin
if MonitoredElement <> NIL then
begin
- Nphases := ControlledElement.NPhases;
+ FNphases := ControlledElement.NPhases;
Nconds := FNphases;
Setbus(1, MonitoredElement.GetBus(ElementTerminal));
end;
inherited;
end;
-{--------------------------------------------------------------------------}
-procedure TGenDispatcherObj.CalcYPrim;
-begin
- // leave YPrims as nil and they will be ignored
- // Yprim is zeroed when created. Leave it as is.
- // IF YPrim=nil THEN YPrim := TcMatrix.CreateMatrix(Yorder);
-end;
-
-
-{--------------------------------------------------------------------------}
-procedure TGenDispatcherObj.GetCurrents(Curr: pComplexArray);
-var
- i: Integer;
-begin
-
- for i := 1 to Fnconds do
- Curr^[i] := CZERO;
-
-end;
-
-{--------------------------------------------------------------------------}
-procedure TGenDispatcherObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
- if Complete then
- begin
- FSWriteln(F);
- end;
-
-end;
-
-
-{--------------------------------------------------------------------------}
procedure TGenDispatcherObj.DoPendingAction;
begin
-
{Do Nothing}
end;
-{--------------------------------------------------------------------------}
procedure TGenDispatcherObj.Sample;
-
var
i: Integer;
PDiff,
@@ -454,7 +284,6 @@ procedure TGenDispatcherObj.Sample;
if FListSize > 0 then
begin
-
//----MonitoredElement.ActiveTerminalIdx := ElementTerminal;
S := MonitoredElement.Power[ElementTerminal]; // Power in active terminal
@@ -506,74 +335,46 @@ procedure TGenDispatcherObj.Sample;
// Push present time onto control queue to force re solve at new dispatch value
ControlQueue.Push(DynaVars.intHour, DynaVars.t, 0, 0, Self);
end;
-
-
{Else just continue}
end;
-
-
-end;
-
-
-procedure TGenDispatcherObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := ''; //'element';
- PropertyValue[2] := '1'; //'terminal';
- PropertyValue[3] := '8000';
- PropertyValue[4] := '100';
- PropertyValue[5] := '0';
- PropertyValue[6] := '';
- PropertyValue[7] := '';
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
end;
function TGenDispatcherObj.MakeGenList: Boolean;
-
var
GenClass: TDSSClass;
Gen: TGeneratorObj;
i: Integer;
begin
-
Result := FALSE;
GenClass := GetDSSClassPtr(DSS, 'generator');
if FListSize > 0 then
begin // Name list is defined - Use it
-
for i := 1 to FListSize do
begin
Gen := GenClass.Find(FGeneratorNameList.Strings[i - 1]);
if Assigned(Gen) and Gen.Enabled then
- FGenPointerList.New := Gen;
+ FGenPointerList.Add(Gen);
end;
-
end
else
begin
- {Search through the entire circuit for enabled generators and add them to the list}
-
+ // Search through the entire circuit for enabled generators and add them to the list
for i := 1 to GenClass.ElementCount do
begin
Gen := GenClass.ElementList.Get(i);
if Gen.Enabled then
- FGenPointerList.New := Gen;
+ FGenPointerList.Add(Gen);
end;
-
- {Allocate uniform weights}
+ // Allocate uniform weights
FListSize := FGenPointerList.Count;
Reallocmem(FWeights, Sizeof(FWeights^[1]) * FListSize);
for i := 1 to FListSize do
FWeights^[i] := 1.0;
-
end;
- // Add up total weights
+ // Add up total weights
TotalWeight := 0.0;
for i := 1 to FlistSize do
TotalWeight := TotalWeight + FWeights^[i];
@@ -586,11 +387,6 @@ function TGenDispatcherObj.MakeGenList: Boolean;
procedure TGenDispatcherObj.Reset;
begin
// inherited;
-
end;
-
-initialization
-
-
-end.
+end.
\ No newline at end of file
diff --git a/src/Controls/InvControl.pas b/src/Controls/InvControl.pas
index 30e2b9d16..bdea8c068 100644
--- a/src/Controls/InvControl.pas
+++ b/src/Controls/InvControl.pas
@@ -1,803 +1,515 @@
unit InvControl;
+
{
----------------------------------------------------------
Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
All rights reserved.
----------------------------------------------------------
}
-{
- A InvControl is a control element that is connected to a terminal of another
- circuit element and sends kW and/or kvar signals to a set of PVSystem objects it controls
-
- A InvControl is defined by a New command:
-
- New InvControl.Name=myname PVSystemList = (pvsystem1 pvsystem2 ...)
-
-Notes:
- WGS (11/26/2012): Using dynamic arrays for many private variables in this unit.
- Although dynamic arrays begin at 0 (by definition in Delphi),
- this unit is using 1 to numberelements in all for loops - the 0th
- element is un-used (except for Strings) in this unit.
- All dynamic arrays are set to length numberelements+1 in the appropriate dimension.
- All dynamic arrays are Finalize'd in the destroy procedure.
-
- // Updated 9/24/2015 to allow for simultaneous modes and additional functionality
-}
-
-INTERFACE
-
-USES
- {$IFDEF FPC}gqueue{$ELSE}System.Generics.Collections{$ENDIF},
- Command, ControlClass, ControlElem, CktElement, DSSClass, PVSystem, Arraydef, ucomplex,
- utilities, XYcurve, Dynamics, DSSPointerList, Classes, StrUtils;
-
-TYPE
-
-
-
-
-
- ERateofChangeMode = (
- INACTIVE,
- LPF,
- RISEFALL
- );
-
+// A InvControl is a control element that is connected to a terminal of another
+// circuit element and sends kW and/or kvar signals to a set of PVSystem objects it controls
+//
+// A InvControl is defined by a New command:
+//
+// New InvControl.Name=myname PVSystemList = (pvsystem1 pvsystem2 ...)
+//
+// Notes:
+// WGS (11/26/2012): Using dynamic arrays for many private variables in this unit.
+// Although dynamic arrays begin at 0 (by definition in Delphi),
+// this unit is using 1 to numberelements in all for loops - the 0th
+// element is un-used (except for Strings) in this unit.
+// All dynamic arrays are set to length numberelements+1 in the appropriate dimension.
+// All dynamic arrays are Finalize'd in the destroy procedure.
+//
+// // Updated 9/24/2015 to allow for simultaneous modes and additional functionality
+
+interface
+
+uses
+ RollAvgWindow,
+ Command,
+ ControlClass,
+ ControlElem,
+ CktElement,
+ DSSClass,
+ PVSystem,
+ Arraydef,
+ UComplex, DSSUcomplex,
+ utilities,
+ XYcurve,
+ Dynamics,
+ DSSPointerList,
+ Classes,
+ StrUtils;
type
+{$SCOPEDENUMS ON}
+ TInvControlProp = (
+ INVALID = 0,
+ PVSystemList = 1,
+ Mode = 2,
+ CombiMode = 3,
+ vvc_curve1 = 4,
+ hysteresis_offset = 5,
+ voltage_curvex_ref = 6,
+ avgwindowlen = 7,
+ voltwatt_curve = 8,
+ //following for dynamic reactive current mode
+ DbVMin = 9,
+ DbVMax = 10,
+ ArGraLowV = 11,
+ ArGraHiV = 12,
+ DynReacavgwindowlen = 13,
+ DeltaQ_factor = 14,
+ VoltageChangeTolerance = 15,
+ VarChangeTolerance = 16,
+ VoltwattYAxis = 17,
+ RateofChangeMode = 18,
+ LPFTau = 19,
+ RiseFallLimit = 20,
+ DeltaP_factor = 21,
+ EventLog = 22,
+ VV_RefReactivePower = 23,
+ ActivePChangeTolerance = 24
+ );
+{$SCOPEDENUMS OFF}
+
+ ERateofChangeMode = (
+ INACTIVE,
+ LPF,
+ RISEFALL
+ );
+
+ TInvControl = class(TControlClass)
+ PRIVATE
+ XY_CurveClass: TDSSClass;
+ PROTECTED
+ procedure DefineProperties; override;
+ PUBLIC
+ constructor Create(dssContext: TDSSContext);
+ destructor Destroy; OVERRIDE;
+ function NewObject(const ObjName: Ansistring; Activate: Boolean = True): Pointer; OVERRIDE;
+
+ procedure UpdateAll;
+ end;
- TRollAvgWindow = class(TObject)
-
-
-private
-
- sample : TQueue;
- sampletime : TQueue;
- runningsumsample : Double;
- runningsumsampletime : Double;
- bufferlength : Integer;
- bufferfull : Boolean;
- function Get_AvgVal : Double;
- function Get_AccumSec : Double;
- procedure Set_BuffLength(const Value: Integer);
-
-public
- constructor Create();
- destructor Destroy; override;
- procedure Add(IncomingSampleValue: Double;IncomingSampleTime: Double;VAvgWindowLengthSec:Double);
-
-
- Property AvgVal :Double Read Get_AvgVal;
- Property AccumSec :Double Read Get_AccumSec;
- Property BuffLength :Integer Read bufferlength Write Set_BuffLength;
-
-end;
-
-
-
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
- TInvControl = class(TControlClass)
- private
- XY_CurveClass: TDSSClass;
- protected
- PROCEDURE DefineProperties;
- FUNCTION MakeLike(const InvControlName:AnsiString):Integer;Override;
- public
- constructor Create(dssContext: TDSSContext);
- destructor Destroy; override;
-
- FUNCTION Edit:Integer; override; // uses global parser
- FUNCTION NewObject(const ObjName:AnsiString):Integer; override;
- Function GetXYCurve(Const CurveName: AnsiString; InvControlMode: AnsiString): TXYcurveObj;
- PROCEDURE UpdateAll;
- end;
-
-
-
- // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
- TInvControlObj = class(TControlElem)
- private
-
- ControlMode : AnsiString;
- CombiControlMode : AnsiString;
- ControlActionHandle: Integer;
- ControlledElement: Array of TPVSystemObj; // list of pointers to controlled PVSystem elements
-
- FkWLimit,
- FkvarLimit,
- FkVALimit,
- FVref, // kV rating for the PVSystem object
- FPpf, // power factor parameter from the PVSystem object, not necessarily present pf 'output' if limited by kva rating or other parameters
- Fpresentkvar, // kvar parameter from the PVSystem object, not necessarily present kvar output if limited by kva rating or other parameters
- FpresentkW: Array of Double;
- NPhasesPVSys: Array of Integer;
- NCondsPVSys: Array of Integer;
- FListSize:Integer;
- FPVSystemNameList:TStringList;
- RateofChangeMode : ERateofChangeMode;
- FLPFTau : Double;
- FPVSystemPointerList: TDSSPointerList;
-
- Fvvc_curve_size: Integer; // length of the individual curve
- Fvvc_curve: TXYcurveObj;
- Fvvc_curvename: AnsiString;
- Fvvc_curveOffset: Double;
- Fvvc_curve2: TXYcurveObj;
- FActiveVVCurve: Array of Integer;
- FVoltage_CurveX_ref: Integer; // valid values are 0: = Vref (rated), 1:= avg
- FFinalpuPmpp: Array of Double;
- FFinalkvar: Array of Double;
- FVAvgWindowLengthSec: Double; // rolling average window length in seconds
- FDRCVAvgWindowLengthSec: Double; // rolling average window length in seconds
- cBuffer : Array of Array of Complex; // Complexarray buffer
- CondOffset : Array of Integer; // Offset for monitored terminal
- FVV_ReacPower_ref: AnsiString;
-
- FVVDeltaVtolerance: Double;
-
- Fvoltwatt_curve_size: Integer;
- Fvoltwatt_curve: TXYcurveObj;
- Fvoltwatt_curvename: AnsiString;
-
- FAvgpVuPrior: Array of Double;
- FPriorWattspu: Array of Double;
- FPriorvarspu: Array of Double;
- FLPFTime: Array of Double;
- FRiseFallLimit : Double;
- FPresentVpu: Array of Double;
- FvoltwattDeltaVTolerance: Double; // tolerance of voltage change from one solution to the
- FPendingChange: Array of Integer;
- FFlagROCOnly : Array of Boolean;
-
-
- QDeliver: Array of Double;
- QNew: Array of Double; //volt-var new set-point
- QOld: Array of Double;
- QOldVV: Array of Double;
- QOldDRC: Array of Double;
- PNew: Array of Double;
- POld: Array of Double;
- QDRCNew: Array of Double; //dynamic reactive power new set-point
-
- QHeadRoom: Array of Double;
- Qoutputpu: Array of Double;
- QoutputVVpu : Array of Double;
- QoutputDRCpu : Array of Double;
-
-
- Qdesiredpu: Array of Double;
- QDRCdesiredpu: Array of Double;
- FVpuSolution: Array of Array of Double;
- FVpuSolutionIdx: Integer;
- FdeltaQ_factor: Double;
- FdeltaP_factor: Double;
-
-
+ TInvControlObj = class(TControlElem)
+ PRIVATE
+ ControlMode: Integer;
+ CombiMode: Integer;
+ ControlActionHandle: Integer;
+ ControlledElement: array of TPVSystemObj; // list of pointers to controlled PVSystem elements
+
+ FkWLimit,
+ FkvarLimit,
+ FkVALimit,
+ FVref, // kV rating for the PVSystem object
+ FPpf, // power factor parameter from the PVSystem object, not necessarily present pf 'output' if limited by kva rating or other parameters
+ Fpresentkvar, // kvar parameter from the PVSystem object, not necessarily present kvar output if limited by kva rating or other parameters
+ FpresentkW: array of Double;
+ NPhasesPVSys: array of Integer;
+ NCondsPVSys: array of Integer;
+ FListSize: Integer;
+ FPVSystemNameList: TStringList;
+ RateofChangeMode: ERateofChangeMode;
+ FLPFTau: Double;
+ FPVSystemPointerList: TDSSPointerList;
+
+ Fvvc_curve: TXYcurveObj;
+ Fvvc_curveOffset: Double;
+ FActiveVVCurve: array of Integer;
+ FVoltage_CurveX_ref: Integer; // valid values are 0: = Vref (rated), 1:= avg
+ FFinalpuPmpp: array of Double;
+ FFinalkvar: array of Double;
+ cBuffer: array of array of Complex; // Complexarray buffer
+ CondOffset: array of Integer; // Offset for monitored terminal
+ FVV_ReacPower_ref: Integer;
+
+ FVVDeltaVtolerance: Double;
+
+ Fvoltwatt_curve: TXYcurveObj;
+
+ FAvgpVuPrior: array of Double;
+ FPriorWattspu: array of Double;
+ FPriorvarspu: array of Double;
+ FLPFTime: array of Double;
+ FRiseFallLimit: Double;
+ FPresentVpu: array of Double;
+ FvoltwattDeltaVTolerance: Double; // tolerance of voltage change from one solution to the
+ FPendingChange: array of Integer;
+ FFlagROCOnly: array of Boolean;
+
+
+ QDeliver: array of Double;
+ QNew: array of Double; //volt-var new set-point
+ QOld: array of Double;
+ QOldVV: array of Double;
+ QOldDRC: array of Double;
+ PNew: array of Double;
+ POld: array of Double;
+ QDRCNew: array of Double; //dynamic reactive power new set-point
+
+ QHeadRoom: array of Double;
+ Qoutputpu: array of Double;
+ QoutputVVpu: array of Double;
+ QoutputDRCpu: array of Double;
+
+
+ Qdesiredpu: array of Double;
+ QDRCdesiredpu: array of Double;
+ FVpuSolution: array of array of Double;
+ FVpuSolutionIdx: Integer;
+ FdeltaQ_factor: Double;
+ FdeltaP_factor: Double;
//following for dynamic reactive current mode
- FDbVMin, FDbVMax,FArGraLowV,FArGraHiV: Double;
- FRollAvgWindow : Array of TRollAvgWindow;
- FRollAvgWindowLength : Integer;
- FRollAvgWindowLengthIntervalUnit: AnsiString;
- deltaVDynReac: Array of Double;
- priorRollAvgWindow: Array of Double;
- FDRCRollAvgWindow : Array of TRollAvgWindow;
- FDRCRollAvgWindowLength : Integer;
- FDRCRollAvgWindowLengthIntervalUnit: AnsiString;
- priorDRCRollAvgWindow: Array of Double;
- FlagChangeCurve: Array of Boolean;
- FVoltwattYAxis: Integer; // 1 = %Pmpp, 0 = %Available power
- FVoltageChangeTolerance: Double;
- FVarChangeTolerance: Double;
- FActivePChangeTolerance: Double;
- FWithinTol: Array of Boolean;
- FWithinTolVV: Array of Boolean;
- FWithinTolVW: Array of Boolean;
- FROCEvaluated: Array of Boolean;
- FHitkVALimit: Array of Boolean;
- FHitkvarLimit: Array of Boolean;
-
-
- PROCEDURE Set_PendingChange(Value: Integer;DevIndex: Integer);
- FUNCTION Get_PendingChange(DevIndex: Integer):Integer;
- FUNCTION InterpretAvgVWindowLen(const s:AnsiString):Integer;
- FUNCTION InterpretDRCAvgVWindowLen(const s:AnsiString):Integer;
- FUNCTION ReturnElementsList:AnsiString;
- PROCEDURE UpdateInvControl(i:integer);
- public
- constructor Create(ParClass:TDSSClass; const InvControlName:AnsiString);
- destructor Destroy; override;
-
- PROCEDURE Set_Enabled(Value:Boolean);Override;
- PROCEDURE MakePosSequence; Override; // Make a positive Sequence Model
- PROCEDURE RecalcElementData; Override;
- PROCEDURE CalcYPrim; Override; // Always Zero for a InvControl
+ FDbVMin, FDbVMax, FArGraLowV, FArGraHiV: Double;
+ FRollAvgWindow: array of TRollAvgWindow;
+ FRollAvgWindowLength: Integer; // FVAvgWindowLengthSec // rolling average window length in seconds
+ deltaVDynReac: array of Double;
+ priorRollAvgWindow: array of Double;
+ FDRCRollAvgWindow: array of TRollAvgWindow;
+ FDRCRollAvgWindowLength: Integer; // FDRCVAvgWindowLengthSec // rolling average window length in seconds
+ priorDRCRollAvgWindow: array of Double;
+ FlagChangeCurve: array of Boolean;
+ FVoltwattYAxis: Integer; // 1 = %Pmpp, 0 = %Available power
+ FVoltageChangeTolerance: Double;
+ FVarChangeTolerance: Double;
+ FActivePChangeTolerance: Double;
+ FWithinTol: array of Boolean;
+ FWithinTolVV: array of Boolean;
+ FWithinTolVW: array of Boolean;
+ FROCEvaluated: array of Boolean;
+ FHitkVALimit: array of Boolean;
+ FHitkvarLimit: array of Boolean;
+
+ procedure Set_PendingChange(Value: Integer; DevIndex: Integer);
+ function Get_PendingChange(DevIndex: Integer): Integer;
+ function ReturnElementsList: Ansistring;
+ procedure UpdateInvControl(i: Integer);
+ PUBLIC
+ constructor Create(ParClass: TDSSClass; const InvControlName: Ansistring);
+ destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
+
+ // procedure Set_Enabled(Value: Boolean); OVERRIDE;
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
+ procedure RecalcElementData; OVERRIDE;
// Sample control quantities and set action times in Control Queue
- PROCEDURE Sample; Override;
+ procedure Sample; OVERRIDE;
// Do the action that is pending from last sample
- PROCEDURE DoPendingAction(Const Code, ProxyHdl:Integer); Override;
-
- PROCEDURE Reset; Override; // Reset to initial defined state
+ procedure DoPendingAction(const Code, ProxyHdl: Integer); OVERRIDE;
- PROCEDURE GetCurrents(Curr: pComplexArray); Override; // Get present value of terminal Curr
- PROCEDURE CalcVoltWatt_pu(j: Integer);
- PROCEDURE CalcVoltVar_vars(j: Integer);
- PROCEDURE CalcDRC_vars(j: Integer);
- FUNCTION CalcLPF(m: Integer; powertype: AnsiString;PVSys:TPVSystemObj):Double;
- FUNCTION CalcRF(m: Integer; powertype: AnsiString;PVSys:TPVSystemObj):Double;
- PROCEDURE InitPropertyValues(ArrayOffset:Integer);Override;
- PROCEDURE DumpProperties(F: TFileStream; Complete:Boolean);Override;
+ procedure Reset; OVERRIDE; // Reset to initial defined state
- FUNCTION MakePVSystemList:Boolean;
- FUNCTION GetPropertyValue(Index:Integer):AnsiString;Override;
+ procedure CalcVoltWatt_pu(j: Integer);
+ procedure CalcVoltVar_vars(j: Integer);
+ procedure CalcDRC_vars(j: Integer);
+ function CalcLPF(m: Integer; powertype: Ansistring; PVSys: TPVSystemObj): Double;
+ function CalcRF(m: Integer; powertype: Ansistring; PVSys: TPVSystemObj): Double;
- Property PendingChange[DevIndex: Integer]:Integer Read Get_PendingChange Write Set_PendingChange;
+ function MakePVSystemList: Boolean;
- end;
+ property PendingChange[DevIndex: Integer]: Integer READ Get_PendingChange WRITE Set_PendingChange;
-{--------------------------------------------------------------------------}
-IMPLEMENTATION
+ end;
-USES
+implementation
- ParserDel, Sysutils, DSSClassDefs, DSSGlobals, Circuit, uCmatrix, MathUtil, Math,
+uses
+ Sysutils,
+ DSSClassDefs,
+ DSSGlobals,
+ Circuit,
+ uCmatrix,
+ MathUtil,
+ Math,
DSSHelper,
DSSObjectHelper,
TypInfo;
-CONST
-
- NumPropsThisClass = 24;
+type
+ TObj = TInvControlObj;
+ TProp = TInvControlProp;
+const
+ NumPropsThisClass = Ord(High(TProp));
NONE = 0;
CHANGEVARLEVEL = 1;
CHANGEWATTLEVEL = 2;
CHANGEWATTVARLEVEL = 3;
CHANGEDRCVVARLEVEL = 4;
+ VARAVAL_WATTS = 0;
+ VARMAX_VARS = 1;
+ VARMAX_WATTS = 2;
+
+ NONE_MODE = 0;
+ VOLTVAR = 1;
+ VOLTWATT = 2;
+ DRC = 3;
+ FIXEDPF = 4;
+
+ NONE_COMBMODE = 0;
+ VV_VW = 1;
+ VV_DRC = 2;
+var
+ PropInfo: Pointer = NIL;
+ ModeEnum, CombiModeEnum, RoCEnum, VV_RefQEnum, VWYAxisEnum, VCurveXRefEnum: TDSSEnum;
+
+constructor TInvControl.Create(dssContext: TDSSContext);
+begin
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ VCurveXRefEnum := TDSSEnum.Create('InvControl: Curve X Reference', True, 1, 3,
+ ['rated', 'avg', 'ravg'],
+ [0, 1, 2]);
+ VWYAxisEnum := TDSSEnum.Create('InvControl: Volt-Watt Y-Axis', True, 2, 2,
+ ['pavailablepu', 'pmpppu', 'pctpmpppu'],
+ [0, 1, 2]);
+ ModeEnum := TDSSEnum.Create('LegacyInvControl: Control Mode', True, 1, 5,
+ ['Voltvar', 'VoltWatt', 'DynamicReaccurr', 'FixedPF'],
+ [ord(VOLTVAR), ord(VOLTWATT), ord(DRC), ord(FIXEDPF)]);
+ CombiModeEnum := TDSSEnum.Create('LegacyInvControl: Combi Mode', True, 4, 4,
+ ['VV_VW', 'VV_DRC'], [ord(VV_VW), ord(VV_DRC)]);
+ RoCEnum := TDSSEnum.Create('LegacyInvControl: Rate-of-change Mode', True, 3, 3,
+ ['Inactive', 'LPF', 'RiseFall'], [ord(INACTIVE), ord(LPF), ord(RISEFALL)]);
+ VV_RefQEnum := TDSSEnum.Create('LegacyInvControl: VV Reactive Power Reference', True, 4, 8,
+ ['VARAVAL_WATTS', 'VARMAX_VARS', 'VARMAX_WATTS'], [0, 1, 2]);
+ VV_RefQEnum.AllowLonger := True;
+ VV_RefQEnum.DefaultValue := VARAVAL_WATTS;
+ end;
+ XY_CurveClass := GetDSSClassPtr(dssContext, 'XYCurve');
-{--------------------------------------------------------------------------}
-constructor TInvControl.Create(dssContext: TDSSContext); // Creates superstructure for all InvControl objects
-Begin
- Inherited Create(dssContext);
+ inherited Create(dssContext, INV_CONTROL, 'InvControl');
+end;
- Class_name := 'InvControl';
- DSSClassType := DSSClassType + INV_CONTROL;
+destructor TInvControl.Destroy;
+begin
+ inherited Destroy;
+end;
- DefineProperties;
+procedure TInvControl.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
+begin
+ Numproperties := NumPropsThisClass;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enums
+ PropertyType[ord(TProp.voltage_curvex_ref)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.voltage_curvex_ref)] := ptruint(@obj.FVoltage_CurveX_ref);
+ PropertyOffset2[ord(TProp.voltage_curvex_ref)] := PtrInt(VCurveXRefEnum);
+
+ PropertyType[ord(TProp.VoltwattYAxis)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.VoltwattYAxis)] := ptruint(@obj.FVoltwattYAxis);
+ PropertyOffset2[ord(TProp.VoltwattYAxis)] := PtrInt(VWYAxisEnum);
+
+ PropertyType[ord(TProp.Mode)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.Mode)] := ptruint(@obj.ControlMode);
+ PropertyOffset2[ord(TProp.Mode)] := PtrInt(ModeEnum);
+
+ PropertyType[ord(TProp.CombiMode)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.CombiMode)] := ptruint(@obj.CombiMode);
+ PropertyOffset2[ord(TProp.CombiMode)] := PtrInt(CombiModeEnum);
+
+ PropertyType[ord(TProp.RateofChangeMode)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.RateofChangeMode)] := ptruint(@obj.RateofChangeMode);
+ PropertyOffset2[ord(TProp.RateofChangeMode)] := PtrInt(RoCEnum);
+
+ PropertyType[ord(TProp.VV_RefReactivePower)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.VV_RefReactivePower)] := ptruint(@obj.FVV_ReacPower_ref);
+ PropertyOffset2[ord(TProp.VV_RefReactivePower)] := PtrUInt(VV_RefQEnum);
+
+ // object references
+ PropertyType[ord(TProp.vvc_curve1)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.vvc_curve1)] := ptruint(@obj.Fvvc_curve);
+ PropertyOffset2[ord(TProp.vvc_curve1)] := ptruint(DSS.XYCurveClass);
+
+ PropertyType[ord(TProp.voltwatt_curve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.voltwatt_curve)] := ptruint(@obj.Fvoltwatt_curve);
+ PropertyOffset2[ord(TProp.voltwatt_curve)] := ptruint(DSS.XYCurveClass);
+
+ // boolean properties
+ PropertyType[ord(TProp.EventLog)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.EventLog)] := ptruint(@obj.ShowEventLog);
+
+ // string lists
+ PropertyType[ord(TProp.PVSystemList)] := TPropertyType.StringListProperty; // TODO: convert to array of objects?
+ PropertyOffset[ord(TProp.PVSystemList)] := ptruint(@obj.FPVSystemNameList);
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.DbVMin)] := ptruint(@obj.FDbVMin);
+ PropertyOffset[ord(TProp.DbVMax)] := ptruint(@obj.FDbVMax);
+ PropertyOffset[ord(TProp.ArGraLowV)] := ptruint(@obj.FArGraLowV);
+ PropertyOffset[ord(TProp.ArGraHiV)] := ptruint(@obj.FArGraHiV);
+ PropertyOffset[ord(TProp.deltaQ_Factor)] := ptruint(@obj.FdeltaQ_factor);
+ PropertyOffset[ord(TProp.VoltageChangeTolerance)] := ptruint(@obj.FVoltageChangeTolerance);
+ PropertyOffset[ord(TProp.VarChangeTolerance)] := ptruint(@obj.FVarChangeTolerance);
+ PropertyOffset[ord(TProp.deltaP_Factor)] := ptruint(@obj.FdeltaP_factor);
+ PropertyOffset[ord(TProp.ActivePChangeTolerance)] := ptruint(@obj.FActivePChangeTolerance);
+ PropertyOffset[ord(TProp.LPFTau)] := ptruint(@obj.FLPFTau);
+ PropertyOffset[ord(TProp.RiseFallLimit)] := ptruint(@obj.FRiseFallLimit);
+
+ // advanced doubles
+ PropertyOffset[ord(TProp.hysteresis_offset)] := ptruint(@obj.Fvvc_curveOffset);
+ PropertyFlags[ord(TProp.hysteresis_offset)] := [TPropertyFlag.NonPositive];
+
+ // integer
+ PropertyType[ord(TProp.DynReacavgwindowlen)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.DynReacavgwindowlen)] := ptruint(@obj.FDRCRollAvgWindowLength);
+ PropertyFlags[ord(TProp.DynReacavgwindowlen)] := [TPropertyFlag.IntervalUnits];
+
+ PropertyType[ord(TProp.avgwindowlen)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.avgwindowlen)] := ptruint(@obj.FRollAvgWindowLength);
+ PropertyFlags[ord(TProp.avgwindowlen)] := [TPropertyFlag.IntervalUnits];
+
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
+end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
- XY_CurveClass := GetDSSClassPtr(DSS, 'XYCurve');
+function TInvControl.NewObject(const ObjName: Ansistring; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
+begin
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
+end;
-End;
+procedure ValidateXYCurve(dss: TDSSContext; var curve: TXYcurveObj; InvControlMode: Integer);
+var
+ i: Integer;
+begin
+ if curve = NIL then
+ Exit;
-{--------------------------------------------------------------------------}
-destructor TInvControl.Destroy;
+ // If VOLTWATT control mode then check for any negative watt values (pu)
+ // and values greater than 1.0 per-unit (=100 percent output)
+ if InvControlMode = VOLTWATT then
+ begin
+ for i := 1 to curve.NumPoints do
+ begin
+ if (curve.YValue_pt[i] < 0.0) or (curve.YValue_pt[i] > 1.0) then
+ begin
+ DoSimpleMsg(DSS, 'XY Curve object: "%s" has active power value(s) greater than 1.0 per-unit or less than 0.0 per-unit. Not allowed for VOLTWATT control mode for PVSystems', [curve.Name], 381);
+ curve := NIL;
+ Break;
+ end;
+ end;
+ end;
+end;
-Begin
-
- Inherited Destroy;
-End;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-PROCEDURE TInvControl.DefineProperties;
-Begin
-
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
-
- PropertyName[1] := 'PVSystemList';
- PropertyName[2] := 'Mode';
- PropertyName[3] := 'CombiMode';
- PropertyName[4] := 'vvc_curve1';
- PropertyName[5] := 'hysteresis_offset';
- PropertyName[6] := 'voltage_curvex_ref';
- PropertyName[7] := 'avgwindowlen';
-
- PropertyName[8] := 'voltwatt_curve';
-
- //following for dynamic reactive current mode
- PropertyName[9] := 'DbVMin';
- PropertyName[10] := 'DbVMax';
- PropertyName[11] := 'ArGraLowV';
- PropertyName[12] := 'ArGraHiV';
- PropertyName[13] := 'DynReacavgwindowlen';
- PropertyName[14] := 'DeltaQ_factor';
- PropertyName[15] := 'VoltageChangeTolerance';
- PropertyName[16] := 'VarChangeTolerance';
- PropertyName[17] := 'VoltwattYAxis';
- PropertyName[18] := 'RateofChangeMode';
- PropertyName[19] := 'LPFTau';
- PropertyName[20] := 'RiseFallLimit';
- PropertyName[21] := 'DeltaP_factor';
- PropertyName[22] := 'EventLog';
- PropertyName[23] := 'VV_RefReactivePower';
- PropertyName[24] := 'ActivePChangeTolerance';
-
- PropertyHelp[1] := 'Array list of PVSystems to be controlled. Usually only one PVSystem is controlled by one InvControl. '+CRLF+CRLF+
- 'If not specified, all PVSystems in the circuit are assumed to be controlled by this control, only. ' +CRLF+CRLF+
- ' No capability of hierarchical control between two controls for a single PVSystem is implemented at this time.';
-
- PropertyHelp[2] := 'Mode with which the InvControl will control the PVSystem(s) specified in PVSystemList. '+CRLF+CRLF+
- 'Must be one of: {'', VOLTVAR* | VOLTWATT | DYNAMICREACCURR} ' +
- CRLF+CRLF+'Default is volt-var control mode. If the user desires to use modes simultaneously, then set the CombiMode property. Setting the Mode to any valid value disables combination mode'+
- CRLF+CRLF+'In volt-var mode (Default), the control attempts to dispatch the vars according to one or two volt-var curves, depending on the local terminal voltage, present active power output, and the capabilities of the PVSystem. ' +
- CRLF+CRLF+'In volt-watt mode , the control attempts to dispatch the watts according to one defined volt-watt curve, depending on the local terminal voltage and the capabilities of the PVSystem. '+
- CRLF+CRLF+'In dynamic reactive current mode, the control attempts to increasingly counter deviations outside the deadband (around nominal, or average) by injecting increasing amounts of inductive or capacitive vars, within the capabilities of the PVSystem.';
-
-
-
- PropertyHelp[3] := 'Combination of Modes with which the InvControl will control the PVSystem(s) specified in PVSystemList. '+CRLF+CRLF+
- 'Must be a combination of the following: {VV_VW | VV_DRC}. Default is to not set this property, in which case the single control mode in Mode is active. ' +
- CRLF+CRLF+'In combined VV_VW mode, both volt-var and volt-watt control modes are active simultaneously. See help individually for volt-var mode and volt-watt mode in Mode property.'+
- CRLF+'Note that the PVSystem will attempt to achieve both the volt-watt and volt-var set-points based on the capabilities of the inverter in the PVSystem (kVA rating), any limits set on maximum active power,' +
- CRLF+', any limits set on maximum reactive power. '+
- CRLF+'Precedence will be given to either watt production or var production based on the setting of VV_RefReactivePower.'+
- CRLF+CRLF+'In combined VV_DRC, both the volt-var and the dynamic reactive current modes are simultaneously active.' +
- CRLF+CRLF+'The volt-var function will attempt to achieve its set-point based on the volt-var curve, and present voltage. The dynamic '+
- CRLF+'reactive power mode function will also be active and it will add or subtract from the reactive power set-point desired by the volt-var function.'+
- CRLF+'Note that the precedence of active and reactive power production is defined by the VV_RefReactivePower property. In no event will the reactive '+
- CRLF+'power exceed the maximum var limit of the PVSystem, and the combination of the active and reactive power output will not exceed the kVA rating of '+
- CRLF+'the inverter (set in the PVSystem).';
-
- PropertyHelp[4] := 'Required for VOLTVAR mode. '+CRLF+CRLF+
- 'The name of an XYCurve object that describes the variation in var output (as per unit of available vars, given present active power output and the capabilities of the PVSystem). '+CRLF+CRLF+
- 'Units for the x-axis are per-unit voltage, which may be in per unit of the rated voltage for the PVSystem, or may be in per unit of the average voltage at the terminals over a user-defined number of prior solutions. '+CRLF+CRLF+
- 'Units for the y-axis are in per-unit available desired vars, corresponding to the terminal voltage (x-axis value in per unit). The per-unit available vars depends on the kva rating of the PVSystem as well as the present '+
- 'output of active power. '+CRLF+CRLF+
- 'Must be specified for VOLTVAR mode.';
-
- PropertyHelp[5] := 'Required for VOLTVAR mode, and defaults to 0. '+CRLF+CRLF+
- 'For the times when the terminal voltage is decreasing, this is the off-set in per-unit voltage of a curve whose shape is the same as vvc_curve. '+
- 'It is offset by a certain negative value of per-unit voltage, which is defined by the base quantity for the x-axis of the volt-var curve (see help for voltage_curvex_ref)'+CRLF+CRLF+
- 'If the PVSystem terminal voltage has been increasing, and has not changed directions, utilize vvc_curve1 for the volt-var response. '+CRLF+CRLF+
- 'If the PVSystem terminal voltage has been increasing and changes directions and begins to decrease, then move from utilizing vvc_curve1 to a volt-var curve of the same shape, but offset by a certain per-unit voltage value. '+CRLF+CRLF+
- 'Maintain the same per-unit available var output level (unless head-room has changed due to change in active power or kva rating of PVSystem). Per-unit var values remain the same for this internally constructed second curve (hysteresis curve). '+CRLF+CRLF+
- 'If the PVSystem terminal voltage has been decreasing and changes directions and begins to increase , then move from utilizing the offset curve, back to the vvc_curve1 for volt-var response, but stay at the same per-unit available vars output level.';
-
- PropertyHelp[6] := 'Required for VOLTVAR and VOLTWATT modes, and defaults to rated. Possible values are: {rated|avg|ravg}. '+CRLF+CRLF+
- 'Defines whether the x-axis values (voltage in per unit) for vvc_curve1 and the volt-watt curve corresponds to:'+CRLF+CRLF+
- 'rated: The rated voltage for the PVSystem object (1.0 in the volt-var curve equals rated voltage)'+CRLF+CRLF+
- 'avg: The average terminal voltage recorded over a certain number of prior power-flow solutions.'+CRLF+
- ' With the avg setting, 1.0 per unit on the x-axis of the volt-var curve(s) corresponds to the average voltage'+CRLF+
- ' from a certain number of prior intervals. See avgwindowlen parameter.'+CRLF+CRLF+
- 'ravg: Same as avg, with the exception that the avgerage terminal voltage is divided by the rated voltage.';
-
- PropertyHelp[7] := 'Required for VOLTVAR mode and VOLTWATT mode, and defaults to 0 seconds (0s). '+CRLF+CRLF+
- 'Sets the length of the averaging window over which the average PVSystem terminal voltage is calculated. '+CRLF+CRLF+
- 'Units are indicated by appending s, m, or h to the integer value. '+CRLF+CRLF+
- 'The averaging window will calculate the average PVSystem terminal voltage over the specified period of time, up to and including the last power flow solution. '+CRLF+CRLF+
- 'Note, if the solution stepsize is larger than the window length, then the voltage will be assumed to have been constant over the time-frame specified by the window length.';
-
- PropertyHelp[8] := 'Required for VOLTWATT mode. '+CRLF+CRLF+
- 'The name of an XYCurve object that describes the variation in active power output (in per unit of maximum active power outut for the PVSystem). '+CRLF+CRLF+
- 'Units for the x-axis are per-unit voltage, which may be in per unit of the rated voltage for the PVSystem, or may be in per unit of the average voltage at the terminals over a user-defined number of prior solutions. '+CRLF+CRLF+
- 'Units for the y-axis are either in: (1) per unit of maximum active power output capability of the PVSystem, or (2) maximum available active power output capability (defined by the parameter: VoltwattYAxis), '+
- 'corresponding to the terminal voltage (x-axis value in per unit). '+CRLF+CRLF+
- 'No default -- must be specified for VOLTWATT mode.';
-
- PropertyHelp[9] := 'Required for the dynamic reactive current mode (DYNAMICREACCURR), and defaults to 0.95 per-unit voltage (referenced to the PVSystem object rated voltage or a windowed average value). '+CRLF+CRLF+
- 'This parameter is the minimum voltage that defines the voltage dead-band within which no reactive power is allowed to be generated. ';
-
- PropertyHelp[10] := 'Required for the dynamic reactive current mode (DYNAMICREACCURR), and defaults to 1.05 per-unit voltage (referenced to the PVSystem object rated voltage or a windowed average value). '+CRLF+CRLF+
- 'This parameter is the maximum voltage that defines the voltage dead-band within which no reactive power is allowed to be generated. ';
-
- PropertyHelp[11] := 'Required for the dynamic reactive current mode (DYNAMICREACCURR), and defaults to 0.1 '+CRLF+CRLF+
- 'This is a gradient, expressed in unit-less terms of %/%, to establish the ratio by which percentage capacitive reactive power production is increased as the percent delta-voltage decreases below DbVMin. '+CRLF+CRLF+
- 'Percent delta-voltage is defined as the present PVSystem terminal voltage minus the moving average voltage, expressed as a percentage of the rated voltage for the PVSystem object. '+CRLF+CRLF+
- 'Note, the moving average voltage for the dynamic reactive current mode is different than the mmoving average voltage for the volt-watt and volt-var modes.';
-
- PropertyHelp[12] := 'Required for the dynamic reactive current mode (DYNAMICREACCURR), and defaults to 0.1 '+CRLF+CRLF+
- 'This is a gradient, expressed in unit-less terms of %/%, to establish the ratio by which percentage inductive reactive power production is increased as the percent delta-voltage decreases above DbVMax. '+CRLF+CRLF+
- 'Percent delta-voltage is defined as the present PVSystem terminal voltage minus the moving average voltage, expressed as a percentage of the rated voltage for the PVSystem object. '+CRLF+CRLF+
- 'Note, the moving average voltage for the dynamic reactive current mode is different than the mmoving average voltage for the volt-watt and volt-var modes.';
-
- PropertyHelp[13] := 'Required for the dynamic reactive current mode (DYNAMICREACCURR), and defaults to 1 seconds (1s). Do not use a value smaller than 1.0 '+CRLF+CRLF+
- 'Sets the length of the averaging window over which the average PVSystem terminal voltage is calculated '+
- 'for the dynamic reactive current mode. '+CRLF+CRLF+
- 'Units are indicated by appending s, m, or h to the integer value. '+CRLF+CRLF+
- 'Typically this will be a shorter averaging window than the volt-var and volt-watt averaging window.'+CRLF+CRLF+
- 'The averaging window will calculate the average PVSystem terminal voltage over the specified period of time, up to and including the last power flow solution. Note, if the solution stepsize is larger than '+
- 'the window length, then the voltage will be assumed to have been constant over the time-frame specified by the window length.';
-
- PropertyHelp[14] := 'Required for the VOLTVAR and DYNAMICREACCURR modes. Defaults to 0.7. '+CRLF+CRLF+
- 'Sets the maximum change (in per unit) from the prior var output level to the desired var output level during each control iteration. '+CRLF+CRLF+CRLF+
- 'If numerical instability is noticed in solutions such as var sign changing from one control iteration to the next and voltages oscillating between two values with some separation, '+
- 'this is an indication of numerical instability (use the EventLog to diagnose). '+CRLF+CRLF+
- 'If the maximum control iterations are exceeded, and no numerical instability is seen in the EventLog of via monitors, then try increasing the value of this parameter to reduce the number '+
- 'of control iterations needed to achieve the control criteria, and move to the power flow solution.';
-
- PropertyHelp[15] := 'Required for VOLTVAR and DYNAMICREACCURR modes. Defaults to 0.0001 per-unit voltage. This parameter should only be modified by advanced users of '+
- 'the InvControl under these modes. '+CRLF+CRLF+
- 'This is the change in voltage from one control iteration solution to the next that is one determining '+
- 'parameter to stop additional control iterations. '+CRLF+CRLF+
- 'This is the difference between the present per-unit voltage at the '+
- 'terminals of the PVSystem and the prior control iteration PVSystem terminal voltage(s) (in per unit), as an absolute value (without sign). '+CRLF+CRLF+
- 'This voltage tolerance value plus the var tolerance value (VarChangeTolerance) determine, together, when to stop control iterations by the '+
- 'InvControl. '+CRLF+CRLF+
- 'If an InvControl is controlling more than one PVSystem, each PVSystem has this quantity calculated independently, and so an individual '+
- 'PVSystem may reach the tolerance within different numbers of control iterations.';
-
- PropertyHelp[16] := 'Required for VOLTVAR and DYNAMICREACCURR modes. Defaults to 0.025 per unit of available vars (for VOLTVAR mode) and 0.025 per unit of the inverter '+
- 'full steady-state current rating (at rated voltage), which is the kva rating (for DYNAMICREACCURR mode). '+CRLF+CRLF+
- 'This parameter should only be modified by advanced users of the InvControl under these modes. '+CRLF+CRLF+
- 'This is the change in vars from one control iteration solution to the next that is one determining '+
- 'parameter to stop additional control iterations. '+CRLF+CRLF+
- 'This is the difference between the desired target vars (in per-unit) of the PVSystem '+
- 'and the present reactive power output (in per unit), as an absolute value (without sign). '+CRLF+CRLF+
- 'This reactive power tolerance value plus the voltage tolerance value (VarChangeTolerance) determine, together, when to stop control iterations by the '+
- 'InvControl. '+CRLF+CRLF+
- 'If an InvControl is controlling more than one PVSystem, each PVSystem has this quantity calculated independently, and so an individual '+
- 'PVSystem may reach the tolerance within different numbers of control iterations.';
-
- PropertyHelp[17] := 'Required for VOLTWATT mode. Must be one of: {PMPPPU* | PAVAILABLEPU| PCTPMPPPU}. The default is PMPPPU. '+CRLF+CRLF+
- 'Units for the y-axis of the volt-watt curve while in volt-watt mode. '+CRLF+CRLF+
- 'When set to PMPPPU the y-axis for the volt-watt curve is understood to be in per unit of the full active power output capability of the PVSystem, which is Pmpp. '+CRLF+CRLF+
- 'When set to PAVAILABLEPU the y-axis for the volt-watt curve is understood to be in per unit of available power at any given time, given Pmpp rating, '+
- 'efficiency factor of the PVSystem, and present irradiance.'+CRLF+CRLF+
- 'When set to PCTPMPPPU the y-axis for the volt-watt curve is understood to be in per unit of the Pmpp rating times the pctPmpp defined in the PVSystem. ';
-
-
- PropertyHelp[18] := 'Required for VOLTWATT and VOLTVAR mode. Must be one of: {INACTIVE* | LPF | RISEFALL }. The default is INACTIVE. '+CRLF+CRLF+
- 'Defines the rate of change mode for VOLTWATT and VOLTVAR control modes. '+CRLF+CRLF+
- 'INACTIVE indicates there is no limit on rate of change imposed for either active or reactive power output. '+CRLF+CRLF+
- 'Note: DeltaQ_factor still applies to VOLTVAR control mode. '+CRLF+CRLF+
- 'LPF indicates a low-pass RC filter is applied to the desired power output level to determine the power output level '+
- 'as a function of a time constant, tau. '+CRLF+CRLF+
- 'RISEFALL indicates a rise and fall limit in the change of active or reactive power expressed in terms of per-unit power per second. '+CRLF+CRLF+
- 'For VOLTVAR mode the rise/fall limit is in terms (per-unit QAvailable)/second. '+CRLF+CRLF+
- 'For VOLTWATT mode the rise/fall limit is either in terms of (per-unit Pmpp)/second or (per-unit WAvailable)/second depending on the setting '+
- 'of the parameter VoltwattYAxis.';
-
-
- PropertyHelp[19] := 'Not required. Defaults to 0 seconds. '+CRLF+CRLF+
- 'If RateofChangeMode equals LPF or COMBINED, this defines the time constant in seconds for a low pass filter '+
- 'that limits the rate of change in input/output for VOLTWATT or VOLTVAR control modes. '+CRLF+CRLF+
- 'The time constant will cause the low-pass filter to achieve 95% of the target value in 3 time constants. '+CRLF;
-
- PropertyHelp[20] := 'Not required. Defaults to no limit (-1). Must be -1 (no limit) or a positive value. '+CRLF+CRLF+
- 'Defines the rise/fall rate of change limit in per-unit power per second for VOLTWATT or VOLTVAR control modes. '+CRLF+CRLF+
- 'For VOLTWATT mode, when the y-axis for the volt-watt curve is in units of PMPPPU, then the units of this number are in: per-unit Pmpp/second. '+CRLF+CRLF+
- 'For VOLTWATT mode, when the y-axis for the volt-watt curve is in units of PAVAILABLEPU, then the units of this number are in: per-unit WAvailable/second. '+CRLF+CRLF+
- 'For VOLTVAR mode, the units for this number are in per-unit of the y-axis quantity of VV_RefReactivePower/second.'+CRLF+CRLF+
- 'Note: Set to -1 to disable the rise/fall limit. Otherwise, set it to a positive value for both rise limit and fall limit. ';
-
- PropertyHelp[21] := 'Required for the VOLTWATT modes. Defaults to 1.0. '+CRLF+CRLF+
- 'Sets the maximum change (in unit of the y-axis) from the prior active power output level to the desired active power output level during each control iteration. '+CRLF+CRLF+CRLF+
- 'If numerical instability is noticed in solutions such as active power changing substantially from one control iteration to the next and/or voltages oscillating between two values with some separation, '+
- 'this is an indication of numerical instability (use the EventLog to diagnose). '+CRLF+CRLF+
- 'If the maximum control iterations are exceeded, and no numerical instability is seen in the EventLog of via monitors, then try increasing the value of this parameter to reduce the number '+
- 'of control iterations needed to achieve the control criteria, and move to the power flow solution.';
-
- PropertyHelp[22] := '{Yes/True* | No/False} Default is YES for InvControl. Log control actions to Eventlog.';
-
- PropertyHelp[23] := 'Required for any mode that has VOLTVAR in it. Defaults to VARAVAL_WATTS. Possible Settings: VARAVAL_WATTS|VARMAX_VARS|VARMAX_WATTS'+CRLF+CRLF+
- 'When the VOLTVAR mode is active (alone or in conjunction with other modes, this property defines the reference for the percent value given on the y-axis of the volt-var curve.'+CRLF+CRLF+
- 'VARAVAL_WATTS: When set to VARAVAL_WATTS the units of the y-axis for the volt-var curve are given in percent of available reactive power given present active power output and the kVA rating of the PVSystem.'+CRLF+
- 'Active power output is given precedence over reactive power output/absorption, so the reactive power output/absorption possibly may not achieve the desired available reactive power level as defined by the volt-var curve if little headroonm.'+CRLF+CRLF+
- 'VARMAX_VARS: When set to VARMAX_VARS the units of the y-axis for the volt-var curve are given in percent of the maximum reactive power setting of each of the PVSystems. Reactive power generation/absorption has'+CRLF+
- 'precedence over active power generation.'+CRLF+CRLF+
- 'VARMAX_WATTS: When set to VARMAX_WATTS the units of the y-axis for the volt-var curve are given in percent of the maximum reactive power setting of each of the PVSystems. Active power generation has'+CRLF+
- 'precedence over reactive power generation/absorption.'+CRLF;
-
- PropertyName[24] := 'ActivePChangeTolerance is the active power tolerance required to be met between control iterations to signal achieving convergence. Default is 0.01';
-
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
-End;
-
-{--------------------------------------------------------------------------}
-FUNCTION TInvControl.NewObject(const ObjName:AnsiString):Integer;
-Begin
- // Make a new InvControl and add it to InvControl class list
- WITH ActiveCircuit Do
- Begin
- ActiveCktElement := TInvControlObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- End;
-End;
-
-{--------------------------------------------------------------------------}
-FUNCTION TInvControl.Edit:Integer;
-VAR
- ParamPointer:Integer;
- ParamName:AnsiString;
- Param:AnsiString;
-
-
-Begin
-
- // continue parsing WITH contents of Parser
- DSS.ActiveInvControlObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveInvControlObj;
-
- Result := 0;
-
- WITH DSS.ActiveInvControlObj Do Begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- WHILE Length(Param)>0 Do Begin
- IF Length(ParamName) = 0 THEN Inc(ParamPointer)
- ELSE ParamPointer := CommandList.GetCommand(ParamName);
-
- If (ParamPointer>0) and (ParamPointer<=NumProperties)
- THEN PropertyValue[ParamPointer]:= Param;
-
- CASE ParamPointer OF
- 0: DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name +'.'+ Name + '"', 364);
- 1: InterpretTStringListArray(Param, FPVSystemNameList);
- 2: Begin
- If CompareTextShortest(Parser.StrValue, 'voltvar')= 0 Then
- Begin
- ControlMode := 'VOLTVAR';
- CombiControlMode := '';
- End
- Else If CompareTextShortest(Parser.StrValue, 'voltwatt')= 0 Then
- Begin
- ControlMode := 'VOLTWATT';
- CombiControlMode := '';
- End
- Else If CompareTextShortest(Parser.StrValue, 'dynamicreaccurr')= 0 Then
- Begin
- ControlMode := 'DYNAMICREACCURR';
- CombiControlMode := '';
- End
- Else If CompareTextShortest(Parser.StrValue, 'fixedpf')= 0 Then
- Begin
- ControlMode := 'FIXEDPF';
- CombiControlMode := '';
- End
- Else
- Begin
- if ControlMode = '' then
- DoSimpleMsg('Invalid Control Mode selected', 1366);
- CombiControlMode := '';
- DSS.SolutionAbort := True;
- exit;
- End;
-
- End;
-
- 3: Begin
- If CompareTextShortest(Parser.StrValue, 'vv_vw')= 0 Then
- Begin
- ControlMode := '';
- CombiControlMode := 'VV_VW';
- End
- Else If CompareTextShortest(Parser.StrValue, 'vv_drc')= 0 Then
- Begin
- ControlMode := '';
- CombiControlMode := 'VV_DRC';
- End
- Else
- Begin
- if CombiControlMode = '' then
- DoSimpleMsg('Invalid CombiControl Mode selected', 1367);
- CombiControlMode := '';
- DSS.SolutionAbort := True;
- exit;
- End;
-
-
- End;
-
-
- 4: Begin
- Fvvc_curvename := Parser.StrValue;
- if Length(Fvvc_curvename) > 0 then
- begin
- Fvvc_curve := GetXYCurve(Fvvc_curvename, 'VOLTVAR');
- Fvvc_curve_size := Fvvc_curve.NumPoints;
- end;
- End;
- 5: Begin
- if(Parser.DblValue > 0.0) THEN DoSimpleMsg('Hysteresis offset should be a negative value, or 0 "' + ParamName + '" for Object "' + Class_Name +'.'+ Name + '"', 1364)
- else
- Fvvc_curveOffset := Parser.DblValue;
- End;
-
- 6: Begin
- If CompareTextShortest(Parser.StrValue, 'rated') = 0 then FVoltage_CurveX_ref := 0
- Else If CompareTextShortest(Parser.StrValue, 'avg')= 0 then FVoltage_CurveX_ref := 1
- Else If CompareTextShortest(Parser.StrValue, 'ravg')= 0 then FVoltage_CurveX_ref := 2
- End;
-
- 7: FRollAvgWindowLength := InterpretAvgVWindowLen(Param);
- 8: Begin
- Fvoltwatt_curvename := Parser.StrValue;
- if Length(Fvoltwatt_curvename) > 0 then
- begin
- Fvoltwatt_curve := GetXYCurve(Fvoltwatt_curvename, 'VOLTWATT');
- Fvoltwatt_curve_size := Fvoltwatt_curve.NumPoints;
- end;
- End;
- 9: Begin
- FDbVMin := Parser.DblValue;
- if(FDbVMax > 0.0) and (FDbVmin > FDbVMax) then
- begin
- DoSimpleMsg('Minimum dead-band voltage value should be less than the maximum dead-band voltage value. Value set to 0.0 "' + ParamName + '" for Object "' + Class_Name +'.'+ Name + '"', 1365);
- FDbvMin := 0.0;
- end;
- End;
- 10: Begin
- FDbVMax := Parser.DblValue;
- if(FDbVMin > 0.0) and (FDbVMax < FDbVmin) then
- begin
- DoSimpleMsg('Maximum dead-band voltage value should be greater than the minimum dead-band voltage value. Value set to 0.0 "' + ParamName + '" for Object "' + Class_Name +'.'+ Name + '"', 1366);
- FDbvMax := 0.0;
- end;
- End;
-
- 11: FArGraLowV := Parser.DblValue;
- 12: FArGraHiV := Parser.DblValue;
- 13: FDRCRollAvgWindowLength := InterpretDRCAvgVWindowLen(Param);
- 14: FdeltaQ_factor := Parser.DblValue;
- 15: FVoltageChangeTolerance := Parser.DblValue;
- 16: FVarChangeTolerance := Parser.DblValue;
- 17: Begin
- If CompareTextShortest(Parser.StrValue, 'pmpppu')= 0 Then FVoltwattYAxis := 1
- Else If CompareTextShortest(Parser.StrValue, 'pavailablepu')= 0 Then FVoltwattYAxis := 0
- Else If CompareTextShortest(Parser.StrValue, 'pctpmpppu')= 0 Then FVoltwattYAxis := 2
- End;
-
- 18: Begin
- If CompareTextShortest(Parser.StrValue, 'inactive')= 0 Then RateofChangeMode := INACTIVE
- Else If CompareTextShortest(Parser.StrValue, 'lpf')= 0 Then RateofChangeMode := LPF
- Else If CompareTextShortest(Parser.StrValue, 'risefall')= 0 Then RateofChangeMode := RISEFALL
- End;
-
- 19: Begin
- If Parser.DblValue > 0 then FLPFTau := Parser.DblValue
- else RateofChangeMode := INACTIVE;
- End;
- 20: Begin
- If Parser.DblValue > 0 then FRiseFallLimit := Parser.DblValue
- else RateofChangeMode := INACTIVE;
- End;
- 21: FdeltaP_factor := Parser.DblValue;
- 22: ShowEventLog := InterpretYesNo(param);
- 23: Begin
- If CompareTextShortest(Parser.StrValue, 'varaval_watts')= 0 Then FVV_ReacPower_ref := 'VARAVAL_WATTS'
- Else If CompareTextShortest(Parser.StrValue, 'varmax_vars')= 0 Then FVV_ReacPower_ref := 'VARMAX_VARS'
- Else If CompareTextShortest(Parser.StrValue, 'varmax_watts')= 0 Then FVV_ReacPower_ref := 'VARMAX_WATTS'
- End;
- 24: FActivePChangeTolerance := Parser.DblValue;
-
- ELSE
- // Inherited parameters
- ClassEdit( DSS.ActiveInvControlObj, ParamPointer - NumPropsthisClass)
- End;
-
- CASE ParamPointer OF
- 1: Begin // re-alloc based on
- FPVSystemPointerList.Clear; // clear this for resetting on first sample
- FListSize := FPVSystemNameList.count;
- End;
- ELSE
-
- END;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- End;
-
- RecalcElementData;
- End;
-
-End;
-
-
-
-{--------------------------------------------------------------------------}
-FUNCTION TInvControl.MakeLike(const InvControlName:AnsiString):Integer;
-VAR
- OtherInvControl:TInvControlObj;
- i, j:Integer;
-Begin
- Result := 0;
- {See if we can find this InvControl name in the present collection}
- OtherInvControl := Find(InvControlName);
- IF OtherInvControl<>Nil THEN
- WITH DSS.ActiveInvControlObj Do Begin
-
- NPhases := OtherInvControl.Fnphases;
- NConds := OtherInvControl.Fnconds; // Force Reallocation of terminal stuff
-
- for i := 1 to FPVSystemPointerList.Count DO
- begin
-
- ControlledElement[i] := OtherInvControl.ControlledElement[i];
-
- FkWLimit[i] := OtherInvControl.FkWLimit[i];
- FkvarLimit[i] := OtherInvControl.FkvarLimit[i];
- FkVALimit[i] := OtherInvControl.FkVALimit[i];
- FVref[i] := OtherInvControl.FVref[i];
- FPpf[i] := OtherInvControl.FPpf[i];
- Fpresentkvar[i] := OtherInvControl.Fpresentkvar[i];
- FpresentkW[i] := OtherInvControl.FpresentkW[i];
-
- CondOffset[i] := OtherInvControl.CondOffset[i];
- FWithinTol[i] := OtherInvControl.FWithinTol[i];
- FWithinTolVV[i] := OtherInvControl.FWithinTolVV[i];
- FWithinTolVW[i] := OtherInvControl.FWithinTolVW[i];
- FROCEvaluated[i] := OtherInvControl.FROCEvaluated[i];
- FFinalpuPmpp[i] := OtherInvControl.FFinalpuPmpp[i];
- FFinalkvar[i] := OtherInvControl.FFinalkvar[i];
- FHitkVALimit[i] := OtherInvControl.FHitkVALimit[i];
- FHitkvarLimit[i] := OtherInvControl.FHitkvarLimit[i];
-
- end;
-
- ControlMode := OtherInvControl.ControlMode;
- CombiControlMode := OtherInvControl.CombiControlMode;
- FListSize := OtherInvControl.FListSize;
- Fvvc_curve_size := OtherInvControl.Fvvc_curve_size;
- Fvvc_curve := OtherInvControl.Fvvc_curve;
- Fvvc_curvename := OtherInvControl.Fvvc_curvename;
- Fvvc_curveOffset := OtherInvControl.Fvvc_curveOffset;
- FVoltage_CurveX_ref := OtherInvControl.FVoltage_CurveX_ref;
- FDRCVAvgWindowLengthSec := OtherInvControl.FDRCVAvgWindowLengthSec;
- FVAvgWindowLengthSec := OtherInvControl.FVAvgWindowLengthSec;
- Fvoltwatt_curve_size := OtherInvControl.Fvoltwatt_curve_size;
- Fvoltwatt_curve := OtherInvControl.Fvoltwatt_curve;
- Fvoltwatt_curvename := OtherInvControl.Fvoltwatt_curvename;
- FDbVMin := OtherInvControl.FDbVMin;
- FDbVMax := OtherInvControl.FDbVMax;
- FArGraLowV := OtherInvControl.FArGraLowV;
- FArGraHiV := OtherInvControl.FArGraHiV;
- FActiveVVCurve := OtherInvControl.FActiveVVCurve;
- FRollAvgWindowLength := OtherInvControl.FRollAvgWindowLength;
- FRollAvgWindowLengthIntervalUnit := OtherInvControl.FRollAvgWindowLengthIntervalUnit;
- FDRCRollAvgWindowLength := OtherInvControl.FDRCRollAvgWindowLength;
- FDRCRollAvgWindowLengthIntervalUnit := OtherInvControl.FDRCRollAvgWindowLengthIntervalUnit;
- FActivePChangeTolerance := OtherInvControl.FActivePChangeTolerance;
- FvoltwattDeltaVTolerance := OtherInvControl.FvoltwattDeltaVTolerance;
- FdeltaQ_factor := OtherInvControl.FdeltaQ_factor;
- FdeltaP_factor := OtherInvControl.FdeltaP_factor;
- FVoltageChangeTolerance := OtherInvControl.FVoltageChangeTolerance;
- FVarChangeTolerance := OtherInvControl.FVarChangeTolerance;
- FVoltwattYAxis := OtherInvControl.FVoltwattYAxis;
- RateofChangeMode := OtherInvControl.RateofChangeMode;
- FLPFTau := OtherInvControl.FLPFTau;
- FRiseFallLimit := OtherInvControl.FRiseFallLimit;
-
-
- TimeDelay := OtherInvControl.TimeDelay;
- For j := 1 to ParentClass.NumProperties Do PropertyValue[j] := OtherInvControl.PropertyValue[j];
-
- End
- ELSE DoSimpleMsg('Error in InvControl MakeLike: "' + InvControlName + '" Not Found.', 370);
-
-End;
-
-
-
-{==========================================================================}
-{ TInvControlObj }
-{==========================================================================}
-
-
-
-{--------------------------------------------------------------------------}
-constructor TInvControlObj.Create(ParClass:TDSSClass; const InvControlName:AnsiString);
-
-Begin
- Inherited Create(ParClass);
- Name := LowerCase(InvControlName);
- DSSObjType := ParClass.DSSClassType;
-
- ElementName := '';
+procedure TInvControlObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
+begin
+ case Idx of
+ 1:
+ begin // re-alloc based on
+ FPVSystemPointerList.Clear; // clear this for resetting on first sample
+ FListSize := FPVSystemNameList.count;
+ end;
+ 4:
+ ValidateXYCurve(DSS, Fvvc_curve, VOLTVAR);
+ 8:
+ ValidateXYCurve(DSS, Fvoltwatt_curve, VOLTWATT);
+ 9:
+ if (FDbVMax > 0.0) and (FDbVmin > FDbVMax) then
+ begin
+ DoSimpleMsg('Minimum dead-band voltage value should be less than the maximum dead-band voltage value. Value set to 0.0 "%s" for object "%s"', [ParentClass.PropertyName[Idx], FullName], 1365);
+ FDbvMin := 0.0;
+ end;
+ 10:
+ if (FDbVMin > 0.0) and (FDbVMax < FDbVmin) then
+ begin
+ DoSimpleMsg('Maximum dead-band voltage value should be greater than the minimum dead-band voltage value. Value set to 0.0 "%s" for object "%s"', [ParentClass.PropertyName[Idx], FullName], 1366);
+ FDbvMax := 0.0;
+ end;
+ ord(TProp.Mode):
+ CombiMode := NONE_COMBMODE;
+ ord(TProp.CombiMode):
+ ControlMode := NONE_MODE;
+ ord(TProp.LPFTau):
+ if FLPFTau <= 0 then
+ RateofChangeMode := INACTIVE;
+ ord(TProp.RiseFallLimit):
+ if FRiseFallLimit <= 0 then
+ RateofChangeMode := INACTIVE;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
+
+procedure TInvControlObj.MakeLike(OtherPtr: Pointer);
+var
+ Other: TObj;
+ i: Integer;
+begin
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
+
+ for i := 1 to FPVSystemPointerList.Count do
+ begin
+ ControlledElement[i] := Other.ControlledElement[i];
+
+ FkWLimit[i] := Other.FkWLimit[i];
+ FkvarLimit[i] := Other.FkvarLimit[i];
+ FkVALimit[i] := Other.FkVALimit[i];
+ FVref[i] := Other.FVref[i];
+ FPpf[i] := Other.FPpf[i];
+ Fpresentkvar[i] := Other.Fpresentkvar[i];
+ FpresentkW[i] := Other.FpresentkW[i];
+
+ CondOffset[i] := Other.CondOffset[i];
+ FWithinTol[i] := Other.FWithinTol[i];
+ FWithinTolVV[i] := Other.FWithinTolVV[i];
+ FWithinTolVW[i] := Other.FWithinTolVW[i];
+ FROCEvaluated[i] := Other.FROCEvaluated[i];
+ FFinalpuPmpp[i] := Other.FFinalpuPmpp[i];
+ FFinalkvar[i] := Other.FFinalkvar[i];
+ FHitkVALimit[i] := Other.FHitkVALimit[i];
+ FHitkvarLimit[i] := Other.FHitkvarLimit[i];
+ end;
+ ControlMode := Other.ControlMode;
+ CombiMode := Other.CombiMode;
+ FListSize := Other.FListSize;
+ Fvvc_curve := Other.Fvvc_curve;
+ Fvvc_curveOffset := Other.Fvvc_curveOffset;
+ FVoltage_CurveX_ref := Other.FVoltage_CurveX_ref;
+ Fvoltwatt_curve := Other.Fvoltwatt_curve;
+ FDbVMin := Other.FDbVMin;
+ FDbVMax := Other.FDbVMax;
+ FArGraLowV := Other.FArGraLowV;
+ FArGraHiV := Other.FArGraHiV;
+ FActiveVVCurve := Other.FActiveVVCurve;
+ FRollAvgWindowLength := Other.FRollAvgWindowLength;
+ FDRCRollAvgWindowLength := Other.FDRCRollAvgWindowLength;
+ FActivePChangeTolerance := Other.FActivePChangeTolerance;
+ FvoltwattDeltaVTolerance := Other.FvoltwattDeltaVTolerance;
+ FdeltaQ_factor := Other.FdeltaQ_factor;
+ FdeltaP_factor := Other.FdeltaP_factor;
+ FVoltageChangeTolerance := Other.FVoltageChangeTolerance;
+ FVarChangeTolerance := Other.FVarChangeTolerance;
+ FVoltwattYAxis := Other.FVoltwattYAxis;
+ RateofChangeMode := Other.RateofChangeMode;
+ FLPFTau := Other.FLPFTau;
+ FRiseFallLimit := Other.FRiseFallLimit;
+ TimeDelay := Other.TimeDelay;
+end;
+
+constructor TInvControlObj.Create(ParClass: TDSSClass; const InvControlName: Ansistring);
+begin
+ inherited Create(ParClass);
+ Name := AnsiLowerCase(InvControlName);
+ DSSObjType := ParClass.DSSClassType;
{
Control elements are zero current sources that attach to a terminal of a
power-carrying device, but do not alter voltage or current flow.
@@ -805,2398 +517,2132 @@ constructor TInvControlObj.Create(ParClass:TDSSClass; const InvControlName:AnsiS
RecalcElementData routine if necessary. This allocates arrays for voltages
and currents and gives more direct access to the values,if needed
}
- NPhases := 3; // Directly set conds and phases
- Fnconds := 3;
- Nterms := 1; // this forces allocation of terminals and conductors
+ FNPhases := 3; // Directly set conds and phases
+ Fnconds := 3;
+ Nterms := 1; // this forces allocation of terminals and conductors
// in base class
- ControlMode := '';
- CombiControlMode := '';
- ControlledElement := nil;
- FkWLimit := nil;
- FkvarLimit := nil;
- FkVALimit := nil;
- FVref := nil;
- FPpf := nil;
- Fpresentkvar := nil;
- FpresentkW := nil;
- NPhasesPVSys := nil;
- NCondsPVSys := nil;
- FPVSystemNameList := nil;
- FPVSystemPointerList := nil;
- Fvvc_curve_size :=0;
- Fvvc_curve := nil;
- Fvvc_curvename := '';
- Fvvc_curveOffset := 0.0;
- Fvvc_curve2 := nil;
- FActiveVVCurve := nil;
- FVoltage_CurveX_ref := 0;
- FVAvgWindowLengthSec := 1.0;
- FDRCVAvgWindowLengthSec := 1.0;
- cBuffer := nil;
- CondOffset := nil;
- FPriorWattspu := nil;
- FPriorvarspu := nil;
- FLPFTime := nil;
- FRiseFallLimit := 0.001;
+ ControlMode := NONE_MODE;
+ CombiMode := NONE_COMBMODE;
+ ControlledElement := NIL;
+ FkWLimit := NIL;
+ FkvarLimit := NIL;
+ FkVALimit := NIL;
+ FVref := NIL;
+ FPpf := NIL;
+ Fpresentkvar := NIL;
+ FpresentkW := NIL;
+ NPhasesPVSys := NIL;
+ NCondsPVSys := NIL;
+ FPVSystemNameList := NIL;
+ FPVSystemPointerList := NIL;
+ Fvvc_curve := NIL;
+ Fvvc_curveOffset := 0.0;
+ FActiveVVCurve := NIL;
+ FVoltage_CurveX_ref := 0;
+ cBuffer := NIL;
+ CondOffset := NIL;
+ FPriorWattspu := NIL;
+ FPriorvarspu := NIL;
+ FLPFTime := NIL;
+ FRiseFallLimit := 0.001;
// following applicable to volt-watt and volt-var
- FRollAvgWindow := nil;
- FRollAvgWindowLength := 1;
+ FRollAvgWindow := NIL;
+ FRollAvgWindowLength := 1;
- FRollAvgWindowLengthIntervalUnit := 's';
- FDRCRollAvgWindow := nil;
- FDRCRollAvgWindowLength := 1;
- FDRCRollAvgWindowLengthIntervalUnit := 's';
+ FDRCRollAvgWindow := NIL;
+ FDRCRollAvgWindowLength := 1;
// volt-watt, only related variables
- Fvoltwatt_curve_size := 0;
- Fvoltwatt_curve := nil;
- Fvoltwatt_curvename := '';
- FAvgpVuPrior := nil;
- FPresentVpu := nil;
- FvoltwattDeltaVTolerance := 0.00001; // per-unit change in voltage tolerance
+ Fvoltwatt_curve := NIL;
+ FAvgpVuPrior := NIL;
+ FPresentVpu := NIL;
+ FvoltwattDeltaVTolerance := 0.00001; // per-unit change in voltage tolerance
// typically between a prior solution and the present solution
- FVVDeltaVtolerance := 0.00001;
- FPendingChange := nil;
- FFlagROCOnly := nil;
+ FVVDeltaVtolerance := 0.00001;
+ FPendingChange := NIL;
+ FFlagROCOnly := NIL;
// following apply to volt-var only
- QDeliver := nil;
- QNew := nil;
- QOld := nil;
- QOldVV := nil;
- QOldDRC := nil;
- QHeadRoom := nil;
- PNew := nil;
- POld := nil;
-
- QDRCNew := nil;
-
- FVpuSolution := nil;
- FVpuSolutionIdx := 0;
- FdeltaQ_factor := 0.7;
- FdeltaP_factor := 1.0;
- Qoutputpu := nil;
- QoutputVVpu := nil;
- QoutputDRCpu := nil;
- Qdesiredpu := nil;
- QDRCdesiredpu := nil;
- FVoltwattYAxis := 1;
- FVoltageChangeTolerance :=0.0001;
- FVarChangeTolerance :=0.025;
- FActivePChangeTolerance :=0.01;
- RateofChangeMode := INACTIVE;
- FLPFTau := 0.001;
-
- FlagChangeCurve := nil;
- FWithinTol := nil;
- FWithinTolVV := nil;
- FWithinTolVW := nil;
- FROCEvaluated := nil;
- FHitkVALimit := nil;
- FHitkvarLimit := nil;
-
- FPVSystemNameList := TSTringList.Create;
- FPVSystemPointerList := TDSSPointerList.Create(20); // Default size and increment
+ QDeliver := NIL;
+ QNew := NIL;
+ QOld := NIL;
+ QOldVV := NIL;
+ QOldDRC := NIL;
+ QHeadRoom := NIL;
+ PNew := NIL;
+ POld := NIL;
+
+ QDRCNew := NIL;
+
+ FVpuSolution := NIL;
+ FVpuSolutionIdx := 0;
+ FdeltaQ_factor := 0.7;
+ FdeltaP_factor := 1.0;
+ Qoutputpu := NIL;
+ QoutputVVpu := NIL;
+ QoutputDRCpu := NIL;
+ Qdesiredpu := NIL;
+ QDRCdesiredpu := NIL;
+ FVoltwattYAxis := 1;
+ FVoltageChangeTolerance := 0.0001;
+ FVarChangeTolerance := 0.025;
+ FActivePChangeTolerance := 0.01;
+ RateofChangeMode := INACTIVE;
+ FLPFTau := 0.001;
+
+ FlagChangeCurve := NIL;
+ FWithinTol := NIL;
+ FWithinTolVV := NIL;
+ FWithinTolVW := NIL;
+ FROCEvaluated := NIL;
+ FHitkVALimit := NIL;
+ FHitkvarLimit := NIL;
+
+ FPVSystemNameList := TSTringList.Create;
+ FPVSystemPointerList := TDSSPointerList.Create(20); // Default size and increment
//following for dynamic reactive current mode
- FDbVMin := 0.95;
- FDbVMax := 1.05;
- FArGraLowV := 0.1;
- FArGraHiV := 0.1;
- deltaVDynReac := nil;
- priorRollAvgWindow := nil;
- priorDRCRollAvgWindow := nil;
- FVV_ReacPower_ref :='VARAVAL_WATTS';
+ FDbVMin := 0.95;
+ FDbVMax := 1.05;
+ FArGraLowV := 0.1;
+ FArGraHiV := 0.1;
+ deltaVDynReac := NIL;
+ priorRollAvgWindow := NIL;
+ priorDRCRollAvgWindow := NIL;
+ FVV_ReacPower_ref := VARAVAL_WATTS;
- FFinalpuPmpp := nil;
- FFinalkvar := nil;
+ FFinalpuPmpp := NIL;
+ FFinalkvar := NIL;
//generic for control
- FPendingChange := nil;
- FFlagROCOnly := nil;
- InitPropertyValues(0);
-
-End;
+ FPendingChange := NIL;
+ FFlagROCOnly := NIL;
+end;
destructor TInvControlObj.Destroy;
-Begin
- ElementName := '';
- Finalize(ControlledElement);
- Finalize(FkWLimit);
- Finalize(FkvarLimit);
- Finalize(FkVALimit);
- Finalize(FVref);
- Finalize(FPpf);
- Finalize(Fpresentkvar);
- Finalize(FpresentkW);
- Finalize(NPhasesPVSys);
- Finalize(NCondsPVSys);
- Finalize(cBuffer);
- Finalize(CondOffset);
- Finalize(FRollAvgWindow);
- Finalize(FDRCRollAvgWindow);
-
- Finalize(FAvgpVuPrior);
- Finalize(FPresentVpu);
-
- Finalize(FPendingChange);
- Finalize(FFlagROCOnly);
- Finalize(QDeliver);
- Finalize(QNew);
- Finalize(QOld);
- Finalize(QOldVV);
- Finalize(QOldDRC);
- Finalize(QHeadroom);
- Finalize(Qoutputpu);
- Finalize(QoutputVVpu);
- Finalize(QoutputDRCpu);
- Finalize(Qdesiredpu);
- Finalize(QDRCdesiredpu);
- Finalize(QDRCNew);
- Finalize(PNew);
- Finalize(POld);
- Finalize(deltaVDynReac);
- Finalize(priorRollAvgWindow);
- Finalize(priorDRCRollAvgWindow);
- Finalize(FVpuSolution);
- Finalize(FlagChangeCurve);
- Finalize(FActiveVVCurve);
- Finalize(FPriorWattspu);
- Finalize(FPriorvarspu);
- Finalize(FLPFTime);
- Finalize(FWithinTol);
- Finalize(FWithinTolVV);
- Finalize(FWithinTolVW);
- Finalize(FROCEvaluated);
- Finalize(FFinalpuPmpp);
- Finalize(FFinalkvar);
- Finalize(FHitkVALimit);
- Finalize(FHitkvarLimit);
-
-
-
- Inherited Destroy;
-End;
-
-{--------------------------------------------------------------------------}
-PROCEDURE TInvControlObj.RecalcElementData;
-
-VAR
- i :Integer;
-
-Begin
-
- IF FPVSystemPointerList.Count = 0 Then MakePVSystemList;
-
- IF FPVSystemPointerList.Count > 0 Then
+begin
+ Finalize(ControlledElement);
+ Finalize(FkWLimit);
+ Finalize(FkvarLimit);
+ Finalize(FkVALimit);
+ Finalize(FVref);
+ Finalize(FPpf);
+ Finalize(Fpresentkvar);
+ Finalize(FpresentkW);
+ Finalize(NPhasesPVSys);
+ Finalize(NCondsPVSys);
+ Finalize(cBuffer);
+ Finalize(CondOffset);
+ Finalize(FRollAvgWindow);
+ Finalize(FDRCRollAvgWindow);
+
+ Finalize(FAvgpVuPrior);
+ Finalize(FPresentVpu);
+
+ Finalize(FPendingChange);
+ Finalize(FFlagROCOnly);
+ Finalize(QDeliver);
+ Finalize(QNew);
+ Finalize(QOld);
+ Finalize(QOldVV);
+ Finalize(QOldDRC);
+ Finalize(QHeadroom);
+ Finalize(Qoutputpu);
+ Finalize(QoutputVVpu);
+ Finalize(QoutputDRCpu);
+ Finalize(Qdesiredpu);
+ Finalize(QDRCdesiredpu);
+ Finalize(QDRCNew);
+ Finalize(PNew);
+ Finalize(POld);
+ Finalize(deltaVDynReac);
+ Finalize(priorRollAvgWindow);
+ Finalize(priorDRCRollAvgWindow);
+ Finalize(FVpuSolution);
+ Finalize(FlagChangeCurve);
+ Finalize(FActiveVVCurve);
+ Finalize(FPriorWattspu);
+ Finalize(FPriorvarspu);
+ Finalize(FLPFTime);
+ Finalize(FWithinTol);
+ Finalize(FWithinTolVV);
+ Finalize(FWithinTolVW);
+ Finalize(FROCEvaluated);
+ Finalize(FFinalpuPmpp);
+ Finalize(FFinalkvar);
+ Finalize(FHitkVALimit);
+ Finalize(FHitkvarLimit);
+
+
+ inherited Destroy;
+end;
+
+procedure TInvControlObj.RecalcElementData;
+var
+ i: Integer;
+begin
+ if FPVSystemPointerList.Count = 0 then
+ MakePVSystemList;
+
+ if FPVSystemPointerList.Count > 0 then
{Setting the terminal of the InvControl device to same as the 1st PVSystem element}
{ This sets it to a realistic value to avoid crashes later }
- Begin
- MonitoredElement := TDSSCktElement(FPVSystemPointerList.Get(1)); // Set MonitoredElement to 1st PVSystem in lise
- Setbus(1, MonitoredElement.Firstbus);
- End;
+ begin
+ MonitoredElement := TDSSCktElement(FPVSystemPointerList.Get(1)); // Set MonitoredElement to 1st PVSystem in lise
+ Setbus(1, MonitoredElement.Firstbus);
+ end;
for i := 1 to FPVSystemPointerList.Count do
begin
-
// User ControlledElement[] as the pointer to the PVSystem elements
- ControlledElement[i] := TPVSystemObj(FPVSystemPointerList.Get(i)); // pointer to i-th PVSystem
- SetLength(cBuffer[i], SizeOF(Complex) * ControlledElement[i].Yorder );
-
- ControlledElement[i].ActiveTerminalIdx := 1; // Make the 1 st terminal active
- Nphases := ControlledElement[i].NPhases;
- Nconds := Nphases;
- FRollAvgWindow[i].BuffLength := FRollAvgWindowLength; // TEMc
- FDRCRollAvgWindow[i].BuffLength := FDRCRollAvgWindowLength;
- if (ControlledElement[i] <> Nil) then
- With ControlledElement[i] Do
- begin
- FkVALimit[i] := kVARating;
- FVref[i] := PresentkV;
- FkWLimit[i] := Pmpp; // AC
- FkvarLimit[i] := kVARating; // can output vars up to the kva limit of the inverter
- FPpf[i] := PowerFactor;
- Fpresentkvar[i] := Presentkvar;
- FpresentkW[i] := PresentkW;
- CondOffset[i] := (NTerms-1) * NCondsPVSys[i]; // for speedy sampling
- end
- else
- begin
- ControlledElement[i] := nil; // PVSystem element not found
- DoErrorMsg('InvControl: "' + Self.Name + '"',
- 'Controlled Element "' + FPVSystemNameList.Strings[i-1] + '" Not Found.',
- ' PVSystem object must be defined previously.', 361);
+ ControlledElement[i] := TPVSystemObj(FPVSystemPointerList.Get(i)); // pointer to i-th PVSystem
+ SetLength(cBuffer[i], SizeOF(Complex) * ControlledElement[i].Yorder);
+
+ ControlledElement[i].ActiveTerminalIdx := 1; // Make the 1 st terminal active
+ FNphases := ControlledElement[i].NPhases;
+ Nconds := Nphases;
+ FRollAvgWindow[i].SetLength(FRollAvgWindowLength);
+ FDRCRollAvgWindow[i].SetLength(FDRCRollAvgWindowLength);
+ if (ControlledElement[i] <> NIL) then
+ with ControlledElement[i] do
+ begin
+ FkVALimit[i] := kVARating;
+ FVref[i] := PresentkV;
+ FkWLimit[i] := Pmpp; // AC
+ FkvarLimit[i] := kVARating; // can output vars up to the kva limit of the inverter
+ FPpf[i] := PowerFactor;
+ Fpresentkvar[i] := Presentkvar;
+ FpresentkW[i] := PresentkW;
+ CondOffset[i] := (NTerms - 1) * NCondsPVSys[i]; // for speedy sampling
+ end
+ else
+ begin
+ ControlledElement[i] := NIL; // PVSystem element not found
+ DoErrorMsg(Format(_('InvControl: "%s"'), [Self.Name]),
+ Format(_('Controlled Element "%s" not found.'), [FPVSystemNameList.Strings[i - 1]]),
+ _('PVSystem object must be defined previously.'), 361);
end;
end;
+end;
-End;
-
-procedure TInvControlObj.MakePosSequence;
+procedure TInvControlObj.MakePosSequence();
// *** This assumes the PVSystem devices have already been converted to pos seq
begin
- IF FPVSystemPointerList.Count = 0 Then RecalcElementData;
- Nphases := 3;
+ if FPVSystemPointerList.Count = 0 then
+ RecalcElementData;
+ FNphases := 3;
Nconds := 3;
Setbus(1, MonitoredElement.GetBus(ElementTerminal));
- IF FPVSystemPointerList.Count > 0 Then
+ if FPVSystemPointerList.Count > 0 then
{Setting the terminal of the InvControl device to same as the 1st PVSystem element}
{ This sets it to a realistic value to avoid crashes later }
- Begin
- MonitoredElement := TDSSCktElement(FPVSystemPointerList.Get(1)); // Set MonitoredElement to 1st PVSystem in lise
- Setbus(1, MonitoredElement.Firstbus);
- Nphases := MonitoredElement.NPhases;
- Nconds := Nphases;
-
- End;
- inherited;
-end;
-
-{--------------------------------------------------------------------------}
-PROCEDURE TInvControlObj.CalcYPrim;
-Begin
- // leave YPrims as nil and they will be ignored
- // Yprim is zeroed when created. Leave it as is.
- // IF YPrim=nil THEN YPrim := TcMatrix.CreateMatrix(Yorder);
-End;
-
-{--------------------------------------------------------------------------}
-PROCEDURE TInvControlObj.GetCurrents(Curr: pComplexArray);
-VAR
- i:Integer;
-Begin
-// Control is a zero current source
- For i := 1 to Fnconds Do Curr^[i] := CZERO;
-
-
-End;
-
-{--------------------------------------------------------------------------}
-PROCEDURE TInvControlObj.DumpProperties(F: TFileStream; Complete:Boolean);
-
-VAR
- i:Integer;
-
-Begin
- Inherited DumpProperties(F,Complete);
-
- WITH ParentClass Do
- For i := 1 to NumProperties Do
- Begin
- FSWriteln(F,'~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- End;
-
- If Complete THEN
- Begin
- FSWriteln(F);
- End;
-
-End;
-
-
-{--------------------------------------------------------------------------}
-PROCEDURE TInvControlObj.DoPendingAction;
-
-VAR
-
- k :Integer;
- // SMonitoredElement :Complex;
- Qtemp,PTemp,QTemp2 :Double;
- pctVV,pctDRC,QTemporig :Double;
-
- // local pointer to current PVSystem element
- PVSys :TPVSystemObj;
-
-BEGIN
- QTemp2 := 0.0;
-
-
-
+ begin
+ MonitoredElement := TDSSCktElement(FPVSystemPointerList.Get(1)); // Set MonitoredElement to 1st PVSystem in lise
+ Setbus(1, MonitoredElement.Firstbus);
+ FNphases := MonitoredElement.NPhases;
+ Nconds := Nphases;
+ end;
+ inherited;
+end;
- for k := 1 to FPVSystemPointerList.Count do
- begin
+procedure TInvControlObj.DoPendingAction;
+var
+ k: Integer;
+ // SMonitoredElement :Complex;
+ Qtemp, PTemp, QTemp2: Double;
+ pctVV, pctDRC, QTemporig: Double;
- PVSys := ControlledElement[k]; // Use local variable in loop
+ // local pointer to current PVSystem element
+ PVSys: TPVSystemObj;
+begin
+ QTemp2 := 0.0;
+ for k := 1 to FPVSystemPointerList.Count do
+ begin
+ PVSys := ControlledElement[k]; // Use local variable in loop
- // SMonitoredElement := PVSys.Power[1]; // s is in va
+ // SMonitoredElement := PVSys.Power[1]; // s is in va
- if(ControlMode = '') and (CombiControlMode = 'VV_DRC') and (PendingChange[k]=CHANGEDRCVVARLEVEL) then
+ if (ControlMode = NONE_MODE) and (CombiMode = VV_DRC) and (PendingChange[k] = CHANGEDRCVVARLEVEL) then
begin
- if (FFlagROCOnly[k] = False) then
+ if (FFlagROCOnly[k] = FALSE) then
begin
- CalcVoltVar_vars(k);
- CalcDRC_vars(k);
- QTemp := QNew[k]+QDRCNew[k];
+ CalcVoltVar_vars(k);
+ CalcDRC_vars(k);
+ QTemp := QNew[k] + QDRCNew[k];
- QTemporig := QTemp;
- if(QTemp = 0) then
+ QTemporig := QTemp;
+ if (QTemp = 0) then
begin
- if abs(QTemp) > abs(PVSys.kvarLimit) then
- QTemp := sign(QTemp)*1.0*PVSys.kvarLimit;
- PVSys.Presentkvar := QTemp;
- QTemp2 := PVSys.Presentkvar;
- Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
+ if abs(QTemp) > abs(PVSys.kvarLimit) then
+ QTemp := sign(QTemp) * 1.0 * PVSys.kvarLimit;
+ PVSys.Presentkvar := QTemp;
+ QTemp2 := PVSys.Presentkvar;
+ Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name +','+ PVSys.Name+',',
- Format('**VV_DRC mode set PVSystem output var level to**, kvar= %.5g',
- [PVSys.Presentkvar,FPresentVpu[k]]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**VV_DRC mode set PVSystem output var level to**, kvar= %.5g',
+ [PVSys.Presentkvar, FPresentVpu[k]]));
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- FAvgpVuPrior[k] := FPresentVpu[k];
- QOld[k] := QTemp;
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ QOld[k] := QTemp;
// WriteDLLDebugFile(Format('%g, %d, %.6g, %.6g, %.6g, %s', [ActiveCircuit.Solution.Dynavars.t, ActiveCircuit.Solution.ControlIteration, QOldVV[k],QoldDRC[k],QTemp, 'after limit (set-point).']));
- Set_PendingChange(NONE,k);
+ Set_PendingChange(NONE, k);
- exit;
+ exit;
end;
- pctVV := QNew[k]/QTemp;
- pctDRC := QDRCNew[k]/QTemp;
+ pctVV := QNew[k] / QTemp;
+ pctDRC := QDRCNew[k] / QTemp;
// WriteDLLDebugFile(Format('%g, %d, %.6g, %.6g, %.6g, %s', [ActiveCircuit.Solution.Dynavars.t, ActiveCircuit.Solution.ControlIteration, QNew[k],QDRCNew[k], QTemp, 'before limit.']));
//Respect the PVSystem's maximum kvar limit, first
- if abs(Qtemp2) > abs(PVSys.kvarLimit) then
+ if abs(Qtemp2) > abs(PVSys.kvarLimit) then
begin
- Qtemp2 := sign(Qtemp)*0.99*PVSys.kvarLimit;
- QDesiredpu[k] := pctVV*(Qtemp2/QTemporig)*QDesiredpu[k];
- QDRCDesiredpu[k] := pctDRC*(Qtemp2/QTemporig)*QDRCDesiredpu[k];
- FHitkvarLimit[k] := True;
+ Qtemp2 := sign(Qtemp) * 0.99 * PVSys.kvarLimit;
+ QDesiredpu[k] := pctVV * (Qtemp2 / QTemporig) * QDesiredpu[k];
+ QDRCDesiredpu[k] := pctDRC * (Qtemp2 / QTemporig) * QDRCDesiredpu[k];
+ FHitkvarLimit[k] := TRUE;
end;
- PVSys.SetNominalPVSystemOuput;
- PTemp := PVSys.PresentkW;
+ PVSys.SetNominalPVSystemOuput;
+ PTemp := PVSys.PresentkW;
// if the desired kW and desired kvar exceed the kva rating of the PVSystem's inverter then...
- if SQRT(Sqr(QTemp2)+Sqr(PTemp)) > PVSys.kVARating then
+ if SQRT(Sqr(QTemp2) + Sqr(PTemp)) > PVSys.kVARating then
begin
//...if watts have precedence, reduce the reactive power to not exceed the kva rating
- if(FVV_ReacPower_ref = 'VARAVAL_WATTS') or (FVV_ReacPower_ref = 'VARMAX_WATTS') then
+ if (FVV_ReacPower_ref = VARAVAL_WATTS) or (FVV_ReacPower_ref = VARMAX_WATTS) then
begin
- Qtemp2 := 0.99*sign(Qtemp2)*SQRT(Sqr(PVSys.kVARating)-Sqr(PTemp));
- Qnew[k] := Qtemp2;
- PVSys.Presentkvar := Qnew[k];
+ Qtemp2 := 0.99 * sign(Qtemp2) * SQRT(Sqr(PVSys.kVARating) - Sqr(PTemp));
+ Qnew[k] := Qtemp2;
+ PVSys.Presentkvar := Qnew[k];
end
//...else, vars have precedence, reduce the active power to not exceed the kva rating
- else
+ else
begin
- PTemp := 0.99*sign(PTemp)*SQRT(Sqr(PVSys.kVARating)-Sqr(QTemp2));
+ PTemp := 0.99 * sign(PTemp) * SQRT(Sqr(PVSys.kVARating) - Sqr(QTemp2));
// Set the active power
- FFinalpuPmpp[k] :=PTemp/PVSys.Pmpp;
- PVSys.VWmode := TRUE;
- PVSys.VWYAxis := FVoltwattYAxis;
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- if (FFlagROCOnly[k] = False) then
+ FFinalpuPmpp[k] := PTemp / PVSys.Pmpp;
+ PVSys.VWmode := TRUE;
+ PVSys.VWYAxis := FVoltwattYAxis;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ if (FFlagROCOnly[k] = FALSE) then
begin
- if (RateofChangeMode=INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
+ if (RateofChangeMode = INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
begin
- PVSys.puPmpp :=FFinalpuPmpp[k];
- PNew[k] :=FFinalpuPmpp[k];
-
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+','+PVSys.Name+',',
- Format('**VOLTVAR VARMAX_VARS mode limited PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp,FPriorWattspu[k]]));
- Qnew[k] := Qtemp2;
- PVSys.Presentkvar := Qnew[k];
-
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- FAvgpVuPrior[k] := FPresentVpu[k];
- POld[k] := PVSys.puPmpp;
- end;
+ PVSys.puPmpp := FFinalpuPmpp[k];
+ PNew[k] := FFinalpuPmpp[k];
+
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**VOLTVAR VARMAX_VARS mode limited PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp, FPriorWattspu[k]]));
+ Qnew[k] := Qtemp2;
+ PVSys.Presentkvar := Qnew[k];
+
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ POld[k] := PVSys.puPmpp;
+ end;
end;
end;
- FHitkvaLimit[k] := True;
+ FHitkvaLimit[k] := TRUE;
end;
// Set the reactive power, if it is different than the present PVSystem kvar setting
- PVSys.VWmode := FALSE;
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- PVSys.Varmode := VARMODEKVAR; // Set var mode to VARMODEKVAR to indicate we might change kvar
+ PVSys.VWmode := FALSE;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ PVSys.Varmode := VARMODEKVAR; // Set var mode to VARMODEKVAR to indicate we might change kvar
- If PVSys.Presentkvar <> QTemp Then
+ if PVSys.Presentkvar <> QTemp then
begin
- if abs(QTemp) > abs(PVSys.kvarLimit) then
- QTemp := sign(QTemp)*1.0*PVSys.kvarLimit;
- PVSys.Presentkvar := QTemp;
+ if abs(QTemp) > abs(PVSys.kvarLimit) then
+ QTemp := sign(QTemp) * 1.0 * PVSys.kvarLimit;
+ PVSys.Presentkvar := QTemp;
end;
- QoutputVVpu[k] := pctVV*PVSys.Presentkvar / QHeadroom[k];
- QoutputDRCpu[k] := pctDRC*PVSys.Presentkvar / QHeadroom[k];
- Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
+ QoutputVVpu[k] := pctVV * PVSys.Presentkvar / QHeadroom[k];
+ QoutputDRCpu[k] := pctDRC * PVSys.Presentkvar / QHeadroom[k];
+ Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name +','+ PVSys.Name+',',
- Format('**VV_DRC mode set PVSystem output var level to**, kvar= %.5g',
- [PVSys.Presentkvar,FPresentVpu[k]]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**VV_DRC mode set PVSystem output var level to**, kvar= %.5g',
+ [PVSys.Presentkvar, FPresentVpu[k]]));
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- FAvgpVuPrior[k] := FPresentVpu[k];
- QOld[k] := QTemp;
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ QOld[k] := QTemp;
- QOldVV[k] := pctVV*QTemp;
+ QOldVV[k] := pctVV * QTemp;
- QoldDRC[k] := pctDRC*QTemp;
+ QoldDRC[k] := pctDRC * QTemp;
- end;
+ end;
- if(FFlagROCOnly[k] = True) then
- begin
+ if (FFlagROCOnly[k] = TRUE) then
+ begin
// Apply LPF volt-var
- if (RateofChangeMode = LPF) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
- begin
- FROCEvaluated[k] := True;
- Qtemp := CalcLPF(k, 'VARS', PVSys);
- if(Qtemp <> -999.99) then
+ if (RateofChangeMode = LPF) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
+ begin
+ FROCEvaluated[k] := TRUE;
+ Qtemp := CalcLPF(k, 'VARS', PVSys);
+ if (Qtemp <> -999.99) then
begin
- if abs(Qtemp) > abs(PVSys.kvarLimit) then
- Qnew[k] := sign(Qtemp)*1.0*PVSys.kvarLimit
- else
- Qnew[k] := Qtemp;
- PVSys.Presentkvar := Qnew[k];
- Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name +','+ PVSys.Name+',',
- Format('VV_DRC mode, LPF set PVSystem output var level to**, kvar= %.5g',
- [Qnew[k]]));
- FAvgpVuPrior[k] := FPresentVpu[k];
- QOld[k] := QNew[k];
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ if abs(Qtemp) > abs(PVSys.kvarLimit) then
+ Qnew[k] := sign(Qtemp) * 1.0 * PVSys.kvarLimit
+ else
+ Qnew[k] := Qtemp;
+ PVSys.Presentkvar := Qnew[k];
+ Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('VV_DRC mode, LPF set PVSystem output var level to**, kvar= %.5g',
+ [Qnew[k]]));
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ QOld[k] := QNew[k];
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
end
- end;
+ end;
// Apply Rise/Fall volt-var
- if (RateofChangeMode = RISEFALL) and (ActiveCircuit.Solution.DynaVars.dblHour > 0.0) then
- begin
- Qtemp := CalcRF(k, 'VARS', PVSys);
- FROCEvaluated[k] := True;
- if(Qtemp <> -999.99) then
+ if (RateofChangeMode = RISEFALL) and (ActiveCircuit.Solution.DynaVars.dblHour > 0.0) then
+ begin
+ Qtemp := CalcRF(k, 'VARS', PVSys);
+ FROCEvaluated[k] := TRUE;
+ if (Qtemp <> -999.99) then
begin
- if abs(Qtemp) > abs(PVSys.kvarLimit) then
- Qnew[k] := sign(Qtemp)*1.0*PVSys.kvarLimit
- else
- Qnew[k] := Qtemp;
- PVSys.Presentkvar := Qnew[k];
- Qoutputpu[k] := Qnew[k] / QHeadroom[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name +','+ PVSys.Name+',',
- Format('VV_DRC mode, RISEFALL set PVSystem output var level to**, kvar= %.5g',
- [Qnew[k]]));
- FAvgpVuPrior[k] := FPresentVpu[k];
- QOld[k] := QNew[k];
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ if abs(Qtemp) > abs(PVSys.kvarLimit) then
+ Qnew[k] := sign(Qtemp) * 1.0 * PVSys.kvarLimit
+ else
+ Qnew[k] := Qtemp;
+ PVSys.Presentkvar := Qnew[k];
+ Qoutputpu[k] := Qnew[k] / QHeadroom[k];
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('VV_DRC mode, RISEFALL set PVSystem output var level to**, kvar= %.5g',
+ [Qnew[k]]));
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ QOld[k] := QNew[k];
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
end;
- end;
- end;
- Set_PendingChange(NONE,k);
+ end;
+ end;
+ Set_PendingChange(NONE, k);
end;
- if(ControlMode = '') and (CombiControlMode = 'VV_VW') and (PendingChange[k]=CHANGEWATTVARLEVEL) then
+ if (ControlMode = NONE_MODE) and (CombiMode = VV_VW) and (PendingChange[k] = CHANGEWATTVARLEVEL) then
begin
- if (FFlagROCOnly[k] = False) then
+ if (FFlagROCOnly[k] = FALSE) then
begin
- CalcVoltVar_vars(k);
- CalcVoltWatt_pu(k);
+ CalcVoltVar_vars(k);
+ CalcVoltWatt_pu(k);
//Respect the PVSystem's maximum kvar limit, first
- if abs(QNew[k]) > abs(PVSys.kvarLimit) then
+ if abs(QNew[k]) > abs(PVSys.kvarLimit) then
begin
- Qnew[k] := sign(QNew[k])*0.99*PVSys.kvarLimit;
- FHitkvarLimit[k] := True;
+ Qnew[k] := sign(QNew[k]) * 0.99 * PVSys.kvarLimit;
+ FHitkvarLimit[k] := TRUE;
end;
- QTemp2 := Qnew[k];
+ QTemp2 := Qnew[k];
//Convert output from CalcVoltWatt_pu to kW
- PVSys.VWmode := TRUE;
- PVSys.VWYAxis := FVoltwattYAxis;
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- if (FFlagROCOnly[k] = False) then
+ PVSys.VWmode := TRUE;
+ PVSys.VWYAxis := FVoltwattYAxis;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ if (FFlagROCOnly[k] = FALSE) then
begin
- if (RateofChangeMode=INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
- if(FVoltwattYAxis = 0) or (FVoltwattYAxis = 1) then
+ if (RateofChangeMode = INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
+ if (FVoltwattYAxis = 0) or (FVoltwattYAxis = 1) then
begin
-
- if(FVoltwattYaxis = 0) then Ptemp := (PVSys.PVSystemVars.PanelkW*PVSys.PVSystemVars.EffFactor*FFinalpuPmpp[k]);
- if(FVoltwattYaxis = 1) then Ptemp := (FFinalpuPmpp[k]*PVSys.Pmpp);
+ if (FVoltwattYaxis = 0) then
+ Ptemp := (PVSys.PVSystemVars.PanelkW * PVSys.PVSystemVars.EffFactor * FFinalpuPmpp[k]);
+ if (FVoltwattYaxis = 1) then
+ Ptemp := (FFinalpuPmpp[k] * PVSys.Pmpp);
// Ptemp := PVSys.PVSystemVars.PanelkW*PVSys.PVSystemVars.EffFactor*FFinalpuPmpp[k];
- if SQRT(Sqr(QTemp2)+Sqr(PTemp)) > PVSys.kVARating then
+ if SQRT(Sqr(QTemp2) + Sqr(PTemp)) > PVSys.kVARating then
begin
//...if watts have precedence, reduce the reactive power to not exceed the kva rating
- if(FVV_ReacPower_ref = 'VARAVAL_WATTS') or (FVV_ReacPower_ref = 'VARMAX_WATTS') then
+ if (FVV_ReacPower_ref = VARAVAL_WATTS) or (FVV_ReacPower_ref = VARMAX_WATTS) then
begin
- if(Ptemp = PVSys.Pmpp) then QTemp2:= 0.0
- else Qtemp2 := 0.99*sign(Qtemp2)*SQRT(Sqr(PVSys.kVARating)-Sqr(PTemp));
- Qnew[k] := Qtemp2;
- PVSys.Presentkvar := Qnew[k];
+ if (Ptemp = PVSys.Pmpp) then
+ QTemp2 := 0.0
+ else
+ Qtemp2 := 0.99 * sign(Qtemp2) * SQRT(Sqr(PVSys.kVARating) - Sqr(PTemp));
+ Qnew[k] := Qtemp2;
+ PVSys.Presentkvar := Qnew[k];
end
//...else, vars have precedence, reduce the active power to not exceed the kva rating
- else
+ else
begin
- if(QTemp2 = PVSys.Pmpp) then PTemp:= 0.0
- else PTemp := 0.99*sign(PTemp)*SQRT(Sqr(PVSys.kVARating)-Sqr(QTemp2));
+ if (QTemp2 = PVSys.Pmpp) then
+ PTemp := 0.0
+ else
+ PTemp := 0.99 * sign(PTemp) * SQRT(Sqr(PVSys.kVARating) - Sqr(QTemp2));
// Set the active power
- FFinalpuPmpp[k] :=PTemp/PVSys.Pmpp;
- PVSys.VWmode := TRUE;
- PVSys.VWYAxis := FVoltwattYAxis;
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- if (FFlagROCOnly[k] = False) then
+ FFinalpuPmpp[k] := PTemp / PVSys.Pmpp;
+ PVSys.VWmode := TRUE;
+ PVSys.VWYAxis := FVoltwattYAxis;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ if (FFlagROCOnly[k] = FALSE) then
begin
- if (RateofChangeMode=INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
+ if (RateofChangeMode = INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
begin
- PVSys.puPmpp :=FFinalpuPmpp[k];
- PNew[k] :=FFinalpuPmpp[k];
-
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+','+PVSys.Name+',',
- Format('**VOLTVAR VARMAX_VARS mode limited PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp,FPriorWattspu[k]]));
- Qnew[k] := Qtemp2;
- PVSys.Presentkvar := Qnew[k];
-
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- FAvgpVuPrior[k] := FPresentVpu[k];
- POld[k] := PVSys.puPmpp;
- end;
+ PVSys.puPmpp := FFinalpuPmpp[k];
+ PNew[k] := FFinalpuPmpp[k];
+
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**VOLTVAR VARMAX_VARS mode limited PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp, FPriorWattspu[k]]));
+ Qnew[k] := Qtemp2;
+ PVSys.Presentkvar := Qnew[k];
+
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ POld[k] := PVSys.puPmpp;
+ end;
end;
end;
- FHitkvaLimit[k] := True;
+ FHitkvaLimit[k] := TRUE;
end;
-
- PVSys.puPmpp :=FFinalpuPmpp[k];
- PVSys.SetNominalPVSystemOuput;
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- PNew[k] :=FFinalpuPmpp[k];
+ PVSys.puPmpp := FFinalpuPmpp[k];
+ PVSys.SetNominalPVSystemOuput;
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ PNew[k] := FFinalpuPmpp[k];
end
- else
+ else
begin
- PVSys.PresentkW :=FFinalpuPmpp[k]*PVSys.Pmpp*PVSys.puPmpp;
+ PVSys.PresentkW := FFinalpuPmpp[k] * PVSys.Pmpp * PVSys.puPmpp;
end;
end;
// if the desired kW and desired kvar exceed the kva rating of the PVSystem's inverter then...
- PVSys.SetNominalPVSystemOuput;
- PTemp := PVSys.PresentkW;
+ PVSys.SetNominalPVSystemOuput;
+ PTemp := PVSys.PresentkW;
// if the desired kW and desired kvar exceed the kva rating of the PVSystem's inverter then...
- if SQRT(Sqr(QTemp2)+Sqr(PTemp)) > PVSys.kVARating then
- begin
+ if SQRT(Sqr(QTemp2) + Sqr(PTemp)) > PVSys.kVARating then
+ begin
//...if watts have precedence, reduce the reactive power to not exceed the kva rating
- if(FVV_ReacPower_ref = 'VARAVAL_WATTS') or (FVV_ReacPower_ref = 'VARMAX_WATTS') then
+ if (FVV_ReacPower_ref = VARAVAL_WATTS) or (FVV_ReacPower_ref = VARMAX_WATTS) then
begin
- Qtemp2 := 0.99*sign(Qtemp2)*SQRT(Sqr(PVSys.kVARating)-Sqr(PTemp));
- Qnew[k] := Qtemp2;
- PVSys.Presentkvar := Qnew[k];
+ Qtemp2 := 0.99 * sign(Qtemp2) * SQRT(Sqr(PVSys.kVARating) - Sqr(PTemp));
+ Qnew[k] := Qtemp2;
+ PVSys.Presentkvar := Qnew[k];
end
//...else, vars have precedence, reduce the active power to not exceed the kva rating
- else
+ else
begin
- PTemp := 0.99*sign(PTemp)*SQRT(Sqr(PVSys.kVARating)-Sqr(QTemp2));
+ PTemp := 0.99 * sign(PTemp) * SQRT(Sqr(PVSys.kVARating) - Sqr(QTemp2));
// Set the active power
- FFinalpuPmpp[k] :=PTemp/PVSys.Pmpp;
- PVSys.VWmode := TRUE;
- PVSys.VWYAxis := FVoltwattYAxis;
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- if (FFlagROCOnly[k] = False) then
+ FFinalpuPmpp[k] := PTemp / PVSys.Pmpp;
+ PVSys.VWmode := TRUE;
+ PVSys.VWYAxis := FVoltwattYAxis;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ if (FFlagROCOnly[k] = FALSE) then
begin
- if (RateofChangeMode=INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
+ if (RateofChangeMode = INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
begin
- PVSys.puPmpp :=FFinalpuPmpp[k];
- PNew[k] :=FFinalpuPmpp[k];
-
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+','+PVSys.Name+',',
- Format('**VOLTVAR VARMAX_VARS mode limited PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp,FPriorWattspu[k]]));
- Qnew[k] := Qtemp2;
- PVSys.Presentkvar := Qnew[k];
-
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- FAvgpVuPrior[k] := FPresentVpu[k];
- POld[k] := PVSys.puPmpp;
- end;
+ PVSys.puPmpp := FFinalpuPmpp[k];
+ PNew[k] := FFinalpuPmpp[k];
+
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**VOLTVAR VARMAX_VARS mode limited PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp, FPriorWattspu[k]]));
+ Qnew[k] := Qtemp2;
+ PVSys.Presentkvar := Qnew[k];
+
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ POld[k] := PVSys.puPmpp;
+ end;
end;
+ end;
+ FHitkvaLimit[k] := TRUE;
end;
- FHitkvaLimit[k] := True;
- end;
-
// Set the reactive power, if it is different than the present PVSystem kvar setting
- PVSys.VWmode := FALSE;
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ PVSys.VWmode := FALSE;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- PVSys.Varmode := VARMODEKVAR; // Set var mode to VARMODEKVAR to indicate we might change kvar
- If PVSys.Presentkvar <> Qnew[k] Then
+ PVSys.Varmode := VARMODEKVAR; // Set var mode to VARMODEKVAR to indicate we might change kvar
+ if PVSys.Presentkvar <> Qnew[k] then
begin
- if abs(QNew[k]) > abs(PVSys.kvarLimit) then
- Qnew[k] := sign(QNew[k])*1.0*PVSys.kvarLimit;
- PVSys.Presentkvar := Qnew[k];
+ if abs(QNew[k]) > abs(PVSys.kvarLimit) then
+ Qnew[k] := sign(QNew[k]) * 1.0 * PVSys.kvarLimit;
+ PVSys.Presentkvar := Qnew[k];
end;
//Respect the PVSystem's maximum kvar limit
- if abs(Qnew[k]) > abs(PVSys.kvarLimit) then
+ if abs(Qnew[k]) > abs(PVSys.kvarLimit) then
begin
- Qnew[k] := sign(Qnew[k])*0.99*PVSys.kvarLimit;
- FHitkvarLimit[k] := True;
+ Qnew[k] := sign(Qnew[k]) * 0.99 * PVSys.kvarLimit;
+ FHitkvarLimit[k] := TRUE;
end;
- PVSys.Presentkvar := Qnew[k];
- Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name +','+ PVSys.Name+',',
- Format('**VV_VW mode set PVSystem output var level to**, kvar= %.5g',
- [Qnew[k]]));
+ PVSys.Presentkvar := Qnew[k];
+ Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**VV_VW mode set PVSystem output var level to**, kvar= %.5g',
+ [Qnew[k]]));
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- FAvgpVuPrior[k] := FPresentVpu[k];
- QOldVV[k] := QNew[k];
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ QOldVV[k] := QNew[k];
// Set the active power
- FFinalpuPmpp[k] :=PTemp/PVSys.Pmpp;
- PVSys.VWmode := TRUE;
- PVSys.VWYAxis := FVoltwattYAxis;
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- if (FFlagROCOnly[k] = False) then
+ FFinalpuPmpp[k] := PTemp / PVSys.Pmpp;
+ PVSys.VWmode := TRUE;
+ PVSys.VWYAxis := FVoltwattYAxis;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ if (FFlagROCOnly[k] = FALSE) then
begin
- if (RateofChangeMode=INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
+ if (RateofChangeMode = INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
begin
- PVSys.puPmpp :=FFinalpuPmpp[k];
- PNew[k] :=FFinalpuPmpp[k];
+ PVSys.puPmpp := FFinalpuPmpp[k];
+ PNew[k] := FFinalpuPmpp[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+','+PVSys.Name+',',
- Format('**VV_VW mode set PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp,POld[k]]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**VV_VW mode set PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp, POld[k]]));
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- FAvgpVuPrior[k] := FPresentVpu[k];
- POld[k] := PVSys.puPmpp;
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ POld[k] := PVSys.puPmpp;
end;
end;
end;
- if(FFlagROCOnly[k] = True) then
- begin
+ if (FFlagROCOnly[k] = TRUE) then
+ begin
// Apply LPF volt-var
- if (RateofChangeMode = LPF) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
- begin
- FROCEvaluated[k] := True;
- Qtemp := CalcLPF(k, 'VARS', PVSys);
- if(Qtemp <> -999.99) then
+ if (RateofChangeMode = LPF) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
+ begin
+ FROCEvaluated[k] := TRUE;
+ Qtemp := CalcLPF(k, 'VARS', PVSys);
+ if (Qtemp <> -999.99) then
begin
- if abs(Qtemp) > abs(PVSys.kvarLimit) then
- Qnew[k] := sign(Qtemp)*1.0*PVSys.kvarLimit
- else
- Qnew[k] := Qtemp;
- PVSys.Presentkvar := Qnew[k];
- Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name +','+ PVSys.Name+',',
- Format('VV_VW mode, LPF set PVSystem output var level to**, kvar= %.5g',
- [Qnew[k]]));
- FAvgpVuPrior[k] := FPresentVpu[k];
- QOld[k] := QNew[k];
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ if abs(Qtemp) > abs(PVSys.kvarLimit) then
+ Qnew[k] := sign(Qtemp) * 1.0 * PVSys.kvarLimit
+ else
+ Qnew[k] := Qtemp;
+ PVSys.Presentkvar := Qnew[k];
+ Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('VV_VW mode, LPF set PVSystem output var level to**, kvar= %.5g',
+ [Qnew[k]]));
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ QOld[k] := QNew[k];
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
end
- end;
+ end;
// Apply Rise/Fall volt-var
- if (RateofChangeMode = RISEFALL) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
- begin
-
- Qtemp := CalcRF(k, 'VARS', PVSys);
- if(Qtemp <> -999.99) then
+ if (RateofChangeMode = RISEFALL) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
+ begin
+ Qtemp := CalcRF(k, 'VARS', PVSys);
+ if (Qtemp <> -999.99) then
begin
- if abs(Qtemp) > abs(PVSys.kvarLimit) then
- Qnew[k] := sign(Qtemp)*1.0*PVSys.kvarLimit
- else
- Qnew[k] := Qtemp;
- PVSys.Presentkvar := Qnew[k];
- Qoutputpu[k] := Qnew[k] / QHeadroom[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name +','+ PVSys.Name+',',
- Format('VV_VW mode, RISEFALL set PVSystem output var level to**, kvar= %.5g',
- [Qnew[k]]));
- FAvgpVuPrior[k] := FPresentVpu[k];
- QOld[k] := QNew[k];
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ if abs(Qtemp) > abs(PVSys.kvarLimit) then
+ Qnew[k] := sign(Qtemp) * 1.0 * PVSys.kvarLimit
+ else
+ Qnew[k] := Qtemp;
+ PVSys.Presentkvar := Qnew[k];
+ Qoutputpu[k] := Qnew[k] / QHeadroom[k];
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('VV_VW mode, RISEFALL set PVSystem output var level to**, kvar= %.5g',
+ [Qnew[k]]));
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ QOld[k] := QNew[k];
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
end;
- end;
+ end;
// rate of change LPF - watts
- if (RateofChangeMode = LPF) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
- begin
- FROCEvaluated[k] := True;
+ if (RateofChangeMode = LPF) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
+ begin
+ FROCEvaluated[k] := TRUE;
- Ptemp := CalcLPF(k, 'WATTS', PVSys);
- if(Ptemp <> -999.99) then
+ Ptemp := CalcLPF(k, 'WATTS', PVSys);
+ if (Ptemp <> -999.99) then
begin
- if PTemp <> 0.0 then
+ if PTemp <> 0.0 then
begin
- PNew[k] := PTemp;
- PVSys.puPmpp := PNew[k];
+ PNew[k] := PTemp;
+ PVSys.puPmpp := PNew[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+','+PVSys.Name+',',
- Format('**VV_VW mode, LPF set PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp,FPriorWattspu[k]]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**VV_VW mode, LPF set PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp, FPriorWattspu[k]]));
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- FAvgpVuPrior[k] := FPresentVpu[k];
- POld[k] := PVSys.puPmpp;
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ POld[k] := PVSys.puPmpp;
end;
end;
- Set_PendingChange(NONE,k);
+ Set_PendingChange(NONE, k);
end;
// rate of change rise/fall limit
- if (RateofChangeMode = RISEFALL) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
- begin
- FROCEvaluated[k] := True;
- PTemp := CalcRF(k, 'WATTS', PVSys);
- if(Ptemp <> -999.99) then
+ if (RateofChangeMode = RISEFALL) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
+ begin
+ FROCEvaluated[k] := TRUE;
+ PTemp := CalcRF(k, 'WATTS', PVSys);
+ if (Ptemp <> -999.99) then
begin
- PNew[k] := PTemp;
- PVSys.puPmpp := PNew[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+','+PVSys.Name+',',
- Format('**VV_VW mode, RISEFALL set PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp,FPriorWattspu[k]]));
-
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- FAvgpVuPrior[k] := FPresentVpu[k];
- POld[k] := PVSys.puPmpp;
+ PNew[k] := PTemp;
+ PVSys.puPmpp := PNew[k];
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**VV_VW mode, RISEFALL set PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp, FPriorWattspu[k]]));
+
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ POld[k] := PVSys.puPmpp;
end;
// Force recalc of power parms
- Set_PendingChange(NONE,k);
+ Set_PendingChange(NONE, k);
- end
+ end
end;
- Set_PendingChange(NONE,k);
+ Set_PendingChange(NONE, k);
end;
- if(ControlMode = 'VOLTVAR') and (CombiControlMode = '') and (PendingChange[k]=CHANGEVARLEVEL) then
+ if (ControlMode = VOLTVAR) and (CombiMode = NONE_COMBMODE) and (PendingChange[k] = CHANGEVARLEVEL) then
begin
- if (FFlagROCOnly[k] = False) then
+ if (FFlagROCOnly[k] = FALSE) then
begin
+ PVSys.VWmode := FALSE;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- PVSys.VWmode := FALSE;
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ PVSys.Varmode := VARMODEKVAR; // Set var mode to VARMODEKVAR to indicate we might change kvar
+ CalcVoltVar_vars(k);
- PVSys.Varmode := VARMODEKVAR; // Set var mode to VARMODEKVAR to indicate we might change kvar
- CalcVoltVar_vars(k);
-
- If PVSys.Presentkvar <> Qnew[k] Then
+ if PVSys.Presentkvar <> Qnew[k] then
begin
- if abs(QNew[k]) > abs(PVSys.kvarLimit) then
- begin
- Qnew[k] := sign(QNew[k])*1.0*PVSys.kvarLimit;
- FHitkvarLimit[k] := True;
+ if abs(QNew[k]) > abs(PVSys.kvarLimit) then
+ begin
+ Qnew[k] := sign(QNew[k]) * 1.0 * PVSys.kvarLimit;
+ FHitkvarLimit[k] := TRUE;
end;
+ PVSys.Presentkvar := Qnew[k];
+ QTemp2 := Qnew[k];
+ end;
PVSys.Presentkvar := Qnew[k];
QTemp2 := Qnew[k];
- end;
- PVSys.Presentkvar := Qnew[k];
- QTemp2 := Qnew[k];
- PVSys.SetNominalPVSystemOuput;
- PTemp := PVSys.PresentkW;
+ PVSys.SetNominalPVSystemOuput;
+ PTemp := PVSys.PresentkW;
// if the desired kW and desired kvar exceed the kva rating of the PVSystem's inverter then...
- if SQRT(Sqr(QTemp2)+Sqr(PTemp)) > PVSys.kVARating then
+ if SQRT(Sqr(QTemp2) + Sqr(PTemp)) > PVSys.kVARating then
begin
//...if watts have precedence, reduce the reactive power to not exceed the kva rating
- if(FVV_ReacPower_ref = 'VARAVAL_WATTS') or (FVV_ReacPower_ref = 'VARMAX_WATTS') then
+ if (FVV_ReacPower_ref = VARAVAL_WATTS) or (FVV_ReacPower_ref = VARMAX_WATTS) then
begin
- Qtemp2 := 0.99*sign(Qtemp2)*SQRT(Sqr(PVSys.kVARating)-Sqr(PTemp));
- Qnew[k] := Qtemp2;
- PVSys.Presentkvar := Qnew[k];
+ Qtemp2 := 0.99 * sign(Qtemp2) * SQRT(Sqr(PVSys.kVARating) - Sqr(PTemp));
+ Qnew[k] := Qtemp2;
+ PVSys.Presentkvar := Qnew[k];
end
//...else, vars have precedence, reduce the active power to not exceed the kva rating
- else
+ else
begin
- PTemp := 0.99*sign(PTemp)*SQRT(Sqr(PVSys.kVARating)-Sqr(QTemp2));
+ PTemp := 0.99 * sign(PTemp) * SQRT(Sqr(PVSys.kVARating) - Sqr(QTemp2));
// Set the active power
- FFinalpuPmpp[k] :=PTemp/PVSys.Pmpp;
- PVSys.VWmode := TRUE;
- PVSys.VWYAxis := FVoltwattYAxis;
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- if (FFlagROCOnly[k] = False) then
+ FFinalpuPmpp[k] := PTemp / PVSys.Pmpp;
+ PVSys.VWmode := TRUE;
+ PVSys.VWYAxis := FVoltwattYAxis;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ if (FFlagROCOnly[k] = FALSE) then
begin
- if (RateofChangeMode=INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
+ if (RateofChangeMode = INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
begin
- PVSys.puPmpp :=FFinalpuPmpp[k];
- PNew[k] :=FFinalpuPmpp[k];
-
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+','+PVSys.Name+',',
- Format('**VOLTVAR VARMAX_VARS mode limited PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp,FPriorWattspu[k]]));
- Qnew[k] := Qtemp2;
- PVSys.Presentkvar := Qnew[k];
-
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- FAvgpVuPrior[k] := FPresentVpu[k];
- POld[k] := PVSys.puPmpp;
- end;
+ PVSys.puPmpp := FFinalpuPmpp[k];
+ PNew[k] := FFinalpuPmpp[k];
+
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**VOLTVAR VARMAX_VARS mode limited PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp, FPriorWattspu[k]]));
+ Qnew[k] := Qtemp2;
+ PVSys.Presentkvar := Qnew[k];
+
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ POld[k] := PVSys.puPmpp;
+ end;
end;
end;
- FHitkvaLimit[k] := True;
- end;
-
- Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
- QoutputVVpu[k] := Qoutputpu[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name +','+ PVSys.Name+',',
- Format('VOLTVAR mode set PVSystem output var level to**, kvar= %.5g',
- [PVSys.Presentkvar]));
- FAvgpVuPrior[k] := FPresentVpu[k];
- QOld[k] := QNew[k];
- QOldVV[k] := QNew[k];
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- end
+ FHitkvaLimit[k] := TRUE;
+ end;
- // Apply LPF volt-var
- else
- begin
+ Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
+ QoutputVVpu[k] := Qoutputpu[k];
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('VOLTVAR mode set PVSystem output var level to**, kvar= %.5g',
+ [PVSys.Presentkvar]));
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ QOld[k] := QNew[k];
+ QOldVV[k] := QNew[k];
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ end
- if (RateofChangeMode = LPF) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
+ // Apply LPF volt-var
+ else
begin
- FROCEvaluated[k] := True;
- Qtemp := CalcLPF(k, 'VARS', PVSys);
- if(Qtemp <> -999.99) then
- begin
- if abs(Qtemp) > abs(PVSys.kvarLimit) then
- Qnew[k] := sign(Qtemp)*1.0*PVSys.kvarLimit
- else
- Qnew[k] := Qtemp;
- PVSys.Presentkvar := Qnew[k];
- Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name +','+ PVSys.Name+',',
- Format('VOLTVAR mode, LPF set PVSystem output var level to**, kvar= %.5g',
- [Qnew[k]]));
- FAvgpVuPrior[k] := FPresentVpu[k];
- QOld[k] := QNew[k];
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- end
- end;
+ if (RateofChangeMode = LPF) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
+ begin
+ FROCEvaluated[k] := TRUE;
+ Qtemp := CalcLPF(k, 'VARS', PVSys);
+ if (Qtemp <> -999.99) then
+ begin
+ if abs(Qtemp) > abs(PVSys.kvarLimit) then
+ Qnew[k] := sign(Qtemp) * 1.0 * PVSys.kvarLimit
+ else
+ Qnew[k] := Qtemp;
+ PVSys.Presentkvar := Qnew[k];
+ Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('VOLTVAR mode, LPF set PVSystem output var level to**, kvar= %.5g',
+ [Qnew[k]]));
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ QOld[k] := QNew[k];
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ end
+ end;
// Apply Rise/Fall volt-var
- if (RateofChangeMode = RISEFALL) and (ActiveCircuit.Solution.DynaVars.dblHour > 0.0) then
- begin
- FROCEvaluated[k] := True;
- Qtemp := CalcRF(k, 'VARS', PVSys);
- if(Qtemp <> -999.99) then
- begin
- if abs(Qtemp) > abs(PVSys.kvarLimit) then
- Qnew[k] := sign(Qtemp)*1.0*PVSys.kvarLimit
- else
- Qnew[k] := Qtemp;
- PVSys.Presentkvar := Qnew[k];
- Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name +','+ PVSys.Name+',',
- Format('VOLTVAR mode, RISEFALL set PVSystem output var level to**, kvar= %.5g',
- [Qnew[k]]));
- FAvgpVuPrior[k] := FPresentVpu[k];
- QOld[k] := QNew[k];
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- end;
+ if (RateofChangeMode = RISEFALL) and (ActiveCircuit.Solution.DynaVars.dblHour > 0.0) then
+ begin
+ FROCEvaluated[k] := TRUE;
+ Qtemp := CalcRF(k, 'VARS', PVSys);
+ if (Qtemp <> -999.99) then
+ begin
+ if abs(Qtemp) > abs(PVSys.kvarLimit) then
+ Qnew[k] := sign(Qtemp) * 1.0 * PVSys.kvarLimit
+ else
+ Qnew[k] := Qtemp;
+ PVSys.Presentkvar := Qnew[k];
+ Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('VOLTVAR mode, RISEFALL set PVSystem output var level to**, kvar= %.5g',
+ [Qnew[k]]));
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ QOld[k] := QNew[k];
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ end;
+ end;
end;
+ Set_PendingChange(NONE, k);
end;
- Set_PendingChange(NONE,k);
- end;
- if(ControlMode = 'DYNAMICREACCURR') and (CombiControlMode = '') and (PendingChange[k]=CHANGEVARLEVEL) then
+ if (ControlMode = DRC) and (CombiMode = NONE_COMBMODE) and (PendingChange[k] = CHANGEVARLEVEL) then
begin
- PVSys.VWmode := FALSE;
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ PVSys.VWmode := FALSE;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- PVSys.Varmode := VARMODEKVAR; // Set var mode to VARMODEKVAR to indicate we might change kvar
- CalcDRC_vars(k);
- QTemp := QDRCNew[k];
- QTempOrig := QDRCNew[k];
+ PVSys.Varmode := VARMODEKVAR; // Set var mode to VARMODEKVAR to indicate we might change kvar
+ CalcDRC_vars(k);
+ QTemp := QDRCNew[k];
+ QTempOrig := QDRCNew[k];
- if abs(QDRCNew[k]) > abs(PVSys.kvarLimit) then
- begin
- QTemp := sign(QDRCNew[k])*0.99*PVSys.kvarLimit;
- QDesiredpu[k] := (Qtemp/QTemporig)*QDesiredpu[k];
- QDRCDesiredpu[k] := (Qtemp/QTemporig)*QDRCDesiredpu[k];
- FHitkvarLimit[k] := True;
- end;
+ if abs(QDRCNew[k]) > abs(PVSys.kvarLimit) then
+ begin
+ QTemp := sign(QDRCNew[k]) * 0.99 * PVSys.kvarLimit;
+ QDesiredpu[k] := (Qtemp / QTemporig) * QDesiredpu[k];
+ QDRCDesiredpu[k] := (Qtemp / QTemporig) * QDRCDesiredpu[k];
+ FHitkvarLimit[k] := TRUE;
+ end;
- PVSys.SetNominalPVSystemOuput;
- PTemp := PVSys.PresentkW;
+ PVSys.SetNominalPVSystemOuput;
+ PTemp := PVSys.PresentkW;
// if the desired kW and desired kvar exceed the kva rating of the PVSystem's inverter then...
- if SQRT(Sqr(Qtemp)+Sqr(PTemp)) > PVSys.kVARating then
- begin
+ if SQRT(Sqr(Qtemp) + Sqr(PTemp)) > PVSys.kVARating then
+ begin
//...if watts have precedence, reduce the reactive power to not exceed the kva rating
- if(FVV_ReacPower_ref = 'VARAVAL_WATTS') or (FVV_ReacPower_ref = 'VARMAX_WATTS') then
- begin
- Qtemp := 0.99*sign(Qtemp)*SQRT(Sqr(PVSys.kVARating)-Sqr(PTemp));
- QDesiredpu[k] := (Qtemp/QTemporig)*QDesiredpu[k];
- QDRCDesiredpu[k] := (Qtemp/QTemporig)*QDRCDesiredpu[k];
- end
+ if (FVV_ReacPower_ref = VARAVAL_WATTS) or (FVV_ReacPower_ref = VARMAX_WATTS) then
+ begin
+ Qtemp := 0.99 * sign(Qtemp) * SQRT(Sqr(PVSys.kVARating) - Sqr(PTemp));
+ QDesiredpu[k] := (Qtemp / QTemporig) * QDesiredpu[k];
+ QDRCDesiredpu[k] := (Qtemp / QTemporig) * QDRCDesiredpu[k];
+ end
//...else, vars have precedence, reduce the active power to not exceed the kva rating
- else
- begin
- PTemp := 0.99*sign(PTemp)*SQRT(Sqr(PVSys.kVARating)-Sqr(Qtemp));
+ else
+ begin
+ PTemp := 0.99 * sign(PTemp) * SQRT(Sqr(PVSys.kVARating) - Sqr(Qtemp));
// Set the active power
- FFinalpuPmpp[k] :=PTemp/PVSys.Pmpp;
- PVSys.VWmode := TRUE;
- PVSys.VWYAxis := FVoltwattYAxis;
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- if (FFlagROCOnly[k] = False) then
+ FFinalpuPmpp[k] := PTemp / PVSys.Pmpp;
+ PVSys.VWmode := TRUE;
+ PVSys.VWYAxis := FVoltwattYAxis;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ if (FFlagROCOnly[k] = FALSE) then
+ begin
+ if (RateofChangeMode = INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
begin
- if (RateofChangeMode=INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
- begin
- PVSys.puPmpp :=FFinalpuPmpp[k];
- PNew[k] :=FFinalpuPmpp[k];
+ PVSys.puPmpp := FFinalpuPmpp[k];
+ PNew[k] := FFinalpuPmpp[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+','+PVSys.Name+',',
- Format('**DRC VARMAX_VARS mode set PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp,FPriorWattspu[k]]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**DRC VARMAX_VARS mode set PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp, FPriorWattspu[k]]));
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- FAvgpVuPrior[k] := FPresentVpu[k];
- POld[k] := PVSys.puPmpp;
- end;
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ POld[k] := PVSys.puPmpp;
end;
end;
- FHitkvaLimit[k] := True;
end;
+ FHitkvaLimit[k] := TRUE;
+ end;
- if abs(QTemp) > abs(PVSys.kvarLimit) then
- QTemp := sign(QTemp)*1.0*PVSys.kvarLimit;
+ if abs(QTemp) > abs(PVSys.kvarLimit) then
+ QTemp := sign(QTemp) * 1.0 * PVSys.kvarLimit;
PVSys.Presentkvar := QTemp;
- Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name +','+ PVSys.Name+',',
- Format('DRC mode set PVSystem output var level to**, kvar= %.5g',
- [QTemp]));
-
- QoutputDRCpu[k] := PVSys.Presentkvar / QHeadroom[k];
-
-
- QoldDRC[k] := QTemp;
-
- FAvgpVuPrior[k] := FPresentVpu[k];
- QOld[k] := QDRCNew[k];
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
-
- // Apply LPF
- if (RateofChangeMode = LPF) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
- begin
- FROCEvaluated[k] := True;
- Qtemp := CalcLPF(k, 'VARS', PVSys);
- if(Qtemp <> -999.99) then
- begin
- if abs(Qtemp) > abs(PVSys.kvarLimit) then
- Qnew[k] := sign(Qtemp)*1.0*PVSys.kvarLimit
- else
- Qnew[k] := Qtemp;
- PVSys.Presentkvar := Qnew[k];
- Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name +','+ PVSys.Name+',',
- Format('DYNAMICREACTIVECURRENT mode, LPF set PVSystem output var level to**, kvar= %.5g',
- [Qnew[k]]));
- FAvgpVuPrior[k] := FPresentVpu[k];
- QOld[k] := QNew[k];
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- end
- end;
-
- // Apply Rise/Fall
+ Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('DRC mode set PVSystem output var level to**, kvar= %.5g',
+ [QTemp]));
+
+ QoutputDRCpu[k] := PVSys.Presentkvar / QHeadroom[k];
+
+
+ QoldDRC[k] := QTemp;
+
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ QOld[k] := QDRCNew[k];
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+
+ // Apply LPF
+ if (RateofChangeMode = LPF) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
+ begin
+ FROCEvaluated[k] := TRUE;
+ Qtemp := CalcLPF(k, 'VARS', PVSys);
+ if (Qtemp <> -999.99) then
+ begin
+ if abs(Qtemp) > abs(PVSys.kvarLimit) then
+ Qnew[k] := sign(Qtemp) * 1.0 * PVSys.kvarLimit
+ else
+ Qnew[k] := Qtemp;
+ PVSys.Presentkvar := Qnew[k];
+ Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('DYNAMICREACTIVECURRENT mode, LPF set PVSystem output var level to**, kvar= %.5g',
+ [Qnew[k]]));
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ QOld[k] := QNew[k];
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ end
+ end;
+
+ // Apply Rise/Fall
if (RateofChangeMode = RISEFALL) and (ActiveCircuit.Solution.DynaVars.dblHour > 0.0) then
- begin
- FROCEvaluated[k] := True;
+ begin
+ FROCEvaluated[k] := TRUE;
Qtemp := CalcRF(k, 'VARS', PVSys);
- if(Qtemp <> -999.99) then
- begin
- if abs(Qtemp) > abs(PVSys.kvarLimit) then
- Qnew[k] := sign(Qtemp)*1.0*PVSys.kvarLimit
- else
+ if (Qtemp <> -999.99) then
+ begin
+ if abs(Qtemp) > abs(PVSys.kvarLimit) then
+ Qnew[k] := sign(Qtemp) * 1.0 * PVSys.kvarLimit
+ else
Qnew[k] := Qtemp;
- PVSys.Presentkvar := Qnew[k];
- Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name +','+ PVSys.Name+',',
- Format('DYNAMICREACTIVECURRENT mode, RISEFALL set PVSystem output var level to**, kvar= %.5g',
- [Qnew[k],FPresentVpu[k]]));
- FAvgpVuPrior[k] := FPresentVpu[k];
- QOld[k] := QNew[k];
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- end;
- end;
- Set_PendingChange(NONE,k);
+ PVSys.Presentkvar := Qnew[k];
+ Qoutputpu[k] := PVSys.Presentkvar / QHeadroom[k];
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('DYNAMICREACTIVECURRENT mode, RISEFALL set PVSystem output var level to**, kvar= %.5g',
+ [Qnew[k], FPresentVpu[k]]));
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ QOld[k] := QNew[k];
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ end;
+ end;
+ Set_PendingChange(NONE, k);
end;
- if(ControlMode = 'VOLTWATT') and (CombiControlMode = '') and (PendingChange[k]=CHANGEWATTLEVEL) then
+ if (ControlMode = VOLTWATT) and (CombiMode = NONE_COMBMODE) and (PendingChange[k] = CHANGEWATTLEVEL) then
begin
- PVSys.VWmode := TRUE;
- PVSys.VWYAxis := FVoltwattYAxis;
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- if (FFlagROCOnly[k] = False) then
+ PVSys.VWmode := TRUE;
+ PVSys.VWYAxis := FVoltwattYAxis;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ if (FFlagROCOnly[k] = FALSE) then
begin
- CalcVoltWatt_pu(k);
+ CalcVoltWatt_pu(k);
- if (RateofChangeMode=INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
+ if (RateofChangeMode = INACTIVE) or (ActiveCircuit.Solution.Dynavars.dblHour = 0.0) then
begin
- PVSys.puPmpp :=FFinalpuPmpp[k];
- PNew[k] :=FFinalpuPmpp[k];
+ PVSys.puPmpp := FFinalpuPmpp[k];
+ PNew[k] := FFinalpuPmpp[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+','+PVSys.Name+',',
- Format('**VOLTWATT mode set PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp,FPriorWattspu[k]]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**VOLTWATT mode set PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp, FPriorWattspu[k]]));
- ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- FAvgpVuPrior[k] := FPresentVpu[k];
- POld[k] := PVSys.puPmpp;
+ ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
+ FAvgpVuPrior[k] := FPresentVpu[k];
+ POld[k] := PVSys.puPmpp;
// Set_PendingChange(NONE,k);
end;
end;
-
// rate of change LPF
- if (RateofChangeMode = LPF) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
+ if (RateofChangeMode = LPF) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
begin
- FROCEvaluated[k] := True;
- Ptemp := CalcLPF(k, 'WATTS', PVSys);
- if(Ptemp <> -999.99) then
- begin
+ FROCEvaluated[k] := TRUE;
+ Ptemp := CalcLPF(k, 'WATTS', PVSys);
+ if (Ptemp <> -999.99) then
+ begin
if PTemp <> 0.0 then
- begin
+ begin
PNew[k] := PTemp;
PVSys.puPmpp := PNew[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+','+PVSys.Name+',',
- Format('**VOLTWATT mode, LPF limited PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp,FPriorWattspu[k]]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**VOLTWATT mode, LPF limited PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp, FPriorWattspu[k]]));
ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
FAvgpVuPrior[k] := FPresentVpu[k];
POld[k] := PVSys.puPmpp;
- end;
- end;
+ end;
+ end;
// Set_PendingChange(NONE,k);
- end;
+ end;
// rate of change rise/fall limit
- if (RateofChangeMode = RISEFALL) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
+ if (RateofChangeMode = RISEFALL) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
begin
- FROCEvaluated[k] := True;
- PTemp := CalcRF(k, 'WATTS', PVSys);
- if(Ptemp <> -999.99) then
- begin
+ FROCEvaluated[k] := TRUE;
+ PTemp := CalcRF(k, 'WATTS', PVSys);
+ if (Ptemp <> -999.99) then
+ begin
PNew[k] := PTemp;
PVSys.puPmpp := PNew[k];
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+','+PVSys.Name+',',
- Format('**VOLTWATT mode, RISEFALL limited PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp,FPriorWattspu[k]]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ',' + PVSys.Name + ',',
+ Format('**VOLTWATT mode, RISEFALL limited PVSystem output level to**, puPmpp= %.5g, PriorWatts= %.5g', [PVSys.puPmpp, FPriorWattspu[k]]));
ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
FAvgpVuPrior[k] := FPresentVpu[k];
POld[k] := PVSys.puPmpp;
- end;
+ end;
// Force recalc of power parms
// Set_PendingChange(NONE,k);
end
end;
- Set_PendingChange(NONE,k);
- PVSys := Nil;
- end;
+ Set_PendingChange(NONE, k);
+ PVSys := NIL;
+ end;
{Do Nothing}
end;
-{--------------------------------------------------------------------------}
-PROCEDURE TInvControlObj.Sample;
+procedure TInvControlObj.Sample;
-VAR
- i,j :Integer;
- basekV,
- Vpresent :Double;
+var
+ i, j: Integer;
+ basekV,
+ Vpresent: Double;
begin
-
// If list is not defined, go make one from all PVSystem in circuit
- IF FPVSystemPointerList.Count=0 Then RecalcElementData;
+ if FPVSystemPointerList.Count = 0 then
+ RecalcElementData;
- If (FListSize>0) then
- Begin
+ if (FListSize > 0) then
+ begin
// If an InvControl controls more than one PV, control each one
// separately based on the PVSystem's terminal voltages, etc.
- for i := 1 to FPVSystemPointerList.Count do
- begin
- if(ActiveCircuit.Solution.DynaVars.t = 1) and (ActiveCircuit.Solution.ControlIteration=1) then
- FWithinTol[i] := False;
- FWithinTolVV[i] := False;
- FWithinTolVW[i] := False;
+ for i := 1 to FPVSystemPointerList.Count do
+ begin
+ if (ActiveCircuit.Solution.DynaVars.t = 1) and (ActiveCircuit.Solution.ControlIteration = 1) then
+ FWithinTol[i] := FALSE;
+ FWithinTolVV[i] := FALSE;
+ FWithinTolVW[i] := FALSE;
ControlledElement[i].ComputeVTerminal;
for j := 1 to ControlledElement[i].Yorder do
- cBuffer[i,j] := ControlledElement[i].Vterminal^[j];
+ cBuffer[i, j] := ControlledElement[i].Vterminal^[j];
- BasekV := ActiveCircuit.Buses^[ ControlledElement[i].terminals[0].busRef].kVBase;
+ BasekV := ActiveCircuit.Buses^[ControlledElement[i].terminals[0].busRef].kVBase;
Vpresent := 0;
// Calculate the present average voltage magnitude
- For j := 1 to ControlledElement[i].NPhases Do
- Vpresent := Vpresent + Cabs(cBuffer[i,j]);
+ for j := 1 to ControlledElement[i].NPhases do
+ Vpresent := Vpresent + Cabs(cBuffer[i, j]);
// convert to per-unit on bus' kvbase, or
// if using averaging window values, then set prior voltage to averaging window
- if(FVoltage_CurveX_ref = 1) and (FRollAvgWindow[i].Get_AvgVal <> 0.0) then FPresentVpu[i] := (Vpresent / ControlledElement[i].NPhases) / (FRollAvgWindow[i].Get_AvgVal)
- else if(FVoltage_CurveX_ref = 2) and (FRollAvgWindow[i].Get_AvgVal <> 0.0) then FPresentVpu[i] := (FRollAvgWindow[i].Get_AvgVal) / (basekV * 1000.0)
- else FPresentVpu[i] := (Vpresent / ControlledElement[i].NPhases) / (basekV * 1000.0);
+ if (FVoltage_CurveX_ref = 1) and (FRollAvgWindow[i].AvgVal <> 0.0) then
+ FPresentVpu[i] := (Vpresent / ControlledElement[i].NPhases) / (FRollAvgWindow[i].AvgVal)
+ else
+ if (FVoltage_CurveX_ref = 2) and (FRollAvgWindow[i].AvgVal <> 0.0) then
+ FPresentVpu[i] := (FRollAvgWindow[i].AvgVal) / (basekV * 1000.0)
+ else
+ FPresentVpu[i] := (Vpresent / ControlledElement[i].NPhases) / (basekV * 1000.0);
- if CombiControlMode = 'VV_DRC' then
- begin
- if ((FHitkVALimit[i] = True) or (FHitkvarLimit[i] = True)) and (ActiveCircuit.Solution.Dynavars.dblHour>0.0) then exit;
+ if CombiMode = VV_DRC then
+ begin
+ if ((FHitkVALimit[i] = TRUE) or (FHitkvarLimit[i] = TRUE)) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
+ exit;
// if inverter is off then exit
- if (ControlledElement[i].InverterON = FALSE) and (ControlledElement[i].VarFollowInverter = TRUE) then continue;
+ if (ControlledElement[i].InverterON = FALSE) and (ControlledElement[i].VarFollowInverter = TRUE) then
+ continue;
// if the volt-var curve does not exist, exit
- if Length(Fvvc_curvename) = 0 then
- begin
- DoSimpleMsg('XY Curve object representing vvc1_curve does not exist or is not tied to InvControl.', 382);
- exit
- end;
+ if Fvvc_curve = NIL then
+ begin
+ DoSimpleMsg(_('XY Curve object representing vvc1_curve does not exist or is not tied to InvControl.'), 382);
+ exit
+ end;
//DRC triggers
- if(priorDRCRollAvgWindow[i] = 0.0) then
+ if (priorDRCRollAvgWindow[i] = 0.0) then
+ begin
+ if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance)) then
begin
+ Set_PendingChange(CHANGEDRCVVARLEVEL, i);
- if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance)) then
- begin
- Set_PendingChange(CHANGEDRCVVARLEVEL,i);
-
-
- With ActiveCircuit.Solution.DynaVars Do
- ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
- ('**Ready to change var output due to DRC trigger in VV_DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i],FAvgpVuPrior[i]]));
- end;
+ with ActiveCircuit.Solution.DynaVars do
+ ControlActionHandle := ActiveCircuit.ControlQueue.Push
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ' ' + ControlledElement[i].Name, Format
+ ('**Ready to change var output due to DRC trigger in VV_DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i], FAvgpVuPrior[i]]));
end;
+ end;
+
//Trigger from volt-var mode
- if (FRocEvaluated[i] = False) and (FWithinTolVV[i] = False) then
+ if (FRocEvaluated[i] = FALSE) and (FWithinTolVV[i] = FALSE) then
+ begin
+ if (((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance) or
+ ((Abs(Abs(QoutputVVpu[i]) - Abs(Qdesiredpu[i])) > FVarChangeTolerance))) or
+ (ActiveCircuit.Solution.ControlIteration = 1)) then
begin
- if (((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance) or
- ((Abs(Abs(QoutputVVpu[i]) - Abs(Qdesiredpu[i])) > FVarChangeTolerance))) or
- (ActiveCircuit.Solution.ControlIteration = 1)) then
- begin
- FWithinTolVV[i] := False;
+ FWithinTolVV[i] := FALSE;
- Set_PendingChange(CHANGEDRCVVARLEVEL,i);
- With ActiveCircuit.Solution.DynaVars Do
+ Set_PendingChange(CHANGEDRCVVARLEVEL, i);
+ with ActiveCircuit.Solution.DynaVars do
ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
- ('**Ready to change VV_DRC output due to volt-var trigger in VV_DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g',
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ' ' + ControlledElement[i].Name, Format
+ ('**Ready to change VV_DRC output due to volt-var trigger in VV_DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g',
- [FPresentVpu[i],FAvgpVuPrior[i]]));
+ [FPresentVpu[i], FAvgpVuPrior[i]]));
- end
- else
- begin
+ end
+ else
+ begin
if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) <= FVoltageChangeTolerance) and
- ((Abs(Abs(QoutputVVpu[i]) - Abs(Qdesiredpu[i])) <= FVarChangeTolerance))) then
- FWithinTolVV[i] := True;
+ ((Abs(Abs(QoutputVVpu[i]) - Abs(Qdesiredpu[i])) <= FVarChangeTolerance))) then
+ FWithinTolVV[i] := TRUE;
// If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
// ('**Hit Tolerance with volt-var**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i],FAvgpVuPrior[i]]));
- end;
end;
+ end;
//Trigger for ROC
- if (RateofChangeMode <> INACTIVE) and (ActiveCircuit.Solution.DynaVars.dblHour > 0.0) then
- begin
- if (FWithinTolVV[i] = True) and (FRocEvaluated[i] = False) then
- begin
- FFlagROCOnly[i] := True;
- Set_PendingChange(CHANGEDRCVVARLEVEL,i);
+ if (RateofChangeMode <> INACTIVE) and (ActiveCircuit.Solution.DynaVars.dblHour > 0.0) then
+ begin
+ if (FWithinTolVV[i] = TRUE) and (FRocEvaluated[i] = FALSE) then
+ begin
+ FFlagROCOnly[i] := TRUE;
+ Set_PendingChange(CHANGEDRCVVARLEVEL, i);
- With ActiveCircuit.Solution.DynaVars Do
+ with ActiveCircuit.Solution.DynaVars do
ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
// If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
// ('**Ready to change VV_DRC output due to ROC trigger (ROC)**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i],FAvgpVuPrior[i]]));
- end;
- end;
+ end;
+ end;
- end;
+ end;
// comment
- if CombiControlMode = 'VV_VW' then
- begin
- if ((FHitkVALimit[i] = True) or (FHitkvarLimit[i] = True)) and (ActiveCircuit.Solution.Dynavars.dblHour>0.0) then exit;
+ if CombiMode = VV_VW then
+ begin
+ if ((FHitkVALimit[i] = TRUE) or (FHitkvarLimit[i] = TRUE)) and (ActiveCircuit.Solution.Dynavars.dblHour > 0.0) then
+ exit;
// if ((FHitkVALimit[i] = True) or (FHitkvarLimit[i] = True)) and (ActiveCircuit.Solution.Dynavars.dblHour=0.0) and ((ActiveCircuit.Solution.ControlIteration) >= (0.5*ActiveCircuit.Solution.MaxControlIterations)) then exit;
// if inverter is off then exit
// if (ControlledElement[i].InverterON = FALSE) then exit;
- if (ControlledElement[i].InverterON = FALSE) and (ControlledElement[i].VarFollowInverter = TRUE) then continue;
+ if (ControlledElement[i].InverterON = FALSE) and (ControlledElement[i].VarFollowInverter = TRUE) then
+ continue;
// if volt-watt curve does not exist, exit
- if Length(Fvoltwatt_curvename) = 0 then
- begin
- DoSimpleMsg('XY Curve object representing voltwatt_curve does not exist or is not tied to InvControl.', 381);
- exit
- end;
+ if Fvoltwatt_curve = NIL then
+ begin
+ DoSimpleMsg(_('XY Curve object representing voltwatt_curve does not exist or is not tied to InvControl.'), 381);
+ exit
+ end;
// if inverter is off and varfollowinverter is true, then exit.
- if (ControlledElement[i].InverterON = FALSE) and (ControlledElement[i].VarFollowInverter = TRUE) then continue;
+ if (ControlledElement[i].InverterON = FALSE) and (ControlledElement[i].VarFollowInverter = TRUE) then
+ continue;
// if the volt-var curve does not exist, exit
- if Length(Fvvc_curvename) = 0 then
- begin
- DoSimpleMsg('XY Curve object representing vvc1_curve does not exist or is not tied to InvControl.', 382);
- exit
- end;
+ if Fvvc_curve = NIL then
+ begin
+ DoSimpleMsg(_('XY Curve object representing vvc1_curve does not exist or is not tied to InvControl.'), 382);
+ exit
+ end;
// Trigger from volt-watt mode
- if (FRocEvaluated[i] = False) and (FWithinTolVW[i] = False) then
- begin
- if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance) or (Abs(PNew[i]-POld[i])>FActivePChangeTolerance) or
- (ActiveCircuit.Solution.ControlIteration = 1)) and (FROCEvaluated[i] = False) then
+ if (FRocEvaluated[i] = FALSE) and (FWithinTolVW[i] = FALSE) then
+ begin
+ if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance) or (Abs(PNew[i] - POld[i]) > FActivePChangeTolerance) or
+ (ActiveCircuit.Solution.ControlIteration = 1)) and (FROCEvaluated[i] = FALSE) then
+ begin
+ FWithinTolVW[i] := FALSE;
+ FFlagROCOnly[i] := FALSE;
+ Set_PendingChange(CHANGEWATTVARLEVEL, i);
+
+ with ActiveCircuit.Solution.DynaVars do
+ ControlActionHandle := ActiveCircuit.ControlQueue.Push
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ' ' + ControlledElement[i].Name, Format
+ ('**Ready to change VV_VW output due to volt-watt trigger**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i], FAvgpVuPrior[i]]));
+ end
+ else
begin
- FWithinTolVW[i] := False;
- FFlagROCOnly[i] := False;
- Set_PendingChange(CHANGEWATTVARLEVEL,i);
-
- With ActiveCircuit.Solution.DynaVars Do
- ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
- ('**Ready to change VV_VW output due to volt-watt trigger**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i],FAvgpVuPrior[i]]));
- end
- else
- begin
if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) <= FVoltageChangeTolerance) or
- (Abs(PNew[i]-Pold[i])<=FActivePChangeTolerance)) then
- FWithinTolVW[i] := True;
- FFlagROCOnly[i] := False;
- end;
+ (Abs(PNew[i] - Pold[i]) <= FActivePChangeTolerance)) then
+ FWithinTolVW[i] := TRUE;
+ FFlagROCOnly[i] := FALSE;
end;
+ end;
//Trigger from volt-var mode
- if (FRocEvaluated[i] = False) and (FWithinTolVV[i] = False) then
+ if (FRocEvaluated[i] = FALSE) and (FWithinTolVV[i] = FALSE) then
+ begin
+ if (((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance) or
+ ((Abs(Abs(Qoutputpu[i]) - Abs(Qdesiredpu[i])) > FVarChangeTolerance))) or
+ (ActiveCircuit.Solution.ControlIteration = 1)) then
begin
- if (((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance) or
- ((Abs(Abs(Qoutputpu[i]) - Abs(Qdesiredpu[i])) > FVarChangeTolerance))) or
- (ActiveCircuit.Solution.ControlIteration = 1)) then
- begin
- FWithinTolVV[i] := False;
+ FWithinTolVV[i] := FALSE;
- Set_PendingChange(CHANGEWATTVARLEVEL,i);
- With ActiveCircuit.Solution.DynaVars Do
+ Set_PendingChange(CHANGEWATTVARLEVEL, i);
+ with ActiveCircuit.Solution.DynaVars do
ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
- ('**Ready to change VV_VW output due to volt-var trigger**, Vavgpu= %.5g, VPriorpu=%.5g',
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ' ' + ControlledElement[i].Name, Format
+ ('**Ready to change VV_VW output due to volt-var trigger**, Vavgpu= %.5g, VPriorpu=%.5g',
- [FPresentVpu[i],FAvgpVuPrior[i]]));
+ [FPresentVpu[i], FAvgpVuPrior[i]]));
- end
- else
- begin
+ end
+ else
+ begin
if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) <= FVoltageChangeTolerance) and
- ((Abs(Abs(Qoutputpu[i]) - Abs(Qdesiredpu[i])) <= FVarChangeTolerance))) then
- FWithinTolVV[i] := True;
+ ((Abs(Abs(Qoutputpu[i]) - Abs(Qdesiredpu[i])) <= FVarChangeTolerance))) then
+ FWithinTolVV[i] := TRUE;
// If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
// ('**Hit Tolerance with volt-var**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i],FAvgpVuPrior[i]]));
- end;
end;
+ end;
//Trigger for ROC
- if (RateofChangeMode <> INACTIVE) and (ActiveCircuit.Solution.DynaVars.dblHour > 0.0) then
- begin
- if (FWithinTolVV[i] = True) and (FWithinTolVW[i] = True) and (FRocEvaluated[i] = False) then
- begin
- FFlagROCOnly[i] := True;
- Set_PendingChange(CHANGEWATTVARLEVEL,i);
+ if (RateofChangeMode <> INACTIVE) and (ActiveCircuit.Solution.DynaVars.dblHour > 0.0) then
+ begin
+ if (FWithinTolVV[i] = TRUE) and (FWithinTolVW[i] = TRUE) and (FRocEvaluated[i] = FALSE) then
+ begin
+ FFlagROCOnly[i] := TRUE;
+ Set_PendingChange(CHANGEWATTVARLEVEL, i);
- With ActiveCircuit.Solution.DynaVars Do
+ with ActiveCircuit.Solution.DynaVars do
ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
// If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
// ('**Ready to change VV_VW output due to volt-watt trigger (ROC)**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i],FAvgpVuPrior[i]]));
- end;
- end;
+ end;
+ end;
+
+ end;
- end;
+ if ControlMode = VOLTWATT then // volt-watt control mode
+ begin
+ if (ControlledElement[i].InverterON = FALSE) then
+ continue;
- if ControlMode = 'VOLTWATT' then // volt-watt control mode
+ if Fvoltwatt_curve = NIL then
begin
- if (ControlledElement[i].InverterON = FALSE) then continue;
+ DoSimpleMsg(_('XY Curve object representing voltwatt_curve does not exist or is not tied to InvControl.'), 381);
+ exit
+ end;
- if Length(Fvoltwatt_curvename) = 0 then
- begin
- DoSimpleMsg('XY Curve object representing voltwatt_curve does not exist or is not tied to InvControl.', 381);
- exit
- end;
+ ControlledElement[i].VWmode := TRUE;
+ if (FRocEvaluated[i] = FALSE) and (FWithinTolVW[i] = FALSE) then
+ begin
+ if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance) or (Abs(PNew[i] - POld[i]) > FActivePChangeTolerance) or
+ (ActiveCircuit.Solution.ControlIteration = 1)) and (FROCEvaluated[i] = FALSE) then
- ControlledElement[i].VWmode := TRUE;
- if (FRocEvaluated[i] = False) and (FWithinTolVW[i] = False) then
- begin
- if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance) or (Abs(PNew[i]-POld[i])>FActivePChangeTolerance) or
- (ActiveCircuit.Solution.ControlIteration = 1)) and (FROCEvaluated[i] = False) then
+ begin
+ FWithinTolVW[i] := FALSE;
+ FFlagROCOnly[i] := FALSE;
+ Set_PendingChange(CHANGEWATTLEVEL, i);
+ with ActiveCircuit.Solution.DynaVars do
+ ControlActionHandle := ActiveCircuit.ControlQueue.Push
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ' ' + ControlledElement[i].Name, Format
+ ('**Ready to change watt output due in VOLTWATT mode**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i], FAvgpVuPrior[i]]));
+ end
+ else
begin
- FWithinTolVW[i] := False;
- FFlagROCOnly[i] := False;
- Set_PendingChange(CHANGEWATTLEVEL,i);
-
- With ActiveCircuit.Solution.DynaVars Do
- ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
- ('**Ready to change watt output due in VOLTWATT mode**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i],FAvgpVuPrior[i]]));
- end
-
- else
- begin
if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) <= FVoltageChangeTolerance) or
- (Abs(PNew[i]-Pold[i])<=FActivePChangeTolerance)) then
- FWithinTolVW[i] := True;
- FFlagROCOnly[i] := False;
- end;
+ (Abs(PNew[i] - Pold[i]) <= FActivePChangeTolerance)) then
+ FWithinTolVW[i] := TRUE;
+ FFlagROCOnly[i] := FALSE;
end;
- if (RateofChangeMode <> INACTIVE) and (ActiveCircuit.Solution.DynaVars.dblHour > 0.0) then
- begin
- if (FWithinTol[i] = True) and (FRocEvaluated[i] = False) then
- begin
- FFlagROCOnly[i] := True;
- Set_PendingChange(CHANGEWATTLEVEL,i);
-
- With ActiveCircuit.Solution.DynaVars Do
+ end;
+ if (RateofChangeMode <> INACTIVE) and (ActiveCircuit.Solution.DynaVars.dblHour > 0.0) then
+ begin
+ if (FWithinTol[i] = TRUE) and (FRocEvaluated[i] = FALSE) then
+ begin
+ FFlagROCOnly[i] := TRUE;
+ Set_PendingChange(CHANGEWATTLEVEL, i);
+
+ with ActiveCircuit.Solution.DynaVars do
ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
// If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
// ('**Ready to change watt output in VOLTWATT mode (ROC)**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i],FAvgpVuPrior[i]]));
- end
- else
- begin
- end;
- end;
- end;
+ end
+ else
+ begin
+ end;
+ end;
+ end;
- if ControlMode = 'VOLTVAR' then // volt-var control mode
+ if ControlMode = VOLTVAR then // volt-var control mode
+ begin
+ if (ControlledElement[i].InverterON = FALSE) and (ControlledElement[i].VarFollowInverter = TRUE) then
+ continue;
+ ControlledElement[i].VWmode := FALSE;
+ if Fvvc_curve = NIL then
begin
-
- if (ControlledElement[i].InverterON = FALSE) and (ControlledElement[i].VarFollowInverter = TRUE) then continue;
- ControlledElement[i].VWmode := FALSE;
- if Length(Fvvc_curvename) = 0 then
- begin
- DoSimpleMsg('XY Curve object representing vvc1_curve does not exist or is not tied to InvControl.', 382);
- exit
- end;
+ DoSimpleMsg(_('XY Curve object representing vvc1_curve does not exist or is not tied to InvControl.'), 382);
+ exit
+ end;
//Trigger from volt-var mode
- if (FRocEvaluated[i] = False) and (FWithinTolVV[i] = False) then
+ if (FRocEvaluated[i] = FALSE) and (FWithinTolVV[i] = FALSE) then
+ begin
+ if (((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance) or
+ ((Abs(Abs(QoutputVVpu[i]) - Abs(Qdesiredpu[i])) > FVarChangeTolerance))) or
+ (ActiveCircuit.Solution.ControlIteration = 1)) then
begin
- if (((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance) or
- ((Abs(Abs(QoutputVVpu[i]) - Abs(Qdesiredpu[i])) > FVarChangeTolerance))) or
- (ActiveCircuit.Solution.ControlIteration = 1)) then
- begin
- FWithinTolVV[i] := False;
+ FWithinTolVV[i] := FALSE;
- Set_PendingChange(CHANGEVARLEVEL,i);
- With ActiveCircuit.Solution.DynaVars Do
+ Set_PendingChange(CHANGEVARLEVEL, i);
+ with ActiveCircuit.Solution.DynaVars do
ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
- ('**Ready to change var output due to volt-var trigger in volt-var mode**, Vavgpu= %.5g, VPriorpu=%.5g',
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ' ' + ControlledElement[i].Name, Format
+ ('**Ready to change var output due to volt-var trigger in volt-var mode**, Vavgpu= %.5g, VPriorpu=%.5g',
- [FPresentVpu[i],FAvgpVuPrior[i]]));
+ [FPresentVpu[i], FAvgpVuPrior[i]]));
- end
- else
- begin
+ end
+ else
+ begin
if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) <= FVoltageChangeTolerance) and
- ((Abs(Abs(QoutputVVpu[i]) - Abs(Qdesiredpu[i])) <= FVarChangeTolerance))) then
- FWithinTolVV[i] := True;
+ ((Abs(Abs(QoutputVVpu[i]) - Abs(Qdesiredpu[i])) <= FVarChangeTolerance))) then
+ FWithinTolVV[i] := TRUE;
// If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
// ('**Hit Tolerance with volt-var**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i],FAvgpVuPrior[i]]));
- end;
end;
+ end;
//Trigger for ROC
- if (RateofChangeMode <> INACTIVE) and (ActiveCircuit.Solution.DynaVars.dblHour > 0.0) then
- begin
- if (FWithinTolVV[i] = True) and (FRocEvaluated[i] = False) then
- begin
- FFlagROCOnly[i] := True;
- Set_PendingChange(CHANGEVARLEVEL,i);
+ if (RateofChangeMode <> INACTIVE) and (ActiveCircuit.Solution.DynaVars.dblHour > 0.0) then
+ begin
+ if (FWithinTolVV[i] = TRUE) and (FRocEvaluated[i] = FALSE) then
+ begin
+ FFlagROCOnly[i] := TRUE;
+ Set_PendingChange(CHANGEVARLEVEL, i);
- With ActiveCircuit.Solution.DynaVars Do
+ with ActiveCircuit.Solution.DynaVars do
ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
// If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
// ('**Ready to change var output due to ROC trigger (ROC) in volt-var mode**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i],FAvgpVuPrior[i]]));
- end;
- end;
-
+ end;
end;
- if ControlMode = 'DYNAMICREACCURR' then // dynamic reactive current control mode
- begin
- if (ControlledElement[i].InverterON = FALSE) and (ControlledElement[i].VarFollowInverter = TRUE) then continue;
+ end;
+
+ if ControlMode = DRC then // dynamic reactive current control mode
+ begin
+ if (ControlledElement[i].InverterON = FALSE) and (ControlledElement[i].VarFollowInverter = TRUE) then
+ continue;
ControlledElement[i].VWmode := FALSE;
//DRC triggers
- if(priorDRCRollAvgWindow[i] = 0.0) then
+ if (priorDRCRollAvgWindow[i] = 0.0) then
+ begin
+ if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance)) then
begin
+ Set_PendingChange(CHANGEVARLEVEL, i);
- if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance)) then
- begin
- Set_PendingChange(CHANGEVARLEVEL,i);
-
-
- With ActiveCircuit.Solution.DynaVars Do
- ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
- ('**Ready to change var output due in DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i],FAvgpVuPrior[i]]));
- end;
+ with ActiveCircuit.Solution.DynaVars do
+ ControlActionHandle := ActiveCircuit.ControlQueue.Push
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ' ' + ControlledElement[i].Name, Format
+ ('**Ready to change var output due in DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i], FAvgpVuPrior[i]]));
end;
- if (FRocEvaluated[i] = False) and (FWithinTol[i] = False) then
- begin
- if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance) or
+ end;
+
+ if (FRocEvaluated[i] = FALSE) and (FWithinTol[i] = FALSE) then
+ begin
+ if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) > FVoltageChangeTolerance) or
// (Abs(Abs(QoutputDRCpu[i]) - Abs(Qdesiredpu[i])) > FVarChangeTolerance) or // TEMc; also tried checking against QDRCdesiredpu
- (ActiveCircuit.Solution.ControlIteration = 1)) then
- begin
- FWithinTol[i] := False;
+ (ActiveCircuit.Solution.ControlIteration = 1)) then
+ begin
+ FWithinTol[i] := FALSE;
- Set_PendingChange(CHANGEVARLEVEL,i);
- With ActiveCircuit.Solution.DynaVars Do
+ Set_PendingChange(CHANGEVARLEVEL, i);
+ with ActiveCircuit.Solution.DynaVars do
ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
- ('**Ready to change DRC output because V or Q out of tolerance**, Vavgpu= %.5g, VPriorpu=%.5g, QoutPU=%.3g, QdesiredPU=%.3g, QDRCdesiredPU=%.3g',
- [FPresentVpu[i],FAvgpVuPrior[i],QoutputDRCpu[i],Qdesiredpu[i],QDRCdesiredpu[i]]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ' ' + ControlledElement[i].Name, Format
+ ('**Ready to change DRC output because V or Q out of tolerance**, Vavgpu= %.5g, VPriorpu=%.5g, QoutPU=%.3g, QdesiredPU=%.3g, QDRCdesiredPU=%.3g',
+ [FPresentVpu[i], FAvgpVuPrior[i], QoutputDRCpu[i], Qdesiredpu[i], QDRCdesiredpu[i]]));
- end
- else
- begin
+ end
+ else
+ begin
if ((Abs(FPresentVpu[i] - FAvgpVuPrior[i]) <= FVoltageChangeTolerance) and
- ((Abs(Abs(QoutputDRCpu[i]) - Abs(Qdesiredpu[i])) <= FVarChangeTolerance))) then
- FWithinTol[i] := True;
- If ShowEventLog Then AppendtoEventLog('InvControl.' + Self.Name+' '+ControlledElement[i].Name, Format
- ('**Hit Tolerance with DRCvar**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i],FAvgpVuPrior[i]]));
+ ((Abs(Abs(QoutputDRCpu[i]) - Abs(Qdesiredpu[i])) <= FVarChangeTolerance))) then
+ FWithinTol[i] := TRUE;
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ' ' + ControlledElement[i].Name, Format
+ ('**Hit Tolerance with DRCvar**, Vavgpu= %.5g, VPriorpu=%.5g', [FPresentVpu[i], FAvgpVuPrior[i]]));
- end;
end;
end;
- end;
- end;
-
-
-end;
-
-
-procedure TInvControlObj.InitPropertyValues(ArrayOffset: Integer);
-begin
- PropertyValue[1] := ''; //PVSystem list
- PropertyValue[2] := 'VOLTVAR'; // initial mode
- PropertyValue[3] := ''; // initial combination mode
- PropertyValue[4] := '';
- PropertyValue[5] := '0';
- PropertyValue[6] := 'rated';
- PropertyValue[7] := '0s';
-
- PropertyValue[8] := 'NONE'; // voltwatt_curve
-
- PropertyValue[9] := '0.95'; //'DbVMin';
- PropertyValue[10] := '1.05'; // 'DbVMax';
- PropertyValue[11] := '0.1'; // 'ArGraLowV';
- PropertyValue[12] := '0.1'; // 'ArGraHiV';
- PropertyValue[13] := '0s'; // 'Rollingavgwindowlen';
- PropertyValue[14] := '0.7'; // DeltaQ_factor
- PropertyValue[15] := '0.0001'; //VoltageChangeTolerance
- PropertyValue[16] := '0.025'; // Varchangetolerance
- PropertyValue[17] := 'PMPPPU'; // Voltwatt y axis units
- PropertyValue[18] := 'INACTIVE'; //rate of change limit
- PropertyValue[19] := '0.0'; // LPF tau constant, in seconds
- PropertyValue[20] := '-1.0'; // Rise/fall Limit
- PropertyValue[21] := '1.0'; // deltaP_factor
- PropertyValue[22] := 'yes'; // show event log?
- PropertyValue[23] := 'VARAVAL'; // y-axis reference (and power precedence) for volt-var
- PropertyValue[24] := '0.01';
-
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
+ end;
+ end;
+ end;
end;
-Function TInvControlObj.MakePVSystemList:Boolean;
+function TInvControlObj.MakePVSystemList: Boolean;
-VAR
- PVSysClass:TDSSClass;
- PVSys:TPVsystemObj;
- i,j:Integer;
+var
+ PVSysClass: TDSSClass;
+ PVSys: TPVsystemObj;
+ i, j: Integer;
begin
+ Result := FALSE;
+ PVSysClass := GetDSSClassPtr(DSS, 'PVsystem');
+
+ if FListSize > 0 then
+ begin // Name list is defined - Use it
+
+ SetLength(CondOffset, FListSize + 1);
+ SetLength(cBuffer, FListSize + 1, 7); // assuming no more than 6 conductors
+
+
+ SetLength(ControlledElement, FListSize + 1); // Use this as the main pointer to PVSystem Elements
+
+ SetLength(FkWLimit, FListSize + 1);
+ SetLength(FkVALimit, FListSize + 1);
+ SetLength(FkvarLimit, FListSize + 1);
+ SetLength(FVref, FListSize + 1);
+ SetLength(FPpf, FListSize + 1);
+ SetLength(Fpresentkvar, FListSize + 1);
+ SetLength(FpresentkW, FListSize + 1);
+ SetLength(FAvgpVuPrior, FListSize + 1);
+ SetLength(FPresentVpu, FListSize + 1);
+
+ SetLength(NPhasesPVSys, FListSize + 1);
+ SetLength(NCondsPVSys, FListSize + 1);
+
+ SetLength(FPendingChange, FListSize + 1);
+ SetLength(FFlagROCOnly, FListSize + 1);
+ SetLength(QDeliver, FListSize + 1);
+ SetLength(QNew, FListSize + 1);
+ SetLength(QOld, FListSize + 1);
+ SetLength(QOldVV, FListSize + 1);
+ SetLength(QOldDRC, FListSize + 1);
+ SetLength(QDRCNew, FListSize + 1);
+ SetLength(QHeadroom, FListSize + 1);
+ SetLength(Qoutputpu, FListSize + 1);
+ SetLength(QoutputVVpu, FListSize + 1);
+ SetLength(QoutputDRCpu, FListSize + 1);
+ SetLength(Qdesiredpu, FListSize + 1);
+ SetLength(QDRCdesiredpu, FListSize + 1);
+ SetLength(deltaVDynReac, FListSize + 1);
+ SetLength(PNew, FListSize + 1);
+ SetLength(POld, FListSize + 1);
+
+ SetLength(FVpuSolution, FListSize + 1, 2 + 1);
+ SetLength(FRollAvgWindow, FListSize + 1);
+ SetLength(FDRCRollAvgWindow, FListSize + 1);
+
+ SetLength(priorRollAvgWindow, FListSize + 1);
+ SetLength(priorDRCRollAvgWindow, FListSize + 1);
+ SetLength(FlagChangeCurve, FListSize + 1);
+ SetLength(FActiveVVCurve, FListSize + 1);
+ SetLength(FPriorWattspu, FListSize + 1);
+ SetLength(FPriorvarspu, FListSize + 1);
+ SetLength(FLPFTime, FListSize + 1);
+ SetLength(FWithinTol, FListSize + 1);
+ SetLength(FWithinTolVV, FListSize + 1);
+ SetLength(FWithinTolVW, FListSize + 1);
+ SetLength(FROCEvaluated, FListSize + 1);
+ SetLength(FHitkVALimit, FListSize + 1);
+ SetLength(FHitkvarLimit, FListSize + 1);
+
+
+ SetLength(FFinalpuPmpp, FListSize + 1);
+ SetLength(FFinalkvar, FListSize + 1);
+
+
+ for i := 1 to FListSize do
+ begin
+ PVSys := PVSysClass.Find(FPVSystemNameList.Strings[i - 1]);
+ if Assigned(PVSys) and PVSys.Enabled then
+ FPVSystemPointerList.Add(PVSys);
+ end;
- Result := FALSE;
- PVSysClass := GetDSSClassPtr(DSS, 'PVsystem');
-
- If FListSize > 0 Then
- Begin // Name list is defined - Use it
-
- SetLength(CondOffset,FListSize+1);
- SetLength(cBuffer,FListSize+1,7); // assuming no more than 6 conductors
-
-
- SetLength(ControlledElement,FListSize+1); // Use this as the main pointer to PVSystem Elements
-
- SetLength(FkWLimit,FListSize+1);
- SetLength(FkVALimit,FListSize+1);
- SetLength(FkvarLimit,FListSize+1);
- SetLength(FVref,FListSize+1);
- SetLength(FPpf,FListSize+1);
- SetLength(Fpresentkvar,FListSize+1);
- SetLength(FpresentkW,FListSize+1);
- SetLength(FAvgpVuPrior, FListSize+1);
- SetLength(FPresentVpu, FListSize+1);
-
- SetLength(NPhasesPVSys,FListSize+1);
- SetLength(NCondsPVSys,FListSize+1);
-
- SetLength(FPendingChange,FListSize+1);
- SetLength(FFlagROCOnly,FListSize+1);
- SetLength(QDeliver,FListSize+1);
- SetLength(QNew,FListSize+1);
- SetLength(QOld,FListSize+1);
- SetLength(QOldVV,FListSize+1);
- SetLength(QOldDRC,FListSize+1);
- SetLength(QDRCNew,FListSize+1);
- SetLength(QHeadroom,FListSize+1);
- SetLength(Qoutputpu,FListSize+1);
- SetLength(QoutputVVpu,FListSize+1);
- SetLength(QoutputDRCpu,FListSize+1);
- SetLength(Qdesiredpu,FListSize+1);
- SetLength(QDRCdesiredpu,FListSize+1);
- SetLength(deltaVDynReac,FListSize+1);
- SetLength(PNew,FListSize+1);
- SetLength(POld,FListSize+1);
-
- SetLength(FVpuSolution,FListSize+1,2+1);
- SetLength(FRollAvgWindow,FListSize+1);
- SetLength(FDRCRollAvgWindow, FListSize+1);
-
- SetLength(priorRollAvgWindow,FListSize+1);
- SetLength(priorDRCRollAvgWindow,FListSize+1);
- SetLength(FlagChangeCurve,FListSize+1);
- SetLength(FActiveVVCurve, FListSize+1);
- SetLength(FPriorWattspu, FListSize+1);
- SetLength(FPriorvarspu, FListSize+1);
- SetLength(FLPFTime, FListSize+1);
- SetLength(FWithinTol, FListSize+1);
- SetLength(FWithinTolVV, FListSize+1);
- SetLength(FWithinTolVW, FListSize+1);
- SetLength(FROCEvaluated, FListSize+1);
- SetLength(FHitkVALimit, FListSize+1);
- SetLength(FHitkvarLimit, FListSize+1);
-
-
-
- SetLength(FFinalpuPmpp, FListSize+1);
- SetLength(FFinalkvar, FListSize+1);
-
-
-
- For i := 1 to FListSize Do Begin
- PVSys := PVSysClass.Find(FPVSystemNameList.Strings[i-1]);
- If Assigned(PVSys) and PVSys.Enabled Then FPVSystemPointerList.New := PVSys;
- End;
-
- End
- Else Begin
+ end
+ else
+ begin
{Search through the entire circuit for enabled pvsysten objects and add them to the list}
- For i := 1 to PVSysClass.ElementCount Do Begin
- PVSys := PVSysClass.ElementList.Get(i);
- If PVSys.Enabled Then FPVSystemPointerList.New := PVSys;
+ for i := 1 to PVSysClass.ElementCount do
+ begin
+ PVSys := PVSysClass.ElementList.Get(i);
+ if PVSys.Enabled then
+ FPVSystemPointerList.Add(PVSys);
FPVSystemNameList.Add(PVSys.Name);
- End;
-
-
- FListSize := FPVSystemPointerList.Count;
-
- SetLength(ControlledElement,FListSize+1);
-
- SetLength(FkWLimit,FListSize+1);
- SetLength(FkVALimit,FListSize+1);
- SetLength(FkvarLimit,FListSize+1);
- SetLength(FVref,FListSize+1);
- SetLength(FPpf,FListSize+1);
- SetLength(Fpresentkvar,FListSize+1);
- SetLength(FpresentkW,FListSize+1);
- SetLength(FAvgpVuPrior, FListSize+1);
- SetLength(FPresentVpu, FListSize+1);
-
- SetLength(NPhasesPVSys,FListSize+1);
- SetLength(NCondsPVSys,FListSize+1);
- SetLength(CondOffset,FListSize+1);
- SetLength(cBuffer,FListSize+1,7); // assuming no more than 6 conductors
- SetLength(FPendingChange,FListSize+1);
- SetLength(FFlagROCOnly,FListSize+1);
-
- SetLength(QDeliver,FListSize+1);
- SetLength(QNew,FListSize+1);
- SetLength(QOld,FListSize+1);
- SetLength(QOldVV,FListSize+1);
- SetLength(QOldDRC,FListSize+1);
- SetLength(QDRCNew,FListSize+1);
- SetLength(QHeadroom,FListSize+1);
- SetLength(Qoutputpu,FListSize+1);
- SetLength(QoutputVVpu,FListSize+1);
- SetLength(QoutputDRCpu,FListSize+1);
- SetLength(Qdesiredpu,FListSize+1);
- SetLength(QDRCdesiredpu,FListSize+1);
- SetLength(PNew,FListSize+1);
- SetLength(POld,FListSize+1);
-
- SetLength(FRollAvgWindow,FListSize+1);
- SetLength(FDRCRollAvgWindow, FListSize+1);
-
- SetLength(deltaVDynReac,FListSize+1);
- SetLength(priorRollAvgWindow,FListSize+1);
- SetLength(priorDRCRollAvgWindow,FListSize+1);
- SetLength(FVpuSolution,FListSize+1,2+1);
- SetLength(FlagChangeCurve,FListSize+1);
- SetLength(FActiveVVCurve, FListSize+1);
- SetLength(FPriorWattspu, FListSize+1);
- SetLength(FPriorvarspu, FListSize+1);
- SetLength(FLPFTime, FListSize+1);
- SetLength(FWithinTol, FListSize+1);
- SetLength(FWithinTolVV, FListSize+1);
- SetLength(FWithinTolVW, FListSize+1);
- SetLength(FROCEvaluated, FListSize+1);
- SetLength(FHitkVALimit, FListSize+1);
- SetLength(FHitkvarLimit, FListSize+1);
-
-
- SetLength(FFinalpuPmpp, FListSize+1);
- SetLength(FFinalkvar, FListSize+1);
-
- End; {Else}
+ end;
+
+
+ FListSize := FPVSystemPointerList.Count;
+
+ SetLength(ControlledElement, FListSize + 1);
+
+ SetLength(FkWLimit, FListSize + 1);
+ SetLength(FkVALimit, FListSize + 1);
+ SetLength(FkvarLimit, FListSize + 1);
+ SetLength(FVref, FListSize + 1);
+ SetLength(FPpf, FListSize + 1);
+ SetLength(Fpresentkvar, FListSize + 1);
+ SetLength(FpresentkW, FListSize + 1);
+ SetLength(FAvgpVuPrior, FListSize + 1);
+ SetLength(FPresentVpu, FListSize + 1);
+
+ SetLength(NPhasesPVSys, FListSize + 1);
+ SetLength(NCondsPVSys, FListSize + 1);
+ SetLength(CondOffset, FListSize + 1);
+ SetLength(cBuffer, FListSize + 1, 7); // assuming no more than 6 conductors
+ SetLength(FPendingChange, FListSize + 1);
+ SetLength(FFlagROCOnly, FListSize + 1);
+
+ SetLength(QDeliver, FListSize + 1);
+ SetLength(QNew, FListSize + 1);
+ SetLength(QOld, FListSize + 1);
+ SetLength(QOldVV, FListSize + 1);
+ SetLength(QOldDRC, FListSize + 1);
+ SetLength(QDRCNew, FListSize + 1);
+ SetLength(QHeadroom, FListSize + 1);
+ SetLength(Qoutputpu, FListSize + 1);
+ SetLength(QoutputVVpu, FListSize + 1);
+ SetLength(QoutputDRCpu, FListSize + 1);
+ SetLength(Qdesiredpu, FListSize + 1);
+ SetLength(QDRCdesiredpu, FListSize + 1);
+ SetLength(PNew, FListSize + 1);
+ SetLength(POld, FListSize + 1);
+
+ SetLength(FRollAvgWindow, FListSize + 1);
+ SetLength(FDRCRollAvgWindow, FListSize + 1);
+
+ SetLength(deltaVDynReac, FListSize + 1);
+ SetLength(priorRollAvgWindow, FListSize + 1);
+ SetLength(priorDRCRollAvgWindow, FListSize + 1);
+ SetLength(FVpuSolution, FListSize + 1, 2 + 1);
+ SetLength(FlagChangeCurve, FListSize + 1);
+ SetLength(FActiveVVCurve, FListSize + 1);
+ SetLength(FPriorWattspu, FListSize + 1);
+ SetLength(FPriorvarspu, FListSize + 1);
+ SetLength(FLPFTime, FListSize + 1);
+ SetLength(FWithinTol, FListSize + 1);
+ SetLength(FWithinTolVV, FListSize + 1);
+ SetLength(FWithinTolVW, FListSize + 1);
+ SetLength(FROCEvaluated, FListSize + 1);
+ SetLength(FHitkVALimit, FListSize + 1);
+ SetLength(FHitkvarLimit, FListSize + 1);
+
+
+ SetLength(FFinalpuPmpp, FListSize + 1);
+ SetLength(FFinalkvar, FListSize + 1);
+
+ end; {Else}
//Initialize arrays
- For i := 1 to FlistSize Do
- begin
- PVSys := PVSysClass.Find(FPVSystemNameList.Strings[i-1]);
-
- For j := 1 to 6 Do cBuffer[i,j] := cZERO;
-
- Set_NTerms(PVSys.NTerms);
-
-
- FkWLimit[i] := 0.0;
- FkVALimit[i] := 0.0;
- FkvarLimit[i] := 0.0;
- FVref[i] := 0.0;
- FPpf[i] := 0.0;
- Fpresentkvar[i] := 0.0;
- FpresentkW[i] := 0.0;
- CondOffset[i] := 0;
- NPhasesPVSys[i] := PVSys.NPhases;
- NCondsPVSys[i] := PVSys.NConds;
- FAvgpVuPrior[i] := 0.0;
- FPresentVpu[i] := 0.0;
- QDeliver[i] := 0.0;
- QNew[i] := 0.0;
- QOld[i] := -1.0;
- QOldVV[i] := -1.0;
- QOldDRC[i] := -1.0;
- QDRCNew[i] := 0.0;
- PNew[i] := 0.0;
- POld[i] := 0.0;
- QHeadroom[i] :=0.0;
- Qoutputpu[i] :=0.0;
- QoutputVVpu[i] :=0.0;
- QoutputDRCpu[i] :=0.0;
- Qdesiredpu[i] :=0.0;
- QDRCdesiredpu[i] :=0.0;
- FRollAvgWindow[i] := TRollAvgWindow.Create;
-// FRollAvgWindow[i].BuffLength := FRollAvgWindowLength;
- FDRCRollAvgWindow[i] := TRollAvgWindow.Create;
-// FDRCRollAvgWindow[i].BuffLength := FDRCRollAvgWindowLength;
-
- deltaVDynReac[i] := 0.0;
- FlagChangeCurve[i] := False;
- FActiveVVCurve[i] := 1;
- priorRollAvgWindow[i] := 0.0;
- priorDRCRollAvgWindow[i] := 0.0;
- FPriorWattspu[i] := 0.0;
- FPriorvarspu[i] := 0.0;
- FLPFTime[i] := 0.0;
- FWithinTol[i] := False;
- FWithinTolVV[i] := False;
- FWithinTolVW[i] := False;
- FROCEvaluated[i] := False;
- FHitkVALimit[i] := False;
- FHitkvarLimit[i] := False;
-
- for j := 1 to 2 do FVpuSolution[i,j] :=0.0;
-
-
- FFinalpuPmpp[i] := 0.0;
- FFinalkvar[i] := 0.0;
-
- FPendingChange[i] := NONE;
- FFlagROCOnly[i] := False;
- end; {For}
-
- RecalcElementData;
- If FPVSystemPointerList.Count>0 Then Result := TRUE;
+ for i := 1 to FlistSize do
+ begin
+ PVSys := PVSysClass.Find(FPVSystemNameList.Strings[i - 1]);
+
+ for j := 1 to 6 do
+ cBuffer[i, j] := cZERO;
+
+ Set_NTerms(PVSys.NTerms);
+
+
+ FkWLimit[i] := 0.0;
+ FkVALimit[i] := 0.0;
+ FkvarLimit[i] := 0.0;
+ FVref[i] := 0.0;
+ FPpf[i] := 0.0;
+ Fpresentkvar[i] := 0.0;
+ FpresentkW[i] := 0.0;
+ CondOffset[i] := 0;
+ NPhasesPVSys[i] := PVSys.NPhases;
+ NCondsPVSys[i] := PVSys.NConds;
+ FAvgpVuPrior[i] := 0.0;
+ FPresentVpu[i] := 0.0;
+ QDeliver[i] := 0.0;
+ QNew[i] := 0.0;
+ QOld[i] := -1.0;
+ QOldVV[i] := -1.0;
+ QOldDRC[i] := -1.0;
+ QDRCNew[i] := 0.0;
+ PNew[i] := 0.0;
+ POld[i] := 0.0;
+ QHeadroom[i] := 0.0;
+ Qoutputpu[i] := 0.0;
+ QoutputVVpu[i] := 0.0;
+ QoutputDRCpu[i] := 0.0;
+ Qdesiredpu[i] := 0.0;
+ QDRCdesiredpu[i] := 0.0;
+ FRollAvgWindow[i] := TRollAvgWindow.Create;
+ FDRCRollAvgWindow[i] := TRollAvgWindow.Create;
+ deltaVDynReac[i] := 0.0;
+ FlagChangeCurve[i] := FALSE;
+ FActiveVVCurve[i] := 1;
+ priorRollAvgWindow[i] := 0.0;
+ priorDRCRollAvgWindow[i] := 0.0;
+ FPriorWattspu[i] := 0.0;
+ FPriorvarspu[i] := 0.0;
+ FLPFTime[i] := 0.0;
+ FWithinTol[i] := FALSE;
+ FWithinTolVV[i] := FALSE;
+ FWithinTolVW[i] := FALSE;
+ FROCEvaluated[i] := FALSE;
+ FHitkVALimit[i] := FALSE;
+ FHitkvarLimit[i] := FALSE;
+
+ for j := 1 to 2 do
+ FVpuSolution[i, j] := 0.0;
+
+
+ FFinalpuPmpp[i] := 0.0;
+ FFinalkvar[i] := 0.0;
+
+ FPendingChange[i] := NONE;
+ FFlagROCOnly[i] := FALSE;
+ end; {For}
+
+ RecalcElementData;
+ if FPVSystemPointerList.Count > 0 then
+ Result := TRUE;
end;
-
procedure TInvControlObj.Reset;
begin
// inherited;
-
end;
-//----------------------------------------------------------------------------
-
-{ -------------------------------------------------------------------------- }
-
-Function TInvControl.GetXYCurve(Const CurveName: AnsiString;InvControlMode: AnsiString): TXYcurveObj;
-VAR
- i: Integer;
-Begin
-
- Result := XY_CurveClass.Find(CurveName);
-
- IF Result = NIL THEN begin
- DoSimpleMsg('XY Curve object: "' + CurveName + '" representing VOLTWATT or VOLTVAR curve (depending on mode) not found.', 380);
- Exit;
- end;
-
-
- // If VOLTWATT control mode then check for any negative watt values (pu)
- // and values greater than 1.0 per-unit (=100 percent output)
- if InvControlMode = 'VOLTWATT' then
- begin
- for i:= 1 to Result.NumPoints do
- begin
- if (Result.YValue_pt[i] < 0.0) or (Result.YValue_pt[i] > 1.0) then
- begin
- DoSimpleMsg('XY Curve object: "' + CurveName + '" has active power value(s) greater than 1.0 per-unit or less than 0.0 per-unit. Not allowed for VOLTWATT control mode for PVSystems', 381);
- Result := NIL;
- Break;
- end;
- end;
- end;
-
-End;
-
-{ -------------------------------------------------------------------------- }
-
-
-{ -------------------------------------------------------------------------- }
-
-FUNCTION TInvControlObj.InterpretAvgVWindowLen(const s:AnsiString):Integer;
-
-Var
- Code :Integer;
- ch :char;
- s2 :AnsiString;
-
-Begin
- {Try to convert and see if we get an error}
- val(s,Result, Code);
- If Code = 0 then
- begin
- FRollAvgWindowLengthIntervalUnit := 's'; // Only a number was specified, so must be seconds
- FVAvgWindowLengthSec := Result*1.0;
- Exit;
- end;
-
- {Error occurred so must have a units specifier}
- ch := s[Length(s)]; // get last character
- s2 := copy(s, 1, Length(s)-1);
- Val(S2, Result, Code);
- If Code>0 then
- Begin {check for error}
- FRollAvgWindowLengthIntervalUnit := 's'; // Only a number was specified, so must be seconds
- FVAvgWindowLengthSec := 1.0;
- Result := 1;
- DosimpleMsg('Error in specification of Voltage Averaging Window Length: ' + s, 1134);
- Exit;
- End;
-
- case ch of
- 'h':
- begin
- FRollAvgWindowLengthIntervalUnit := 'h';
- FVAvgWindowLengthSec := Result*3600.0;
- end;
- 'm':
- begin
- FRollAvgWindowLengthIntervalUnit := 'm';
- FVAvgWindowLengthSec := Result*60.0;
- end;
- 's':
- begin
- FRollAvgWindowLengthIntervalUnit := 's';
- FVAvgWindowLengthSec := Result*1.0;
- end;
- Else
- FRollAvgWindowLengthIntervalUnit := 's';
- FVAvgWindowLengthSec := Result*1.0;
- Result := 0; // Don't change it
- DosimpleMsg('Error in specification of voltage sample interval size: "' + s +'" Units can only be h, m, or s (single char only) ', 99934);
- end;
-End;
-
-FUNCTION TInvControlObj.InterpretDRCAvgVWindowLen(const s:AnsiString):Integer;
-
-Var
- Code :Integer;
- ch :char;
- s2 :AnsiString;
-
-Begin
- {Try to convert and see if we get an error}
- val(s,Result, Code);
- If Code = 0 then
- begin
- FDRCRollAvgWindowLengthIntervalUnit := 's'; // Only a number was specified, so must be seconds
- FDRCVAvgWindowLengthSec := Result*1.0;
- Exit;
- end;
-
- {Error occurred so must have a units specifier}
- ch := s[Length(s)]; // get last character
- s2 := copy(s, 1, Length(s)-1);
- Val(S2, Result, Code);
- If Code>0 then
- Begin {check for error}
- FDRCRollAvgWindowLengthIntervalUnit := 's'; // Only a number was specified, so must be seconds
- FDRCVAvgWindowLengthSec := 1.0;
- Result := 1;
- DosimpleMsg('Error in specification of Voltage Averaging Window Length: ' + s, 1134);
- Exit;
- End;
-
- case ch of
- 'h':
- begin
- FDRCRollAvgWindowLengthIntervalUnit := 'h';
- FDRCVAvgWindowLengthSec := Result*3600.0;
- end;
- 'm':
- begin
- FDRCRollAvgWindowLengthIntervalUnit := 'm';
- FDRCVAvgWindowLengthSec := Result*60.0;
- end;
- 's':
- begin
- FDRCRollAvgWindowLengthIntervalUnit := 's';
- FDRCVAvgWindowLengthSec := Result*1.0;
- end;
- Else
- FDRCRollAvgWindowLengthIntervalUnit := 's';
- FDRCVAvgWindowLengthSec := Result*1.0;
- Result := 0; // Don't change it
- DosimpleMsg('Error in specification of voltage sample interval size: "' + s +'" Units can only be h, m, or s (single char only) ', 99934);
- end;
-End;
-
-{--------------------------------------------------------------------------}
-FUNCTION TInvControlObj.GetPropertyValue(Index: Integer): AnsiString;
-
-
-
-Begin
-
- Result := '';
- CASE Index of
- 1 : Result := ReturnElementsList;
- 2 :
- Begin
- if ControlMode = 'VOLTVAR' then Result := 'VOLTVAR';
- if ControlMode = 'VOLTWATT' then Result := 'VOLTWATT';
- if ControlMode = 'DYNAMICREACCURR' then Result := 'DYNAMICREACCURR';
- End;
-
- 4 : Result := Format ('%s',[Fvvc_curvename]);
- 5 : Result := Format('%-.6g', [Fvvc_curveOffset]);
- 6 :
- begin
- if(FVoltage_CurveX_ref = 0) then Result := 'rated'
- else if (FVoltage_CurveX_ref = 1) then Result := 'avg'
- else if (FVoltage_CurveX_ref = 2) then Result := 'avgrated'
-
- end;
- 7 : Result := Format('%d', [FRollAvgWindowLength,FRollAvgWindowLengthIntervalUnit]);
- 8 : Result := Format ('%s',[Fvoltwatt_curvename]);
- 9 : Result := Format('%.6g', [FDbVMin]);
- 10 : Result := Format('%.6g', [FDbVMax]);
- 11 : Result := Format('%.6g', [FArGraLowV]);
- 12 : Result := Format('%.6g', [FArGraHiV]);
- 13 : Result := Format('%d', [FDRCRollAvgWindowLength,FDRCRollAvgWindowLengthIntervalUnit]);
- 14 : Result := Format('%.6g', [FdeltaQ_factor]);
- 15 : Result := Format('%.6g', [FVoltageChangeTolerance]);
- 16 : Result := Format('%.6g', [FVarChangeTolerance]);
-
- 17 :
- begin
- if(FVoltwattYAxis = 1) then Result := 'PMPPPU'
- else Result := 'PAVAILABLEPU';
- end;
- 18 :
- begin
- If RateofChangeMode = INACTIVE then Result := 'INACTIVE'
- Else If RateofChangeMode = LPF then Result := 'LPF'
- Else If RateofChangeMode = RISEFALL then Result := 'RISEFALL';
-
- end;
- 21 : Result := Format('%.6g', [FdeltaP_factor]);
- // 21 skipped, EventLog always went to the default handler
- 23 : Result := FVV_ReacPower_ref;
- 24 : Result := Format('%.6g', [FActivePChangeTolerance]);
-
- ELSE // take the generic handler
- Result := Inherited GetPropertyValue(index);
- END;
-End;
-{--------------------------------------------------------------------------}
-
-//----------------------------------------------------------------------------
-FUNCTION TInvControlObj.ReturnElementsList: AnsiString;
-VAR
- i :Integer;
-Begin
- If FListSize=0 Then
- Begin
- Result := '';
- Exit;
- End;
-
- Result := '['+ FPVSystemNameList.Strings[0];
- For i := 1 to FListSize-1 Do
- Begin
- Result := Result + ', ' + FPVSystemNameList.Strings[i];
- End;
- Result := Result + ']'; // terminate the array
-
-End;
-
-//----------------------------------------------------------------------------
-
-
-
-
-procedure TInvControlObj.Set_Enabled(Value: Boolean);
+function TInvControlObj.ReturnElementsList: Ansistring;
+var
+ i: Integer;
begin
- inherited;
-
- {Reset controlled PVSystems to original PF}
+ if FListSize = 0 then
+ begin
+ Result := '';
+ Exit;
+ end;
+ Result := '[' + FPVSystemNameList.Strings[0];
+ for i := 1 to FListSize - 1 do
+ begin
+ Result := Result + ', ' + FPVSystemNameList.Strings[i];
+ end;
+ Result := Result + ']'; // terminate the array
end;
-procedure TInvControlObj.Set_PendingChange(Value: Integer;DevIndex: Integer);
+//procedure TInvControlObj.Set_Enabled(Value: Boolean);
+//begin
+// inherited;
+// // Reset controlled PVSystems to original PF
+//end;
+
+procedure TInvControlObj.Set_PendingChange(Value: Integer; DevIndex: Integer);
begin
- FPendingChange[DevIndex] := Value;
- DblTraceParameter := Value;
+ FPendingChange[DevIndex] := Value;
+ DblTraceParameter := Value;
end;
-procedure TInvControlObj.UpdateInvControl(i:integer);
-Var
- j,k : Integer;
- solnvoltage : Double;
- localControlledElement : TDSSCktElement;
- tempVbuffer : pComplexArray;
- PVSys : TPVSystemObj;
+procedure TInvControlObj.UpdateInvControl(i: Integer);
+var
+ j, k: Integer;
+ solnvoltage: Double;
+ localControlledElement: TDSSCktElement;
+ tempVbuffer: pComplexArray;
+ PVSys: TPVSystemObj;
begin
- tempVbuffer := Nil; // Initialize for Reallocmem
+ tempVbuffer := NIL; // Initialize for Reallocmem
- for j := 1 to FPVSystemPointerList.Count do
- begin
+ for j := 1 to FPVSystemPointerList.Count do
+ begin
// only update solution idx one time through this routine
- if (j = 1) and (i = 1) then
- begin
+ if (j = 1) and (i = 1) then
+ begin
//update solution voltage in per-unit for hysteresis
- if FVpuSolutionIdx = 2 then FVpuSolutionIdx := 1
- else FVpuSolutionIdx := FVpuSolutionIdx+1;
+ if FVpuSolutionIdx = 2 then
+ FVpuSolutionIdx := 1
+ else
+ FVpuSolutionIdx := FVpuSolutionIdx + 1;
- end;
+ end;
- localControlledElement := ControlledElement[j];
- PVSys := localControlledElement as TPVSystemObj;
- FPriorWattspu[j] := PVSys.PresentkW/PVSys.PVSystemVars.FPmpp;
- FPriorvarspu[j] := PVSys.Presentkvar/SQRT(Sqr(PVSys.kVARating)-Sqr(PVSys.PresentkW));
- PVSys.PVSystemVars.FpuPmpp := 1.0;
- FWithinTol[j] := False;
- FWithinTolVV[j] := False;
- FWithinTolVW[j] := False;
- FROCEvaluated[j] := False;
+ localControlledElement := ControlledElement[j];
+ PVSys := localControlledElement as TPVSystemObj;
+ FPriorWattspu[j] := PVSys.PresentkW / PVSys.PVSystemVars.FPmpp;
+ FPriorvarspu[j] := PVSys.Presentkvar / SQRT(Sqr(PVSys.kVARating) - Sqr(PVSys.PresentkW));
+ PVSys.PVSystemVars.FpuPmpp := 1.0;
+ FWithinTol[j] := FALSE;
+ FWithinTolVV[j] := FALSE;
+ FWithinTolVW[j] := FALSE;
+ FROCEvaluated[j] := FALSE;
- FHitkVALimit[j] := False;
- FHitkvarLimit[j] := False;
+ FHitkVALimit[j] := FALSE;
+ FHitkvarLimit[j] := FALSE;
- FFlagROCOnly[j] := False;
+ FFlagROCOnly[j] := FALSE;
// allocated enough memory to buffer to hold voltages and initialize to cZERO
- Reallocmem(tempVbuffer, Sizeof(tempVbuffer^[1]) * localControlledElement.NConds);
- for k := 1 to localControlledElement.NConds do tempVbuffer[k] := cZERO;
+ Reallocmem(tempVbuffer, Sizeof(tempVbuffer^[1]) * localControlledElement.NConds);
+ for k := 1 to localControlledElement.NConds do
+ tempVbuffer[k] := cZERO;
- priorRollAvgWindow[j] := FRollAvgWindow[j].Get_AvgVal;
- priorDRCRollAvgWindow[j] := FDRCRollAvgWindow[j].Get_AvgVal;
+ priorRollAvgWindow[j] := FRollAvgWindow[j].AvgVal;
+ priorDRCRollAvgWindow[j] := FDRCRollAvgWindow[j].AvgVal;
// compute the present terminal voltage
- localControlledElement.ComputeVterminal;
+ localControlledElement.ComputeVterminal;
// save the applicable rolling average voltage in monitor
- if (ControlMode = 'VOLTVAR') and (FVAvgWindowLengthSec > 0.0) then
- PVSys.Set_Variable(5,FRollAvgWindow[j].Get_AvgVal)
- else
- PVSys.Set_Variable(5,FDRCRollAvgWindow[j].Get_AvgVal);
+ if (ControlMode = VOLTVAR) and (FRollAvgWindowLength > 0.0) then
+ PVSys.Set_Variable(5, FRollAvgWindow[j].AvgVal)
+ else
+ PVSys.Set_Variable(5, FDRCRollAvgWindow[j].AvgVal);
- for k := 1 to localControlledElement.Yorder do tempVbuffer[k] := localControlledElement.Vterminal^[k];
+ for k := 1 to localControlledElement.Yorder do
+ tempVbuffer[k] := localControlledElement.Vterminal^[k];
- solnvoltage := 0.0;
- for k := 1 to localControlledElement.Nphases do solnvoltage := solnvoltage + Cabs(tempVbuffer[k]);
- solnvoltage := solnvoltage / (localControlledElement.Nphases*1.0); // average of voltages if more than one phase
+ solnvoltage := 0.0;
+ for k := 1 to localControlledElement.Nphases do
+ solnvoltage := solnvoltage + Cabs(tempVbuffer[k]);
+ solnvoltage := solnvoltage / (localControlledElement.Nphases * 1.0); // average of voltages if more than one phase
// add present power flow solution voltage to the rolling average window
- FRollAvgWindow[j].Add(solnvoltage,ActiveCircuit.Solution.DynaVars.h,FVAvgWindowLengthSec);
- FDRCRollAvgWindow[j].Add(solnvoltage,ActiveCircuit.Solution.DynaVars.h,FDRCVAvgWindowLengthSec);
+ FRollAvgWindow[j].Add(solnvoltage, ActiveCircuit.Solution.DynaVars.h, FRollAvgWindowLength);
+ FDRCRollAvgWindow[j].Add(solnvoltage, ActiveCircuit.Solution.DynaVars.h, FDRCRollAvgWindowLength);
- FVpuSolution[j,FVpuSolutionIdx] := solnvoltage/((ActiveCircuit.Buses^[ localcontrolledelement.terminals[0].busRef].kVBase)*1000.0);
+ FVpuSolution[j, FVpuSolutionIdx] := solnvoltage / ((ActiveCircuit.Buses^[localcontrolledelement.terminals[0].busRef].kVBase) * 1000.0);
- Reallocmem(tempVbuffer, 0); // Clean up memory
-
- end;
+ Reallocmem(tempVbuffer, 0); // Clean up memory
+ end;
end;
-FUNCTION TInvControlObj.Get_PendingChange(DevIndex: Integer):Integer;
+function TInvControlObj.Get_PendingChange(DevIndex: Integer): Integer;
begin
- Result := FPendingChange[DevIndex];
+ Result := FPendingChange[DevIndex];
end;
-Procedure TInvControlObj.CalcVoltWatt_pu(j: Integer);
-VAR
- Pdesiredpu :Double;
- DeltaP :Double;
+procedure TInvControlObj.CalcVoltWatt_pu(j: Integer);
+var
+ Pdesiredpu: Double;
+ DeltaP: Double;
// local pointer to current PVSystem element
- PVSys :TPVSystemObj;
-
-
-BEGIN
-
- PVSys := ControlledElement[j]; // Use local variable in loop
-
+ PVSys: TPVSystemObj;
+begin
+ PVSys := ControlledElement[j]; // Use local variable in loop
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- PVSys.VWmode := TRUE;
- PVSys.VWYAxis := FVoltwattYAxis;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ PVSys.VWmode := TRUE;
+ PVSys.VWYAxis := FVoltwattYAxis;
// P desired pu is the desired output based on the avg pu voltage on the
// monitored element
- Pdesiredpu := Fvoltwatt_curve.GetYValue(FPresentVpu[j]); //Y value = watts in per-unit of Pmpp
+ Pdesiredpu := Fvoltwatt_curve.GetYValue(FPresentVpu[j]); //Y value = watts in per-unit of Pmpp
- if (FROCEvaluated[j] = False) then
- begin
- DeltaP := Pdesiredpu - POld[j];
- PNew[j] := POld[j] + DeltaP * FdeltaP_factor;
- FFinalpuPmpp[j] := PNew[j];
- end
- else
- FFinalpuPmpp[j] := PVSys.puPmpp;
- PVSys := Nil;
-End;
+ if (FROCEvaluated[j] = FALSE) then
+ begin
+ DeltaP := Pdesiredpu - POld[j];
+ PNew[j] := POld[j] + DeltaP * FdeltaP_factor;
+ FFinalpuPmpp[j] := PNew[j];
+ end
+ else
+ FFinalpuPmpp[j] := PVSys.puPmpp;
+ PVSys := NIL;
+end;
-Procedure TInvControlObj.CalcDRC_vars(j: Integer);
-VAR
+procedure TInvControlObj.CalcDRC_vars(j: Integer);
+var
- DeltaQ,basekV,
- QTemp{,TempQ} :Double;
+ DeltaQ, basekV,
+ QTemp{,TempQ}: Double;
// SMonitoredElement :Complex;
// local pointer to current PVSystem element
- PVSys :TPVSystemObj;
+ PVSys: TPVSystemObj;
-BEGIN
-
- PVSys := ControlledElement[j]; // Use local variable in loop
+begin
+ PVSys := ControlledElement[j]; // Use local variable in loop
// SMonitoredElement := PVSys.Power[1]; // s is in va
- PVSys.VWmode := FALSE;
- PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
- PVSys.Varmode := VARMODEKVAR; // Set var mode to VARMODEKVAR to indicate we might change kvar
+ PVSys.VWmode := FALSE;
+ PVSys.ActiveTerminalIdx := 1; // Set active terminal of PVSystem to terminal 1
+ PVSys.Varmode := VARMODEKVAR; // Set var mode to VARMODEKVAR to indicate we might change kvar
- QDRCDesiredpu[j] := 0.0;
+ QDRCDesiredpu[j] := 0.0;
// calculate headroom from kva rating of PVSystem and presentkW output level
- if FVV_ReacPower_ref = 'VARAVAL_WATTS' then QHeadRoom[j] := SQRT(Sqr(PVSys.kVARating)-Sqr(PVSys.PresentkW));
- if (FVV_ReacPower_ref = 'VARMAX_VARS') or (FVV_ReacPower_ref = 'VARMAX_WATTS') then QHeadRoom[j] := PVSys.kvarLimit;
+ if FVV_ReacPower_ref = VARAVAL_WATTS then
+ QHeadRoom[j] := SQRT(Sqr(PVSys.kVARating) - Sqr(PVSys.PresentkW));
+ if (FVV_ReacPower_ref = VARMAX_VARS) or (FVV_ReacPower_ref = VARMAX_WATTS) then
+ QHeadRoom[j] := PVSys.kvarLimit;
-
-
-
-
-
- basekV := ActiveCircuit.Buses^[ PVSys.terminals[0].busRef].kVBase;
+ basekV := ActiveCircuit.Buses^[PVSys.terminals[0].busRef].kVBase;
// calculate deltaV quantity in per-unit from subtracting the rolling average
// value (in p.u.) from the present p.u. terminal voltage (average of line-ground)
// if more than one phase
- if(FDRCRollAvgWindow[j].Get_AvgVal/(basekV*1000.0)) = 0.0 then deltaVDynReac[j]:=0
- else deltaVDynReac[j] := FPresentVpu[j] - FDRCRollAvgWindow[j].Get_AvgVal/(basekV*1000.0);
+ if (FDRCRollAvgWindow[j].AvgVal / (basekV * 1000.0)) = 0.0 then
+ deltaVDynReac[j] := 0
+ else
+ deltaVDynReac[j] := FPresentVpu[j] - FDRCRollAvgWindow[j].AvgVal / (basekV * 1000.0);
// if below the lower deadband and deltaV quantity is non-zero then
// calculate desired pu var output. In per-unit of kva rating (also
// ampere rating), per report specifications.
- if (deltaVDynReac[j] <>0) and (FPresentVpu[j] < FDbVMin) then
- QDRCDesiredpu[j] := -deltaVDynReac[j]*FArGraLowV
+ if (deltaVDynReac[j] <> 0) and (FPresentVpu[j] < FDbVMin) then
+ QDRCDesiredpu[j] := -deltaVDynReac[j] * FArGraLowV
// if above the upper deadband and deltaV quantity is non-zero then
// calculate desired pu var output. In per-unit of kva rating (also
// ampere rating), per report specifications.
- else if (deltaVDynReac[j] <>0) and (FPresentVpu[j] > FDbVMax) then
- QDRCDesiredpu[j] := -deltaVDynReac[j]*FArGraHiV
+ else
+ if (deltaVDynReac[j] <> 0) and (FPresentVpu[j] > FDbVMax) then
+ QDRCDesiredpu[j] := -deltaVDynReac[j] * FArGraHiV
- else if deltaVDynReac[j] = 0.0 then
- QDRCDesiredpu[j] := 0.0;
+ else
+ if deltaVDynReac[j] = 0.0 then
+ QDRCDesiredpu[j] := 0.0;
- if (ActiveCircuit.Solution.Dynavars.t=1) then
- QDRCDesiredpu[j] := 0.0;
+ if (ActiveCircuit.Solution.Dynavars.t = 1) then
+ QDRCDesiredpu[j] := 0.0;
// as with volt-var mode, we don't want to jump directly to solution
// or we'll have oscillatory behavior
- QTemp := 0;
- if FVV_ReacPower_ref = 'VARAVAL_WATTS' then QTemp := QDRCDesiredpu[j]*PVSys.kVARating;
- if (FVV_ReacPower_ref = 'VARMAX_VARS') or (FVV_ReacPower_ref = 'VARMAX_WATTS') then QTemp := QDRCDesiredpu[j]*PVSys.kvarLimit;
+ QTemp := 0;
+ if FVV_ReacPower_ref = VARAVAL_WATTS then
+ QTemp := QDRCDesiredpu[j] * PVSys.kVARating;
+ if (FVV_ReacPower_ref = VARMAX_VARS) or (FVV_ReacPower_ref = VARMAX_WATTS) then
+ QTemp := QDRCDesiredpu[j] * PVSys.kvarLimit;
- if(Abs(QTemp) > QHeadroom[j]) then
- begin
- if FVV_ReacPower_ref = 'VARAVAL_WATTS' then QDRCDesiredpu[j] := sign(QDRCDesiredpu[j])*1.0
- else QDRCDesiredpu[j] := sign(QDRCDesiredpu[j])*1.0;
- end;
- if FVV_ReacPower_ref = 'VARAVAL_WATTS' then DeltaQ := QDRCDesiredpu[j]*PVSys.kVARating - QoldDRC[j]
- else DeltaQ := QDRCDesiredpu[j]*PVSys.kvarLimit - QoldDRC[j];
- // if FVV_ReacPower_ref = 'VARAVAL_WATTS' then TempQ := QDRCDesiredpu[j]*PVSys.kVARating
+ if (Abs(QTemp) > QHeadroom[j]) then
+ begin
+ if FVV_ReacPower_ref = VARAVAL_WATTS then
+ QDRCDesiredpu[j] := sign(QDRCDesiredpu[j]) * 1.0
+ else
+ QDRCDesiredpu[j] := sign(QDRCDesiredpu[j]) * 1.0;
+ end;
+ if FVV_ReacPower_ref = VARAVAL_WATTS then
+ DeltaQ := QDRCDesiredpu[j] * PVSys.kVARating - QoldDRC[j]
+ else
+ DeltaQ := QDRCDesiredpu[j] * PVSys.kvarLimit - QoldDRC[j];
+ // if FVV_ReacPower_ref = VARAVAL_WATTS then TempQ := QDRCDesiredpu[j]*PVSys.kVARating
// else TempQ := QDRCDesiredpu[j]*PVSys.kvarLimit;
- if abs(DeltaQ) > PVSys.kvarLimit then DeltaQ := 1.0*sign(DeltaQ)*PVSys.kvarLimit;
-
- QDRCNew[j] := QoldDRC[j]+(DeltaQ*FDeltaQ_factor);
-
+ if abs(DeltaQ) > PVSys.kvarLimit then
+ DeltaQ := 1.0 * sign(DeltaQ) * PVSys.kvarLimit;
-END;
+ QDRCNew[j] := QoldDRC[j] + (DeltaQ * FDeltaQ_factor);
+end;
-FUNCTION TInvControlObj.CalcLPF(m: Integer; powertype: AnsiString;PVSys:TPVSystemObj):Double;
-VAR
- Pdesiredpu :Double;
- DeltaQ,alpha,DeltaP,
- LPFvarspu,LPFwattspu :Double;
+function TInvControlObj.CalcLPF(m: Integer; powertype: Ansistring; PVSys: TPVSystemObj): Double;
+var
+ Pdesiredpu: Double;
+ DeltaQ, alpha, DeltaP,
+ LPFvarspu, LPFwattspu: Double;
// Applies the LPF:
// Return value is in kvar for VARS
// Return value is in puPmpp for WATTS
-BEGIN
-
- Result := -999.999;
+begin
+ Result := -999.999;
// calculate the alpha constant
- alpha := 1.0/(ActiveCircuit.Solution.DynaVars.h)/(FLPFTau+1.0/ActiveCircuit.Solution.DynaVars.h);
- if powertype = 'VARS' then begin
- LPFvarspu :=alpha*(Qoutputpu[m])+(1-alpha)*(FPriorvarspu[m]);
- if (LPFvarspu <> 0.0) then begin
- QDeliver[m] := LPFvarspu*QHeadRoom[m];
- DeltaQ := QDeliver[m] - Qold[m];
- Result := QOld[m] + DeltaQ * FdeltaQ_factor;
- end;
+ alpha := 1.0 / (ActiveCircuit.Solution.DynaVars.h) / (FLPFTau + 1.0 / ActiveCircuit.Solution.DynaVars.h);
+ if powertype = 'VARS' then
+ begin
+ LPFvarspu := alpha * (Qoutputpu[m]) + (1 - alpha) * (FPriorvarspu[m]);
+ if (LPFvarspu <> 0.0) then
+ begin
+ QDeliver[m] := LPFvarspu * QHeadRoom[m];
+ DeltaQ := QDeliver[m] - Qold[m];
+ Result := QOld[m] + DeltaQ * FdeltaQ_factor;
+ end;
- end;
- if powertype = 'WATTS' then begin
- LPFWattspu :=alpha*(FFinalpuPmpp[m])+(1-alpha)*(FPriorWattspu[m]);
- if (LPFWattspu <> 0.0) then begin
- Pdesiredpu := LPFWattspu;
- DeltaP := Pdesiredpu - POld[m];
- Result := POld[m] + DeltaP * FdeltaP_factor;
end;
- end;
-END;
-
-FUNCTION TInvControlObj.CalcRF(m: Integer;powertype: AnsiString;PVSys:TPVSystemObj):Double;
-VAR
- Pdesiredpu :Double;
- DeltaP,
- Pdesiredpu_temp,
- Qdesiredpu_temp :Double;
-
-BEGIN
- Result := 0.0;
+ if powertype = 'WATTS' then
+ begin
+ LPFWattspu := alpha * (FFinalpuPmpp[m]) + (1 - alpha) * (FPriorWattspu[m]);
+ if (LPFWattspu <> 0.0) then
+ begin
+ Pdesiredpu := LPFWattspu;
+ DeltaP := Pdesiredpu - POld[m];
+ Result := POld[m] + DeltaP * FdeltaP_factor;
+ end;
+ end;
+end;
+
+function TInvControlObj.CalcRF(m: Integer; powertype: Ansistring; PVSys: TPVSystemObj): Double;
+var
+ Pdesiredpu: Double;
+ DeltaP,
+ Pdesiredpu_temp,
+ Qdesiredpu_temp: Double;
+
+begin
+ Result := 0.0;
// Applies the Rise/Fall limiting function:
// Return value is in kvar for VARS
// Return value is in puPmpp for WATTS
- if FVV_ReacPower_ref = 'VARAVAL_WATTS' then
- QHeadRoom[m] := SQRT(Sqr(PVSys.kVARating)-Sqr(PVSys.PresentkW));
- if (FVV_ReacPower_ref = 'VARMAX_VARS') or (FVV_ReacPower_ref = 'VARMAX_WATTS') then
- QHeadRoom[m] := PVSys.kvarLimit;
-
- if powertype='VARS' then begin
+ if FVV_ReacPower_ref = VARAVAL_WATTS then
+ QHeadRoom[m] := SQRT(Sqr(PVSys.kVARating) - Sqr(PVSys.PresentkW));
+ if (FVV_ReacPower_ref = VARMAX_VARS) or (FVV_ReacPower_ref = VARMAX_WATTS) then
+ QHeadRoom[m] := PVSys.kvarLimit;
- if(abs(PVSys.Presentkvar) < 0.00001) then begin
-
- exit;
- end;
+ if powertype = 'VARS' then
+ begin
+ if (abs(PVSys.Presentkvar) < 0.00001) then
+ begin
+ exit;
+ end;
// rate of change rise/fall limit
- if(PVSys.Presentkvar/QHeadroom[m] - FPriorvarspu[m])<=0 then begin
- if(PVSys.Presentkvar<=0) then
- Qdesiredpu_temp := Max((FPriorvarspu[m]-(FRiseFallLimit*(1.0/ActiveCircuit.Solution.DynaVars.h))),PVSys.Presentkvar/QHeadroom[m])
- else
- Qdesiredpu_temp := Min((FPriorvarspu[m]-(FRiseFallLimit*(1.0/ActiveCircuit.Solution.DynaVars.h))),PVSys.Presentkvar/QHeadroom[m])
- end else begin
- if(PVSys.Presentkvar<=0) then
- Qdesiredpu_temp := Max((FPriorvarspu[m]+(-1.0*FRiseFallLimit*(1.0/ActiveCircuit.Solution.DynaVars.h))),PVSys.Presentkvar/QHeadroom[m])
- else // TODO - Wes check the following, Tom prepended Qdesiredpu_temp :=
- Qdesiredpu_temp := Min((FPriorvarspu[m]+(-1.0*FRiseFallLimit*(1.0/ActiveCircuit.Solution.DynaVars.h))),PVSys.Presentkvar/QHeadroom[m]);
+ if (PVSys.Presentkvar / QHeadroom[m] - FPriorvarspu[m]) <= 0 then
+ begin
+ if (PVSys.Presentkvar <= 0) then
+ Qdesiredpu_temp := Max((FPriorvarspu[m] - (FRiseFallLimit * (1.0 / ActiveCircuit.Solution.DynaVars.h))), PVSys.Presentkvar / QHeadroom[m])
+ else
+ Qdesiredpu_temp := Min((FPriorvarspu[m] - (FRiseFallLimit * (1.0 / ActiveCircuit.Solution.DynaVars.h))), PVSys.Presentkvar / QHeadroom[m])
+ end
+ else
+ begin
+ if (PVSys.Presentkvar <= 0) then
+ Qdesiredpu_temp := Max((FPriorvarspu[m] + (-1.0 * FRiseFallLimit * (1.0 / ActiveCircuit.Solution.DynaVars.h))), PVSys.Presentkvar / QHeadroom[m])
+ else // TODO - Wes check the following, Tom prepended Qdesiredpu_temp :=
+ Qdesiredpu_temp := Min((FPriorvarspu[m] + (-1.0 * FRiseFallLimit * (1.0 / ActiveCircuit.Solution.DynaVars.h))), PVSys.Presentkvar / QHeadroom[m]);
+ end;
+ FROCEvaluated[m] := TRUE;
+ Result := Qdesiredpu_temp * QHeadRoom[m];
end;
- FROCEvaluated[m] := True;
- Result := Qdesiredpu_temp*QHeadRoom[m];
- end;
-
- if powertype = 'WATTS' then begin
- // rate of change rise/fall limit
- if (abs(FFinalpuPmpp[m] - FPriorWattspu[m])/(1.0/ActiveCircuit.Solution.DynaVars.h*1.0)) > FRiseFallLimit then begin
- if(FFinalpuPmpp[m] - FPriorWattspu[m])<=0 then
- Pdesiredpu_temp := (FPriorWattspu[m]-(FRiseFallLimit*(1.0/ActiveCircuit.Solution.DynaVars.h)))
- else
- Pdesiredpu_temp := (FPriorWattspu[m]+(FRiseFallLimit*(1.0/ActiveCircuit.Solution.DynaVars.h)));
- if(Pdesiredpu_temp > PVSys.PresentkW/PVSys.PVSystemVars.FPmpp) then
- Pdesiredpu_temp := PVSys.PresentkW/PVSys.PVSystemVars.FPmpp;
- if (Pdesiredpu_temp <> 0.0) then begin
- Pdesiredpu :=Pdesiredpu_temp;
- DeltaP := Pdesiredpu - POld[m];
- Result := POld[m] + DeltaP * FdeltaP_factor;
- end;
- end else
- Result := PVSys.PresentkW/PVSys.PVSystemVars.FPmpp;
- end;
-END;
-
-Procedure TInvControlObj.CalcVoltVar_vars(j: Integer);
-VAR
-
- voltagechangesolution,QPresentpu,VpuFromCurve,
- DeltaQ :Double;
- // SMonitoredElement :Complex;
-
-
- // local pointer to current PVSystem element
- PVSys :TPVSystemObj;
- FDiffvar :Array of Double;
- FDesiredpu_temp :Array of Double;
-BEGIN
-
- SetLength(FDiffvar,4+1);
- SetLength(FDesiredpu_temp, 4+1);
+ if powertype = 'WATTS' then
+ begin
+ // rate of change rise/fall limit
+ if (abs(FFinalpuPmpp[m] - FPriorWattspu[m]) / (1.0 / ActiveCircuit.Solution.DynaVars.h * 1.0)) > FRiseFallLimit then
+ begin
+ if (FFinalpuPmpp[m] - FPriorWattspu[m]) <= 0 then
+ Pdesiredpu_temp := (FPriorWattspu[m] - (FRiseFallLimit * (1.0 / ActiveCircuit.Solution.DynaVars.h)))
+ else
+ Pdesiredpu_temp := (FPriorWattspu[m] + (FRiseFallLimit * (1.0 / ActiveCircuit.Solution.DynaVars.h)));
+ if (Pdesiredpu_temp > PVSys.PresentkW / PVSys.PVSystemVars.FPmpp) then
+ Pdesiredpu_temp := PVSys.PresentkW / PVSys.PVSystemVars.FPmpp;
+ if (Pdesiredpu_temp <> 0.0) then
+ begin
+ Pdesiredpu := Pdesiredpu_temp;
+ DeltaP := Pdesiredpu - POld[m];
+ Result := POld[m] + DeltaP * FdeltaP_factor;
+ end;
+ end
+ else
+ Result := PVSys.PresentkW / PVSys.PVSystemVars.FPmpp;
+ end;
+end;
+procedure TInvControlObj.CalcVoltVar_vars(j: Integer);
+var
+ voltagechangesolution, QPresentpu, VpuFromCurve,
+ DeltaQ: Double;
+ // SMonitoredElement :Complex;
- PVSys := ControlledElement[j];
+ // local pointer to current PVSystem element
+ PVSys: TPVSystemObj;
+ FDiffvar: array of Double;
+ FDesiredpu_temp: array of Double;
+begin
+ SetLength(FDiffvar, 4 + 1);
+ SetLength(FDesiredpu_temp, 4 + 1);
+ PVSys := ControlledElement[j];
- // SMonitoredElement := PVSys.Power[1]; // s is in va
+ // SMonitoredElement := PVSys.Power[1]; // s is in va
- QDesiredpu[j] := 0.0;
+ QDesiredpu[j] := 0.0;
+ if FVV_ReacPower_ref = VARAVAL_WATTS then
+ begin
+ if (PVSys.PresentkW < PVSys.kVARating) then
+ QHeadRoom[j] := SQRT(Sqr(PVSys.kVARating) - Sqr(PVSys.PresentkW))
+ else
+ QHeadRoom[j] := 0.0
+ end;
- if FVV_ReacPower_ref = 'VARAVAL_WATTS' then
- begin
- if(PVSys.PresentkW < PVSys.kVARating) then
- QHeadRoom[j] := SQRT(Sqr(PVSys.kVARating)-Sqr(PVSys.PresentkW))
- else
- QHeadRoom[j] := 0.0
- end;
-
- if (FVV_ReacPower_ref = 'VARMAX_VARS') or (FVV_ReacPower_ref = 'VARMAX_WATTS') then QHeadRoom[j] := PVSys.kvarLimit;
+ if (FVV_ReacPower_ref = VARMAX_VARS) or (FVV_ReacPower_ref = VARMAX_WATTS) then
+ QHeadRoom[j] := PVSys.kvarLimit;
- if(QHeadRoom[j] = 0.0) then QHeadRoom[j] := PVSys.kvarLimit;
- QPresentpu := PVSys.Presentkvar / QHeadRoom[j];
- voltagechangesolution := 0.0;
+ if (QHeadRoom[j] = 0.0) then
+ QHeadRoom[j] := PVSys.kvarLimit;
+ QPresentpu := PVSys.Presentkvar / QHeadRoom[j];
+ voltagechangesolution := 0.0;
// for first two seconds, keep voltagechangesolution equal to zero
// we don't have solutions from the time-series power flow, yet
- if ((ActiveCircuit.Solution.DynaVars.dblHour*3600.0 / ActiveCircuit.Solution.DynaVars.h)<3.0) then voltagechangesolution := 0.0
- else if(FVpuSolutionIdx = 1) then voltagechangesolution := FVpuSolution[j,1] - FVpuSolution[j,2]
- else if(FVpuSolutionIdx = 2) then voltagechangesolution := FVpuSolution[j,2] - FVpuSolution[j,1];
+ if ((ActiveCircuit.Solution.DynaVars.dblHour * 3600.0 / ActiveCircuit.Solution.DynaVars.h) < 3.0) then
+ voltagechangesolution := 0.0
+ else
+ if (FVpuSolutionIdx = 1) then
+ voltagechangesolution := FVpuSolution[j, 1] - FVpuSolution[j, 2]
+ else
+ if (FVpuSolutionIdx = 2) then
+ voltagechangesolution := FVpuSolution[j, 2] - FVpuSolution[j, 1];
// if no hysteresis (Fvvc_curveOffset == 0), then just look up the value
// from the volt-var curve
- if (FWithinTolVV[j]=False) then
- begin
- if Fvvc_curveOffset = 0.0 then begin // no hysteresis
- Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j])
- end // end of logic for the no-hysteresis case
+ if (FWithinTolVV[j] = FALSE) then
+ begin
+ if Fvvc_curveOffset = 0.0 then
+ begin // no hysteresis
+ Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j])
+ end // end of logic for the no-hysteresis case
// else if we're going in the positive direction and on curve 1, stay
// with curve 1
- else if (voltagechangesolution > 0) and (FActiveVVCurve[j] = 1) then
+ else
+ if (voltagechangesolution > 0) and (FActiveVVCurve[j] = 1) then
begin
- if(FlagChangeCurve[j] = True) then
+ if (FlagChangeCurve[j] = TRUE) then
begin
VpuFromCurve := Fvvc_curve.GetXValue(QPresentpu);
- if(Abs(FPresentVpu[j] - VpuFromCurve) < FVoltageChangeTolerance/2.0) then
+ if (Abs(FPresentVpu[j] - VpuFromCurve) < FVoltageChangeTolerance / 2.0) then
begin
- Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]); //Y value = in per-unit of headroom
- FlagChangeCurve[j] := False;
+ Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]); //Y value = in per-unit of headroom
+ FlagChangeCurve[j] := FALSE;
end
else
- begin
+ begin
Qdesiredpu[j] := QPresentpu;
- FlagChangeCurve[j] := False;
- end;
+ FlagChangeCurve[j] := FALSE;
+ end;
end
- else
+ else
begin
- Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]); //Y value = in per-unit of headroom
+ Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]); //Y value = in per-unit of headroom
end
end
// with hysteresis if we're going in the positive direction on voltages
// from last two power flow solutions, and we're using curve 2, keep vars
// the same, and change to curve1 active
- else if (voltagechangesolution > 0) and (FActiveVVCurve[j] = 2) then
+ else
+ if (voltagechangesolution > 0) and (FActiveVVCurve[j] = 2) then
begin
Qdesiredpu[j] := QPresentpu;
FActiveVVCurve[j] := 1;
- FlagChangeCurve[j] := True;
+ FlagChangeCurve[j] := TRUE;
end
// with hysteresis if we're going in the negative direction on voltages
@@ -3204,26 +2650,28 @@ procedure TInvControlObj.UpdateInvControl(i:integer);
// lookup the vars for the voltage we're at (with offset on curve1),
// or if we've not just changed curves, stay at the current p.u.
// var output
- else if (voltagechangesolution < 0) and (FActiveVVCurve[j] = 2) then
+ else
+ if (voltagechangesolution < 0) and (FActiveVVCurve[j] = 2) then
begin
- if(FlagChangeCurve[j] = True) then
+ if (FlagChangeCurve[j] = TRUE) then
begin
VpuFromCurve := Fvvc_curve.GetXValue(QPresentpu);
VpuFromCurve := VpuFromCurve - Fvvc_curveOffset;
- if(Abs(FPresentVpu[j] - VpuFromCurve) < FVoltageChangeTolerance/2.0) then
+ if (Abs(FPresentVpu[j] - VpuFromCurve) < FVoltageChangeTolerance / 2.0) then
begin
- Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]-Fvvc_curveOffset); //Y value = in per-unit of headroom
- FlagChangeCurve[j] := False;
+ Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j] - Fvvc_curveOffset); //Y value = in per-unit of headroom
+ FlagChangeCurve[j] := FALSE;
end
- else begin
- Qdesiredpu[j] := QPresentpu;
- FlagChangeCurve[j] := False;
+ else
+ begin
+ Qdesiredpu[j] := QPresentpu;
+ FlagChangeCurve[j] := FALSE;
end;
end
- else
+ else
begin
- Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]-Fvvc_curveOffset); //Y value = in per-unit of headroom
+ Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j] - Fvvc_curveOffset); //Y value = in per-unit of headroom
end
end
@@ -3232,196 +2680,77 @@ procedure TInvControlObj.UpdateInvControl(i:integer);
// from last two power flow solutions, and we're using curve 1, then
// stay wjth present output vars and make curve2 active, set curve change
// flag
- else if (voltagechangesolution < 0) and (FActiveVVCurve[j] = 1) then
+ else
+ if (voltagechangesolution < 0) and (FActiveVVCurve[j] = 1) then
begin
Qdesiredpu[j] := QPresentpu;
FActiveVVCurve[j] := 2;
- FlagChangeCurve[j] := True;
+ FlagChangeCurve[j] := TRUE;
end
// if no change in voltage from one powerflow to the next, then
// do one of the following
- else if (voltagechangesolution = 0) and (FActiveVVCurve[j] = 1) and (FlagChangeCurve[j] = False) then
+ else
+ if (voltagechangesolution = 0) and (FActiveVVCurve[j] = 1) and (FlagChangeCurve[j] = FALSE) then
begin
- Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]);
+ Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]);
end
- else if (voltagechangesolution = 0) and (FlagChangeCurve[j] = True) then
+ else
+ if (voltagechangesolution = 0) and (FlagChangeCurve[j] = TRUE) then
begin
- Qdesiredpu[j] := QPresentpu;
+ Qdesiredpu[j] := QPresentpu;
end
- else if (voltagechangesolution = 0) and (FActiveVVCurve[j] = 2) and (FlagChangeCurve[j] = False) then
+ else
+ if (voltagechangesolution = 0) and (FActiveVVCurve[j] = 2) and (FlagChangeCurve[j] = FALSE) then
begin
- Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]-Fvvc_curveOffset);
+ Qdesiredpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j] - Fvvc_curveOffset);
end;
- if SQRT(Sqr(PVSys.kVARating)-Sqr(PVSys.PresentkW)) = 0 then QDesiredpu[j] := 0.0;
+ if SQRT(Sqr(PVSys.kVARating) - Sqr(PVSys.PresentkW)) = 0 then
+ QDesiredpu[j] := 0.0;
// only move deltaQ_factor amount to the desired p.u. available var
// output
- if (FROCEvaluated[j] = False) then
- begin
- if(FlagChangeCurve[j] = False) then
+ if (FROCEvaluated[j] = FALSE) then
begin
- QDeliver[j] := QDesiredpu[j]*QHeadRoom[j];
- DeltaQ := QDeliver[j] - QoldVV[j];
+ if (FlagChangeCurve[j] = FALSE) then
+ begin
+ QDeliver[j] := QDesiredpu[j] * QHeadRoom[j];
+ DeltaQ := QDeliver[j] - QoldVV[j];
- QNew[j] := QOldVV[j] + DeltaQ * FdeltaQ_factor;
- end
+ QNew[j] := QOldVV[j] + DeltaQ * FdeltaQ_factor;
+ end
// else, stay at present var output level
- else
- begin
- QNew[j] := PVSys.Presentkvar;
+ else
+ begin
+ QNew[j] := PVSys.Presentkvar;
+ end;
end;
- end;
-
- end;
-Finalize(FDiffvar);
-Finalize(FDesiredpu_temp);
-
-
-End;
-
-
-//Called at end of main power flow solution loop
-PROCEDURE TInvControl.UpdateAll;
-VAR
- i : Integer;
-
-Begin
-
- For i := 1 to ElementList.Count Do
- With TInvControlObj(ElementList.Get(i)) Do
- If Enabled Then UpdateInvControl(i);
-
-End;
-
-
-procedure TRollAvgWindow.Add(IncomingSampleValue: Double;IncomingSampleTime: Double;VAvgWindowLengthSec:Double);
-begin
-{$IFNDEF FPC}
- if(sample.Count > 0) and (bufferfull) then
- begin
- runningsumsample := runningsumsample - sample.Dequeue;
- if(bufferlength = 0) then
- begin
- IncomingSampleValue := 0.0;
- end;
- sample.Enqueue(IncomingSampleValue);
- runningsumsample := runningsumsample + IncomingSampleValue;
- runningsumsampletime := runningsumsampletime - sampletime.Dequeue;
- sampletime.Enqueue(IncomingSampleTime);
- runningsumsampletime := runningsumsampletime +IncomingSampleTime;
- end
- else
- begin
- if(bufferlength = 0) then
- begin
- IncomingSampleValue := 0.0;
- end;
- sample.Enqueue(IncomingSampleValue);
- runningsumsample := runningsumsample + IncomingSampleValue;
- sampletime.Enqueue(IncomingSampleTime);
- runningsumsampletime := runningsumsampletime + IncomingSampleTime;
- if (runningsumsampletime > VAvgWindowLengthSec)
- then bufferfull := True;
- if (sample.Count = bufferlength)
- then bufferfull := True;
end;
-{$ELSE}
-if(sample.size > 0) and (bufferfull) then
- begin
- runningsumsample := runningsumsample - sample.front; sample.pop;
- if(bufferlength = 0) then
- begin
- IncomingSampleValue := 0.0;
- end;
- sample.push(IncomingSampleValue);
- runningsumsample := runningsumsample + IncomingSampleValue;
- runningsumsampletime := runningsumsampletime - sampletime.front; sampletime.pop;
- sampletime.push(IncomingSampleTime);
- runningsumsampletime := runningsumsampletime +IncomingSampleTime;
- end
-else
- begin
- if(bufferlength = 0) then
- begin
- IncomingSampleValue := 0.0;
- end;
- sample.push(IncomingSampleValue);
- runningsumsample := runningsumsample + IncomingSampleValue;
- sampletime.push(IncomingSampleTime);
- runningsumsampletime := runningsumsampletime + IncomingSampleTime;
- if (runningsumsampletime > VAvgWindowLengthSec)
- then bufferfull := True;
- if (sample.size = bufferlength)
- then bufferfull := True;
- end;
-{$ENDIF}
-end;
-
-constructor TRollAvgWindow.Create();
-begin
- sample := TQueue.Create();
- sampletime := TQueue.Create();
-
- runningsumsample := 0.0;
- runningsumsampletime := 0.0;
- bufferlength := 0;
- bufferfull := False;
-end;
-
-destructor TRollAvgWindow.Destroy;
-begin
- sample := nil;
- sampletime := nil;
-
- inherited;
-end;
-
-procedure TRollAvgWindow.Set_BuffLength(const Value: Integer);
-begin
- bufferlength := Value;
+ Finalize(FDiffvar);
+ Finalize(FDesiredpu_temp);
end;
-function TRollAvgWindow.Get_AvgVal: Double;
-begin
-{$IFNDEF FPC}
- if(sample.Count = 0) then
- Result:= 0.0
- else Result:= runningsumsample / sample.Count;
-{$ELSE}
- if(sample.size = 0) then
- Result:= 0.0
- else Result:= runningsumsample / sample.size;
-{$ENDIF}
-end;
-
-function TRollAvgWindow.Get_AccumSec: Double;
+//Called at end of main power flow solution loop
+procedure TInvControl.UpdateAll;
+var
+ i: Integer;
begin
-{$IFNDEF FPC}
- if(sample.Count = 0) then
- Result:= 0.0
- else Result:= runningsumsampletime;
-{$ELSE}
- if(sample.size = 0) then
- Result:= 0.0
- else Result:= runningsumsampletime;
-{$ENDIF}
+ for i := 1 to ElementList.Count do
+ with TInvControlObj(ElementList.Get(i)) do
+ if Enabled then
+ UpdateInvControl(i);
end;
-
-INITIALIZATION
-
-
-
-Finalization
-
-
-
+finalization ModeEnum.Free;
+ CombiModeEnum.Free;
+ RoCEnum.Free;
+ VV_RefQEnum.Free;
+ VWYAxisEnum.Free;
+ VCurveXRefEnum.Free;
end.
-
diff --git a/src/Controls/InvControl2.pas b/src/Controls/InvControl2.pas
index 831a1b57b..5fbe74df0 100644
--- a/src/Controls/InvControl2.pas
+++ b/src/Controls/InvControl2.pas
@@ -1,391 +1,380 @@
unit InvControl2;
+
{
----------------------------------------------------------
Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
All rights reserved.
----------------------------------------------------------
}
-{
- A InvControl is a control element that is connected to a terminal of another
- circuit element and sends kW and/or kvar signals to a set of PVSystem objects it controls
+//
+// A InvControl is a control element that is connected to a terminal of another
+// circuit element and sends kW and/or kvar signals to a set of PVSystem objects it controls
+//
+// A InvControl is defined by a New command:
+//
+// New InvControl.Name=myname PVSystemList = (pvsystem1 pvsystem2 ...)
+//
+//Notes:
+// WGS (11/26/2012): Using dynamic arrays for many private variables in this unit.
+// Although dynamic arrays begin at 0 (by definition in Delphi),
+// this unit is using 1 to numberelements in all for loops - the 0th
+// element is un-used (except for Strings) in this unit.
+// All dynamic arrays are set to length numberelements+1 in the appropriate dimension.
+//
+// Updated 9/24/2015 to allow for simultaneous modes and additional functionality
- A InvControl is defined by a New command:
+interface
- New InvControl.Name=myname PVSystemList = (pvsystem1 pvsystem2 ...)
+uses
+ RollAvgWindow,
+ Command,
+ ControlClass,
+ ControlElem,
+ CktElement,
+ DSSClass,
+ bus,
+ PCElement,
+ PVSystem2,
+ Storage2,
+ Arraydef,
+ UComplex, DSSUcomplex,
+ utilities,
+ XYcurve,
+ Dynamics,
+ DSSPointerList,
+ Classes,
+ StrUtils;
-Notes:
- WGS (11/26/2012): Using dynamic arrays for many private variables in this unit.
- Although dynamic arrays begin at 0 (by definition in Delphi),
- this unit is using 1 to numberelements in all for loops - the 0th
- element is un-used (except for Strings) in this unit.
- All dynamic arrays are set to length numberelements+1 in the appropriate dimension.
- All dynamic arrays are Finalize'd in the destroy procedure.
+type
+{$SCOPEDENUMS ON}
+ TInvControl2Prop = (
+ INVALID = 0,
+
+ DERList = 1,
+ Mode = 2,
+ CombiMode = 3,
+ vvc_curve1 = 4,
+ hysteresis_offset = 5,
+ voltage_curvex_ref = 6,
+ avgwindowlen = 7,
+
+ voltwatt_curve = 8,
+
+ DbVMin = 9,
+ DbVMax = 10,
+ ArGraLowV = 11,
+ ArGraHiV = 12,
+ DynReacavgwindowlen = 13,
+ deltaQ_Factor = 14,
+ VoltageChangeTolerance = 15,
+ VarChangeTolerance = 16,
+ VoltwattYAxis = 17,
+ RateofChangeMode = 18,
+ LPFTau = 19, // weird double: in the original version, value was not set
+ RiseFallLimit = 20, // weird double: in the original version, value was not set
+ deltaP_Factor = 21,
+ EventLog = 22,
+ RefReactivePower = 23,
+ ActivePChangeTolerance = 24,
+ monVoltageCalc = 25,
+ monBus = 26,
+ MonBusesVbase = 27,
+ voltwattCH_curve = 28,
+ wattpf_curve = 29,
+ wattvar_curve = 30,
+ // VV_RefReactivePower = 31, // was deprecated, removed
+ PVSystemList, // was 32
+ Vsetpoint // was 33
+ );
+{$SCOPEDENUMS OFF}
- // Updated 9/24/2015 to allow for simultaneous modes and additional functionality
-}
+ // Modes
+{$PUSH}
+{$Z4} // keep enums as int32 values
+ TInvControl2ControlMode = (
+ NONE_MODE = 0,
+ VOLTVAR = 1,
+ VOLTWATT = 2,
+ DRC = 3,
+ WATTPF = 4,
+ WATTVAR = 5,
+ AVR = 6
+ );
-interface
+ // Combi Modes
+ TInvControl2CombiMode = (
+ NONE_COMBMODE = 0,
+ VV_VW = 1,
+ VV_DRC = 2
+ );
+{$POP}
-uses
+ ERateofChangeMode = (
+ INACTIVE,
+ LPF,
+ RISEFALL
+ );
- {$IFDEF FPC}gqueue{$else}System.Generics.Collections{$ENDIF},
- Command, ControlClass, ControlElem, CktElement, DSSClass, bus, PCElement,PVSystem2, Storage2, Storage2Vars, Arraydef, ucomplex,
- utilities, XYcurve, Dynamics, DSSPointerList, Classes, StrUtils;
+ TInvControl2 = class(TControlClass)
-type
+ PRIVATE
+ XY_CurveClass: TDSSClass;
+
+ PROTECTED
+ procedure DefineProperties; override;
+ PUBLIC
+ constructor Create(dssContext: TDSSContext);
+ destructor Destroy; OVERRIDE;
- ERateofChangeMode = (
- INACTIVE,
- LPF,
- RISEFALL
- );
-
- TRollAvgWindow = class(TObject)
-
- private
- sample : TQueue;
- sampletime : TQueue;
- runningsumsample : Double;
- runningsumsampletime : Double;
- bufferlength : Integer;
- bufferfull : Boolean;
- function Get_AvgVal : Double;
- function Get_AccumSec : Double;
-
- procedure Set_BuffLength(const Value: Integer);
-
- public
- constructor Create();
- destructor Destroy; override;
- procedure Add(IncomingSampleValue: Double;IncomingSampleTime: Double;VAvgWindowLengthSec:Double);
-
-
- Property AvgVal :Double Read Get_AvgVal;
- Property AccumSec :Double Read Get_AccumSec;
- Property BuffLength :Integer Read bufferlength Write Set_BuffLength;
- end;
-
- TInvControl2 = class(TControlClass)
-
- private
- XY_CurveClass: TDSSClass;
-
- protected
- procedure DefineProperties;
- function MakeLike(const InvControl2Name:AnsiString):Integer;Override;
-
- public
- constructor Create(dssContext: TDSSContext);
- destructor Destroy; override;
-
- function Edit():Integer; override; // uses global parser
- function NewObject(const ObjName:AnsiString):Integer; override;
- function GetXYCurve(Const CurveName: AnsiString; InvControl2Mode: Integer): TXYcurveObj;
- procedure UpdateAll();
- end;
-
- TInvControl2Obj = class(TControlElem)
-
- private
- ControlMode : Integer;
- CombiControlMode : Integer;
- ControlActionHandle : Integer;
- ControlledElement : Array of TPCElement;
- MonitoredElement : TDSSCktElement; // First DER element for now (the first element from ControlledElement TDSSPointerList)
-
- {Variables for voltages}
- FVreg : Double;
- FAvgpVpuPrior : Array of Double;
- FAvgpDRCVpuPrior : Array of Double;
- FPresentVpu : Array of Double;
- FPresentDRCVpu : Array of Double;
- FVpuSolution : Array of Array of Double;
- FVpuSolutionIdx : Integer;
-
- {Variables for convergence process}
- FdeltaQ_factor : Double;
- FdeltaP_factor : Double;
-
- FdeltaQFactor : Array of Double;
- FdeltaPFactor : Array of Double;
- DeltaV_old : Array of Double;
-
- FVoltageChangeTolerance : Double;
- FVarChangeTolerance : Double;
- FActivePChangeTolerance : Double;
-
- // Active power
- PLimitVW : Array of Double;
- POldVWpu : Array of Double;
- FFlagVWOperates : Array of Boolean; // Flag enabled when volt-watt Pdesired is less than 1. So volt-watt algorithm starts to work
- PLimitVWpu : Array of Double;
- PLimitLimitedpu : Array of Double;
- PLimitEndpu : Array of Double;
- PLimitOptionpu : Array of Double;
- kW_out_desiredpu : Array of Double;
- kW_out_desired : Array of Double;
-
- // Reactive power
- QDesireEndpu : Array of Double; // Q value used in the convergency algorithm
- QDesireVVpu : Array of Double; // Q desired caculated in volt-var curve
- QDesireWPpu : Array of Double; // Q desired caculated in watt-pf curve
- QDesireWVpu : Array of Double; // Q desired caculated in watt-var curve
- QDesireDRCpu : Array of Double; // Q desired from the DRC equation
- QDesireAVRpu: Array of Double;
- QDesireLimitedpu : Array of Double; // Calculates possible Q considering kVA (watt priority) and kvarlimit limits
- QDesireOptionpu : Array of Double; // Calculates Q Limit considering LPF and RF
- QDesiredVV : Array of Double; // volt-var new set-point
- QDesiredWP : Array of Double; // watt-pf new set-point
- QDesiredWV : Array of Double; // watt-var new set-point
- QDesiredAVR: Array of Double;
- QOld : Array of Double;
- QOldVV : Array of Double;
- QOldAVR: Array of Double;
- QOldDRC : Array of Double;
- QOldVVDRC : Array of Double;
- QDesiredDRC : Array of Double; //dynamic reactive power new set-point
- QDesiredVVDRC : Array of Double;
-
- {Variables of functions that CONTROL reactive power}
- QHeadRoom : Array of Double;
- QHeadRoomNeg : Array of Double;
- Qoutputpu : Array of Double;
- QoutputVVpu : Array of Double;
- QoutputDRCpu : Array of Double;
- QoutputVVDRCpu : Array of Double;
- QoutputAVRpu: Array of Double;
-
- FPriorvarspu : Array of Double;
- FPriorvars : Array of Double;
-
- {Variables of functions that LIMIT active power}
- PBase : Array of Double;
-
- FPriorWattspu : Array of Double;
- FPriorwatts : Array of Double;
-
- {Variables of DER element}
- FDERPointerList : TDSSPointerList;
- FListSize : Integer;
- FDERNameList : TStringList;
- FVBase : Array of Double;
- FVarFollowInverter : Array of Boolean;
- FInverterON : Array of Boolean;
- FpresentkW : Array of Double;
- FkVARating : Array of Double;
- Fpresentkvar : Array of Double;
- FkvarLimit : Array of Double;
- FkvarLimitNeg : Array of Double;
- FCurrentkvarLimit : Array of Double;
- FCurrentkvarLimitNeg : Array of Double;
- FDCkWRated : Array of Double; // Pmpp for PVSystem, kWRated for Storage
- FpctDCkWRated : Array of Double; // pctPmpp for PVSystem, pctkWRated for Storage
- FEffFactor : Array of Double;
- FDCkW : Array of Double; // PanelkW for PVSystem, DCkW for Storage
- FPPriority : Array of Boolean;
- NPhasesDER : Array of Integer;
- NCondsDER : Array of Integer;
-
- {Variables for monitored Bus/buses}
- FMonBusesNameList : TStringList;
- FMonBusesPhase : Integer;
- FUsingMonBuses : Boolean;
- FMonBuses : Array of AnsiString;
- FMonBusesIndex : Integer;
- FMonBusesVbase : pDoubleArray;
- FMonBusesNodes : Array of Array of Integer;
-
- {Variables for LPF and RF options}
- RateofChangeMode : ERateofChangeMode;
- FLPFTau : Double;
- FRiseFallLimit : Double;
- FPriorPLimitOptionpu : Array of Double;
- FPriorQDesireOptionpu : Array of Double;
-
- {Variables of the smart inverter functions}
- FVoltage_CurveX_ref : Integer; // valid values are 0: = Vref (rated), 1:= avg
- FReacPower_ref : AnsiString;
- FVoltwattYAxis : Integer; // 1 = %Pmpp, 0 = %Available power
+ function NewObject(const ObjName: Ansistring; Activate: Boolean = True): Pointer; OVERRIDE;
+
+ procedure UpdateAll();
+ end;
+
+ TInvControl2Obj = class(TControlElem)
+ PRIVATE
+ ControlActionHandle: Integer;
+ ControlledElement: array of TPCElement;
+ MonitoredElement: TDSSCktElement; // First DER element for now (the first element from ControlledElement TDSSPointerList)
+
+ // Variables for voltages
+ FVreg: Double;
+ FAvgpVpuPrior: array of Double;
+ FAvgpDRCVpuPrior: array of Double;
+ FPresentVpu: array of Double;
+ FPresentDRCVpu: array of Double;
+ FVpuSolution: array of array of Double;
+ FVpuSolutionIdx: Integer;
+
+ // Variables for convergence process
+ FdeltaQ_factor: Double;
+ FdeltaP_factor: Double;
+
+ FdeltaQFactor: array of Double;
+ FdeltaPFactor: array of Double;
+ DeltaV_old: array of Double;
+
+ FVoltageChangeTolerance: Double;
+ FVarChangeTolerance: Double;
+ FActivePChangeTolerance: Double;
+
+ // Active power
+ PLimitVW: array of Double;
+ POldVWpu: array of Double;
+ FFlagVWOperates: array of Boolean; // Flag enabled when volt-watt Pdesired is less than 1. So volt-watt algorithm starts to work
+ PLimitVWpu: array of Double;
+ PLimitLimitedpu: array of Double;
+ PLimitEndpu: array of Double;
+ PLimitOptionpu: array of Double;
+ kW_out_desiredpu: array of Double;
+ kW_out_desired: array of Double;
+
+ // Reactive power
+ QDesireEndpu: array of Double; // Q value used in the convergency algorithm
+ QDesireVVpu: array of Double; // Q desired caculated in volt-var curve
+ QDesireWPpu: array of Double; // Q desired caculated in watt-pf curve
+ QDesireWVpu: array of Double; // Q desired caculated in watt-var curve
+ QDesireDRCpu: array of Double; // Q desired from the DRC equation
+ QDesireAVRpu: array of Double;
+ QDesireLimitedpu: array of Double; // Calculates possible Q considering kVA (watt priority) and kvarlimit limits
+ QDesireOptionpu: array of Double; // Calculates Q Limit considering LPF and RF
+ QDesiredVV: array of Double; // volt-var new set-point
+ QDesiredWP: array of Double; // watt-pf new set-point
+ QDesiredWV: array of Double; // watt-var new set-point
+ QDesiredAVR: array of Double;
+ QOld: array of Double;
+ QOldVV: array of Double;
+ QOldAVR: array of Double;
+ QOldDRC: array of Double;
+ QOldVVDRC: array of Double;
+ QDesiredDRC: array of Double; //dynamic reactive power new set-point
+ QDesiredVVDRC: array of Double;
+
+ // Variables of functions that CONTROL reactive power
+ QHeadRoom: array of Double;
+ QHeadRoomNeg: array of Double;
+ Qoutputpu: array of Double;
+ QoutputVVpu: array of Double;
+ QoutputDRCpu: array of Double;
+ QoutputVVDRCpu: array of Double;
+ QoutputAVRpu: array of Double;
+
+ FPriorvarspu: array of Double;
+ FPriorvars: array of Double;
+
+ // Variables of functions that LIMIT active power
+ PBase: array of Double;
+
+ FPriorWattspu: array of Double;
+ FPriorwatts: array of Double;
+
+ // Variables of DER element
+ FDERPointerList: TDSSPointerList;
+ FListSize: Integer;
+ FVBase: array of Double;
+ FVarFollowInverter: array of Boolean;
+ FInverterON: array of Boolean;
+ FpresentkW: array of Double;
+ FkVARating: array of Double;
+ Fpresentkvar: array of Double;
+ FkvarLimit: array of Double;
+ FkvarLimitNeg: array of Double;
+ FCurrentkvarLimit: array of Double;
+ FCurrentkvarLimitNeg: array of Double;
+ FDCkWRated: array of Double; // Pmpp for PVSystem, kWRated for Storage
+ FpctDCkWRated: array of Double; // pctPmpp for PVSystem, pctkWRated for Storage
+ FEffFactor: array of Double;
+ FDCkW: array of Double; // PanelkW for PVSystem, DCkW for Storage
+ FPPriority: array of Boolean;
+ NPhasesDER: array of Integer;
+ NCondsDER: array of Integer;
+
+ // Variables for monitored Bus/buses
+ FMonBusesPhase: Integer;
+ FUsingMonBuses: Boolean;
+ FMonBuses: array of Ansistring;
+ FMonBusesIndex: Integer;
+ FMonBusesVbase: pDoubleArray;
+ FMonBusesNodes: array of array of Integer;
+
+ // Variables for LPF and RF options
+ RateofChangeMode: ERateofChangeMode;
+ FRiseFallLimit: Double;
+ FPriorPLimitOptionpu: array of Double;
+ FPriorQDesireOptionpu: array of Double;
+
+ // Variables of the smart inverter functions
+ FVoltage_CurveX_ref: Integer; // valid values are 0: = Vref (rated), 1:= avg
+ FReacPower_ref: Integer;
+ FVoltwattYAxis: Integer; // 1 = %Pmpp, 0 = %Available power
// volt-var
- Fvvc_curve_size : Integer; // length of the individual curve
- Fvvc_curve : TXYcurveObj;
- Fvvc_curvename : AnsiString;
- Fvvc_curveOffset : Double;
- Fvvc_curve2 : TXYcurveObj;
- FlagChangeCurve : Array of Boolean;
- FActiveVVCurve : Array of Integer;
- FVAvgWindowLengthSec : Double; // rolling average window length in seconds
- FRollAvgWindow : Array of TRollAvgWindow;
- FRollAvgWindowLength : Integer;
- priorRollAvgWindow : Array of Double;
- FRollAvgWindowLengthIntervalUnit : AnsiString;
+ Fvvc_curveOffset: Double;
+ FlagChangeCurve: array of Boolean;
+ FActiveVVCurve: array of Integer;
+ FRollAvgWindowLength: Integer;//FVAvgWindowLengthSec // rolling average window length in seconds
+ FRollAvgWindow: array of TRollAvgWindow;
+
+ priorRollAvgWindow: array of Double;
// watt-pf
- Fwattpf_curve_size : Integer;
- Fwattpf_curve : TXYcurveObj;
- Fwattpf_curvename : AnsiString;
- pf_wp_nominal : Double;
-
- // watt-var
- Fwattvar_curve_size : Integer;
- Fwattvar_curve : TXYcurveObj;
- Fwattvar_curvename : AnsiString;
+ pf_wp_nominal: Double;
// DRC
- FDbVMin : Double;
- FDbVMax : Double;
- FArGraLowV : Double;
- FArGraHiV : Double;
- deltaVDynReac : Array of Double;
- FDRCRollAvgWindowpu : Array of Double;
- FDRCRollAvgWindow : Array of TRollAvgWindow;
- FDRCRollAvgWindowLength : Integer;
- FDRCRollAvgWindowLengthIntervalUnit : AnsiString;
- priorDRCRollAvgWindow : Array of Double;
- FDRCVAvgWindowLengthSec : Double; // rolling average window length in seconds
-
- // volt-watt
- Fvoltwatt_curve_size : Integer;
- Fvoltwatt_curve : TXYcurveObj;
- Fvoltwatt_curvename : AnsiString;
-
- // volt-watt (charging)
- FvoltwattCH_curve_size : Integer;
- FvoltwattCH_curve : TXYcurveObj;
- FvoltwattCH_curvename : AnsiString;
+ FDbVMin: Double;
+ FDbVMax: Double;
+ deltaVDynReac: array of Double;
+ FDRCRollAvgWindowpu: array of Double;
+ FDRCRollAvgWindow: array of TRollAvgWindow;
+ priorDRCRollAvgWindow: array of Double;
+
// Active voltage regulation (AVR)
- Fv_setpoint: Double;
- DQDV: Array of Double;
- Fv_setpointLimited: Array of Double;
- FAvgpAVRVpuPrior: Array of Double;
-
- {Flags used to record function states. They are interval variables of DER}
- FVVOperation : Array of Double;
- FVWOperation : Array of Double;
- FDRCOperation : Array of Double;
- FVVDRCOperation : Array of Double;
- FWPOperation : Array of Double;
- FWVOperation : Array of Double;
- FAVROperation: Array of Double;
-
- {Others}
- cBuffer : Array of Array of Complex; // Complex array buffer
- CondOffset : Array of Integer; // Offset for monitored terminal
- FPendingChange : Array of Integer;
-
- {Functions and Procedures}
- procedure Set_PendingChange(Value: Integer;DevIndex: Integer);
- function Get_PendingChange(DevIndex: Integer):Integer;
- function InterpretAvgVWindowLen(const s:AnsiString):Integer;
- function InterpretDRCAvgVWindowLen(const s:AnsiString):Integer;
- function ReturnElementsList:AnsiString;
- procedure UpdateInvControl(i:integer);
- procedure UpdateDERParameters(i: Integer);
- procedure CalcVoltWatt_watts(j: Integer);
- procedure CalcQVVcurve_desiredpu(j: Integer);
- procedure CalcQWPcurve_desiredpu(j: Integer);
- procedure CalcQWVcurve_desiredpu(j: Integer);
- procedure CalcQDRC_desiredpu(j: Integer);
- procedure CalcQAVR_desiredpu(j: Integer);
- procedure Check_Qlimits(j: Integer; Q: Double);
- procedure Check_Qlimits_WV(j: Integer; Q: Double);
- procedure Calc_PQ_WV(j: Integer);
- procedure Calc_QHeadRoom(j: Integer);
- procedure CalcVoltVar_vars(j: Integer);
- procedure CalcAVR_vars(j: Integer);
- procedure CalcWATTPF_vars(j: Integer);
- procedure CalcWATTVAR_vars(j: Integer);
- procedure CalcDRC_vars(j: Integer);
- procedure CalcVVDRC_vars(j: Integer);
- procedure CalcLPF(m: Integer; powertype: AnsiString; LPF_desiredpu: Double);
- procedure CalcRF(m: Integer; powertype: AnsiString; RF_desiredpu: Double);
- procedure Calc_PBase(j: Integer);
- procedure Check_Plimits(j: Integer; P:Double);
- procedure CalcPVWcurve_limitpu(j: Integer);
- procedure GetmonVoltage(var Vpresent: Double; i: Integer; BasekV: Double);
- procedure Change_deltaQ_factor(j: Integer);
- procedure Change_deltaP_factor(j: Integer);
-
-
-
- public
-
- constructor Create(ParClass:TDSSClass; const InvControl2Name:AnsiString);
- destructor Destroy; override;
-
- procedure Set_Enabled(Value:Boolean);Override;
- procedure MakePosSequence(); Override; // Make a positive Sequence Model
- procedure RecalcElementData(); Override;
- procedure CalcYPrim(); Override; // Always Zero for a InvControl
-
- // Sample control quantities and set action times in Control Queue
- procedure Sample(); Override;
-
- // do the action that is pending from last sample
- procedure DoPendingAction(Const Code, ProxyHdl:Integer); Override;
-
- procedure Reset; Override; // Reset to initial defined state
-
- procedure GetCurrents(Curr: pComplexArray); Override; // Get present value of terminal Curr
-
-
- procedure InitPropertyValues(ArrayOffset:Integer);Override;
- procedure DumpProperties(F: TFileStream; Complete:Boolean);Override;
-
-
- function MakeDERList:Boolean;
- function GetPropertyValue(Index:Integer):AnsiString;Override;
-
- Property PendingChange[DevIndex: Integer]:Integer Read Get_PendingChange Write Set_PendingChange;
-
- {Properties that give access to this Class variables}
- property Mode : Integer read ControlMode;
- property CombiMode : Integer read CombiControlMode;
- property DERNameList : TStringList read FDERNameList;
- property vvc_curve1 : AnsiString read Fvvc_curvename;
- property hysteresis_offset : Double read Fvvc_curveOffset;
- property voltage_curvex_ref : Integer read FVoltage_CurveX_ref;
- property avgwindowlen : Integer read FRollAvgWindowLength;
- property voltwatt_curve : AnsiString read Fvoltwatt_curvename;
- property voltwattCH_curve : AnsiString read FvoltwattCH_curvename;
- property DbVMin : Double read FDbVMin;
- property DbVMax : Double read FDbVMax;
- property ArGraLowV : Double read FArGraLowV;
- property ArGraHiV : Double read FArGraHiV;
- property DynReacavgwindowlen : Integer read FDRCRollAvgWindowLength;
- property DeltaQ_factor : Double read FDeltaQ_factor;
- property VoltageChangeTolerance : Double read FVoltageChangeTolerance;
- property VarChangeTolerance : Double read FVarChangeTolerance;
- property VoltwattYAxis : Integer read FVoltwattYAxis;
- //property RateofChangeMode : AnsiString read
- property LPFTau : Double read FLPFTau;
- property RiseFallLimit : Double read FRiseFallLimit;
- property DeltaP_factor : Double read FDeltaP_factor;
- //property EventLog : AnsiString read
- property RefReactivePower : AnsiString read FReacPower_ref;
- property ActivePChangeTolerance : Double read FActivePChangeTolerance;
- property monVoltageCalc : Integer read FMonBusesPhase;
- property monBus : TStringList read FMonBusesNameList;
- property monBusVbase : pDoubleArray read FMonBusesVbase;
- property v_setpoint: Double read Fv_setpoint;
- // Need to include the new modes here
-
- end;
-
-
-IMPLEMENTATION
+ Fv_setpoint: Double;
+ DQDV: array of Double;
+ Fv_setpointLimited: array of Double;
+ FAvgpAVRVpuPrior: array of Double;
+
+ // Flags used to record function states. They are interval variables of DER
+ FVVOperation: array of Double;
+ FVWOperation: array of Double;
+ FDRCOperation: array of Double;
+ FVVDRCOperation: array of Double;
+ FWPOperation: array of Double;
+ FWVOperation: array of Double;
+ FAVROperation: array of Double;
+
+ // Others
+ cBuffer: array of array of Complex; // Complex array buffer
+ CondOffset: array of Integer; // Offset for monitored terminal
+ FPendingChange: array of Integer;
+
+ procedure Set_PendingChange(Value: Integer; DevIndex: Integer);
+ function Get_PendingChange(DevIndex: Integer): Integer;
+ procedure UpdateInvControl(i: Integer);
+ procedure UpdateDERParameters(i: Integer);
+ procedure CalcVoltWatt_watts(j: Integer);
+ procedure CalcQVVcurve_desiredpu(j: Integer);
+ procedure CalcQWPcurve_desiredpu(j: Integer);
+ procedure CalcQWVcurve_desiredpu(j: Integer);
+ procedure CalcQDRC_desiredpu(j: Integer);
+ procedure CalcQAVR_desiredpu(j: Integer);
+ procedure Check_Qlimits(j: Integer; Q: Double);
+ procedure Check_Qlimits_WV(j: Integer; Q: Double);
+ procedure Calc_PQ_WV(j: Integer);
+ procedure Calc_QHeadRoom(j: Integer);
+ procedure CalcVoltVar_vars(j: Integer);
+ procedure CalcAVR_vars(j: Integer);
+ procedure CalcWATTPF_vars(j: Integer);
+ procedure CalcWATTVAR_vars(j: Integer);
+ procedure CalcDRC_vars(j: Integer);
+ procedure CalcVVDRC_vars(j: Integer);
+ procedure CalcLPF(m: Integer; powertype: Ansistring; LPF_desiredpu: Double);
+ procedure CalcRF(m: Integer; powertype: Ansistring; RF_desiredpu: Double);
+ procedure Calc_PBase(j: Integer);
+ procedure Check_Plimits(j: Integer; P: Double);
+ procedure CalcPVWcurve_limitpu(j: Integer);
+ procedure GetmonVoltage(var Vpresent: Double; i: Integer; BasekV: Double);
+ procedure Change_deltaQ_factor(j: Integer);
+ procedure Change_deltaP_factor(j: Integer);
+
+
+ PUBLIC
+ DERNameList: TStringList;
+ MonBusesNameList: TStringList;
+ LPFTau: Double; // Variable for LPF and RF options
+
+ // DRC
+ FDRCRollAvgWindowLength: Integer; //FDRCVAvgWindowLengthSec // rolling average window length in seconds
+ FArGraLowV: Double;
+ FArGraHiV: Double;
+
+ Fvvc_curve: TXYcurveObj; // volt-var
+ Fwattpf_curve: TXYcurveObj;
+ Fwattvar_curve: TXYcurveObj;
+ Fvoltwatt_curve: TXYcurveObj; // volt-watt
+ FvoltwattCH_curve: TXYcurveObj; // volt-watt (charging)
+
+ ControlMode: TInvControl2ControlMode;
+ CombiMode: TInvControl2CombiMode;
+
+ constructor Create(ParClass: TDSSClass; const InvControl2Name: Ansistring);
+ destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
+
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
+ procedure RecalcElementData(); OVERRIDE;
+ procedure Sample(); OVERRIDE; // Sample control quantities and set action times in Control Queue
+ procedure DoPendingAction(const Code, ProxyHdl: Integer); OVERRIDE; // do the action that is pending from last sample
+ procedure Reset; OVERRIDE; // Reset to initial defined state
+ function MakeDERList: Boolean;
+ property PendingChange[DevIndex: Integer]: Integer READ Get_PendingChange WRITE Set_PendingChange;
+ end;
-uses
- ParserDel, Sysutils, DSSClassDefs, DSSGlobals, Circuit, uCmatrix, MathUtil, Math,
+implementation
+
+uses
+ Sysutils,
+ DSSClassDefs,
+ DSSGlobals,
+ Circuit,
+ uCmatrix,
+ MathUtil,
+ Math,
DSSHelper,
DSSObjectHelper;
+type
+ TObj = TInvControl2Obj;
+ TProp = TInvControl2Prop;
+
const
+ NumPropsThisClass = Ord(High(TProp));
- NumPropsThisClass = 33;
+ ReacPower_VARAVAL = 0;
+ ReacPower_VARMAX = 1;
NONE = 0;
CHANGEVARLEVEL = 1;
@@ -394,1169 +383,686 @@ TInvControl2Obj = class(TControlElem)
CHANGEDRCVVARLEVEL = 4;
AVGPHASES = -1;
- MAXPHASE = -2;
- MINPHASE = -3;
+ MAXPHASE = -2;
+ MINPHASE = -3;
FLAGDELTAQ = -1.0;
FLAGDELTAP = -1.0;
DELTAQDEFAULT = 0.5;
DELTAPDEFAULT = 0.5;
- // Modes
- NONE_MODE = 0;
- VOLTVAR = 1;
- VOLTWATT = 2;
- DRC = 3;
- WATTPF = 4;
- WATTVAR = 5;
- AVR = 6;
-
- // Combi Modes
- NONE_COMBMODE = 0;
- VV_VW = 1;
- VV_DRC = 2;
-
type
TPVSystemObj = TPVSystem2Obj;
TStorageObj = TStorage2Obj;
+var
+ PropInfo: Pointer = NIL;
+ ModeEnum, CombiModeEnum, VoltageCurveXRefEnum, VoltWattYAxisEnum, RoCEnum, RefQEnum: TDSSEnum;
constructor TInvControl2.Create(dssContext: TDSSContext);
- begin
- Inherited Create(dssContext);
-
- Class_name := 'InvControl';
- DSSClassType := DSSClassType + INV_CONTROL;
-
- DefineProperties;
+begin
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ ModeEnum := TDSSEnum.Create('InvControl: Control Mode', True, 1, 5,
+ ['Voltvar', 'VoltWatt', 'DynamicReaccurr', 'WattPF', 'Wattvar', 'AVR'],
+ [ord(VOLTVAR), ord(VOLTWATT), ord(DRC), ord(WATTPF), ord(WATTVAR), ord(AVR)]);
+ CombiModeEnum := TDSSEnum.Create('InvControl: Combi Mode', True, 4, 4,
+ ['VV_VW', 'VV_DRC'], [ord(VV_VW), ord(VV_DRC)]);
+ VoltageCurveXRefEnum := TDSSEnum.Create('InvControl: Voltage Curve X Ref', True, 1, 2,
+ ['Rated', 'Avg', 'RAvg'], [0, 1, 2]);
+ VoltWattYAxisEnum := TDSSEnum.Create('InvControl: Volt-watt Y-Axis', True, 1, 2,
+ ['PAvailablePU', 'PMPPPU', 'PctPMPPPU', 'KVARatingPU'], [0, 1, 2, 3]);
+ RoCEnum := TDSSEnum.Create('InvControl: Rate-of-change Mode', True, 3, 3,
+ ['Inactive', 'LPF', 'RiseFall'], [ord(INACTIVE), ord(LPF), ord(RISEFALL)]);
+ RefQEnum := TDSSEnum.Create('InvControl: Reactive Power Reference', True, 4, 4,
+ ['VARAVAL', 'VARMAX'], [0, 1]);
+ RefQEnum.AllowLonger := True;
+ end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
- XY_CurveClass := GetDSSClassPtr(DSS, 'XYCurve');
+ XY_CurveClass := GetDSSClassPtr(dssContext, 'XYCurve');
- end;
+ inherited Create(dssContext, INV_CONTROL, 'InvControl');
+end;
destructor TInvControl2.Destroy;
- begin
- Inherited Destroy;
- end;
+begin
+ inherited Destroy;
+end;
-procedure TInvControl2.DefineProperties;
- begin
+function GetMonBusesCount(Obj: TObj): Integer;
+begin
+ Result := Obj.MonBusesNameList.Count;
+end;
+procedure TInvControl2.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
+begin
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
- PropertyName[1] := 'DERList';
- PropertyName[2] := 'Mode';
- PropertyName[3] := 'CombiMode';
- PropertyName[4] := 'vvc_curve1';
- PropertyName[5] := 'hysteresis_offset';
- PropertyName[6] := 'voltage_curvex_ref';
- PropertyName[7] := 'avgwindowlen';
-
- PropertyName[8] := 'voltwatt_curve';
-
- //following for dynamic reactive current mode
- PropertyName[9] := 'DbVMin';
- PropertyName[10] := 'DbVMax';
- PropertyName[11] := 'ArGraLowV';
- PropertyName[12] := 'ArGraHiV';
- PropertyName[13] := 'DynReacavgwindowlen';
- PropertyName[14] := 'deltaQ_Factor';
- PropertyName[15] := 'VoltageChangeTolerance';
- PropertyName[16] := 'VarChangeTolerance';
- PropertyName[17] := 'VoltwattYAxis';
- PropertyName[18] := 'RateofChangeMode';
- PropertyName[19] := 'LPFTau';
- PropertyName[20] := 'RiseFallLimit';
- PropertyName[21] := 'deltaP_Factor';
- PropertyName[22] := 'EventLog';
- PropertyName[23] := 'RefReactivePower';
- PropertyName[24] := 'ActivePChangeTolerance';
- PropertyName[25] := 'monVoltageCalc';
- PropertyName[26] := 'monBus';
- PropertyName[27] := 'MonBusesVbase';
- PropertyName[28] := 'voltwattCH_curve';
- PropertyName[29] := 'wattpf_curve';
- PropertyName[30] := 'wattvar_curve';
- PropertyName[31] := 'VV_RefReactivePower';
- PropertyName[32] := 'PVSystemList';
- PropertyName[33] := 'Vsetpoint';
-
- PropertyHelp[1] := 'Array list of PVSystem and/or Storage elements to be controlled. ' +
- 'If not specified, all PVSystem and Storage in the circuit are assumed to be controlled by this control. ' +CRLF+CRLF+
- 'No capability of hierarchical control between two controls for a single element is implemented at this time.';
-
- PropertyHelp[2] := 'Smart inverter function in which the InvControl will control the PC elements specified in DERList, according to the options below:' +CRLF+CRLF+
- 'Must be one of: {VOLTVAR* | VOLTWATT | DYNAMICREACCURR | WATTPF | WATTVAR} ' +CRLF+
- 'if the user desires to use modes simultaneously, then set the CombiMode property. Setting the Mode to any valid value disables combination mode.'+
-
- CRLF+CRLF+'In volt-var mode (Default). This mode attempts to CONTROL the vars, according to one or two volt-var curves, depending on the monitored voltages, present active power output, and the capabilities of the PVSystem/Storage. ' +
- CRLF+CRLF+'In volt-watt mode. This mode attempts to LIMIT the watts, according to one defined volt-watt curve, depending on the monitored voltages and the capabilities of the PVSystem/Storage. '+
- CRLF+CRLF+'In dynamic reactive current mode. This mode attempts to increasingly counter deviations by CONTROLLING vars, depending on the monitored voltages, present active power output, and the capabilities of the of the PVSystem/Storage.'+
- CRLF+CRLF+'In watt-pf mode. This mode attempts to CONTROL the vars, according to a watt-pf curve, depending on the present active power output, and the capabilities of the PVSystem/Storage. '+
- CRLF+CRLF+'In watt-var mode. This mode attempts to CONTROL the vars, according to a watt-var curve, depending on the present active power output, and the capabilities of the PVSystem/Storage. ';
-
- PropertyHelp[3] := 'Combination of smart inverter functions in which the InvControl will control the PC elements in DERList, according to the options below: '+CRLF+CRLF+
- 'Must be a combination of the following: {VV_VW | VV_DRC}. Default is to not set this property, in which case the single control mode in Mode is active. ' +
-
- CRLF+CRLF+'In combined VV_VW mode, both volt-var and volt-watt control modes are active simultaneously. See help individually for volt-var mode and volt-watt mode in Mode property.'+
- CRLF+'Note that the PVSystem/Storage will attempt to achieve both the volt-watt and volt-var set-points based on the capabilities of the inverter in the PVSystem/Storage (kVA rating, etc), any limits set on maximum active power,' +
-// CRLF+', any limits set on maximum reactive power. '+
-// CRLF+'Precedence will be given to either watt production or var production based on the setting of RefReactivePower.'+
- CRLF+CRLF+'In combined VV_DRC, both the volt-var and the dynamic reactive current modes are simultaneously active.';
-// CRLF+CRLF+'The volt-var function will attempt to achieve its set-point based on the volt-var curve, and present voltage. The dynamic '+
-// CRLF+'reactive power mode function will also be active and it will add or subtract from the reactive power set-point desired by the volt-var function.'+
-// CRLF+'Note that the precedence of active and reactive power production is defined by the RefReactivePower property. In no event will the reactive '+
-// CRLF+'power exceed the maximum var limit of the PVSystem, and the combination of the active and reactive power output will not exceed the kVA rating of '+
-// CRLF+'the inverter (set in the PVSystem/Storage).';
-
- PropertyHelp[4] := 'Required for VOLTVAR mode. '+CRLF+CRLF+
- 'Name of the XYCurve object containing the volt-var curve. The positive values of the y-axis of the volt-var curve represent values in pu of the provided base reactive power. ' +
- 'The negative values of the y-axis are values in pu of the absorbed base reactive power. ' +CRLF+
- 'Provided and absorbed base reactive power values are defined in the RefReactivePower property' +CRLF+CRLF+
- 'Units for the x-axis are per-unit voltage, which may be in per unit of the rated voltage for the PVSystem/Storage, or may be in per unit of the average voltage at the terminals over a user-defined number of prior solutions. ';
-
- PropertyHelp[5] := 'Required for VOLTVAR mode, and defaults to 0. '+CRLF+CRLF+
- 'for the times when the terminal voltage is decreasing, this is the off-set in per-unit voltage of a curve whose shape is the same as vvc_curve. '+
- 'It is offset by a certain negative value of per-unit voltage, which is defined by the base quantity for the x-axis of the volt-var curve (see help for voltage_curvex_ref)'+CRLF+CRLF+
- 'if the PVSystem/Storage terminal voltage has been increasing, and has not changed directions, utilize vvc_curve1 for the volt-var response. '+CRLF+CRLF+
- 'if the PVSystem/Storage terminal voltage has been increasing and changes directions and begins to decrease, then move from utilizing vvc_curve1 to a volt-var curve of the same shape, but offset by a certain per-unit voltage value. '+CRLF+CRLF+
- 'Maintain the same per-unit available var output level (unless head-room has changed due to change in active power or kva rating of PVSystem/Storage). Per-unit var values remain the same for this internally constructed second curve (hysteresis curve). '+CRLF+CRLF+
- 'if the terminal voltage has been decreasing and changes directions and begins to increase , then move from utilizing the offset curve, back to the vvc_curve1 for volt-var response, but stay at the same per-unit available vars output level.';
-
- PropertyHelp[6] := 'Required for VOLTVAR and VOLTWATT modes, and defaults to rated. Possible values are: {rated|avg|ravg}. '+CRLF+CRLF+
- 'Defines whether the x-axis values (voltage in per unit) for vvc_curve1 and the volt-watt curve corresponds to:'+CRLF+CRLF+
- 'rated. The rated voltage for the PVSystem/Storage object (1.0 in the volt-var curve equals rated voltage).'+CRLF+CRLF+
- 'avg. The average terminal voltage recorded over a certain number of prior power-flow solutions.'+CRLF+
- 'with the avg setting, 1.0 per unit on the x-axis of the volt-var curve(s) corresponds to the average voltage.'+CRLF+
- 'from a certain number of prior intervals. See avgwindowlen parameter.'+CRLF+CRLF+
- 'ravg. Same as avg, with the exception that the avgerage terminal voltage is divided by the rated voltage.';
-
- PropertyHelp[7] := 'Required for VOLTVAR mode and VOLTWATT mode, and defaults to 0 seconds (0s). '+CRLF+CRLF+
- 'Sets the length of the averaging window over which the average PVSystem/Storage terminal voltage is calculated. '+CRLF+CRLF+
- 'Units are indicated by appending s, m, or h to the integer value. '+CRLF+CRLF+
- 'The averaging window will calculate the average PVSystem/Storage terminal voltage over the specified period of time, up to and including the last power flow solution. '+CRLF+CRLF+
- 'Note, if the solution stepsize is larger than the window length, then the voltage will be assumed to have been constant over the time-frame specified by the window length.';
-
- PropertyHelp[8] := 'Required for VOLTWATT mode. '+CRLF+CRLF+
- 'Name of the XYCurve object containing the volt-watt curve. '+CRLF+CRLF+
- 'Units for the x-axis are per-unit voltage, which may be in per unit of the rated voltage for the PVSystem/Storage, or may be in per unit of the average voltage at the terminals over a user-defined number of prior solutions. '+CRLF+CRLF+
- 'Units for the y-axis are either in one of the options described in the VoltwattYAxis property. ';
-
- PropertyHelp[9] := 'Required for the dynamic reactive current mode (DYNAMICREACCURR), and defaults to 0.95 per-unit voltage (referenced to the PVSystem/Storage object rated voltage or a windowed average value). '+CRLF+CRLF+
- 'This parameter is the minimum voltage that defines the voltage dead-band within which no reactive power is allowed to be generated. ';
-
- PropertyHelp[10] := 'Required for the dynamic reactive current mode (DYNAMICREACCURR), and defaults to 1.05 per-unit voltage (referenced to the PVSystem object rated voltage or a windowed average value). '+CRLF+CRLF+
- 'This parameter is the maximum voltage that defines the voltage dead-band within which no reactive power is allowed to be generated. ';
-
- PropertyHelp[11] := 'Required for the dynamic reactive current mode (DYNAMICREACCURR), and defaults to 0.1 '+CRLF+CRLF+
- 'This is a gradient, expressed in unit-less terms of %/%, to establish the ratio by which percentage capacitive reactive power production is increased as the percent delta-voltage decreases below DbVMin. '+CRLF+CRLF+
- 'Percent delta-voltage is defined as the present PVSystem/Storage terminal voltage minus the moving average voltage, expressed as a percentage of the rated voltage for the PVSystem/Storage object. '+CRLF+CRLF+
- 'Note, the moving average voltage for the dynamic reactive current mode is different than the moving average voltage for the volt-watt and volt-var modes.';
-
- PropertyHelp[12] := 'Required for the dynamic reactive current mode (DYNAMICREACCURR), and defaults to 0.1 '+CRLF+CRLF+
- 'This is a gradient, expressed in unit-less terms of %/%, to establish the ratio by which percentage inductive reactive power production is increased as the percent delta-voltage decreases above DbVMax. '+CRLF+CRLF+
- 'Percent delta-voltage is defined as the present PVSystem/Storage terminal voltage minus the moving average voltage, expressed as a percentage of the rated voltage for the PVSystem/Storage object. '+CRLF+CRLF+
- 'Note, the moving average voltage for the dynamic reactive current mode is different than the mmoving average voltage for the volt-watt and volt-var modes.';
-
- PropertyHelp[13] := 'Required for the dynamic reactive current mode (DYNAMICREACCURR), and defaults to 1 seconds (1s). do not use a value smaller than 1.0 '+CRLF+CRLF+
- 'Sets the length of the averaging window over which the average PVSystem/Storage terminal voltage is calculated '+
- 'for the dynamic reactive current mode. '+CRLF+CRLF+
- 'Units are indicated by appending s, m, or h to the integer value. '+CRLF+CRLF+
- 'Typically this will be a shorter averaging window than the volt-var and volt-watt averaging window.'+CRLF+CRLF+
- 'The averaging window will calculate the average PVSystem/Storage terminal voltage over the specified period of time, up to and including the last power flow solution. Note, if the solution stepsize is larger than '+
- 'the window length, then the voltage will be assumed to have been constant over the time-frame specified by the window length.';
-
- PropertyHelp[14] := 'Required for the VOLTVAR and DYNAMICREACCURR modes. Defaults to -1.0. '+CRLF+CRLF+
- 'Defining -1.0, OpenDSS takes care internally of delta_Q itself. It tries to improve convergence as well as speed up process'+CRLF+CRLF+
- 'Sets the maximum change (in per unit) from the prior var output level to the desired var output level during each control iteration. '+CRLF+CRLF+CRLF+
- 'if numerical instability is noticed in solutions such as var sign changing from one control iteration to the next and voltages oscillating between two values with some separation, '+
- 'this is an indication of numerical instability (use the EventLog to diagnose). '+CRLF+CRLF+
- 'if the maximum control iterations are exceeded, and no numerical instability is seen in the EventLog of via monitors, then try increasing the value of this parameter to reduce the number '+
- 'of control iterations needed to achieve the control criteria, and move to the power flow solution.';
-
- PropertyHelp[15] := 'Defaults to 0.0001 per-unit voltage. This parameter should only be modified by advanced users of the InvControl. '+CRLF+CRLF+
- 'Tolerance in pu of the control loop convergence associated to the monitored voltage in pu. ' +
- 'This value is compared with the difference of the monitored voltage in pu of the current and previous control iterations of the control loop'+CRLF+CRLF+
-
- 'This voltage tolerance value plus the var/watt tolerance value (VarChangeTolerance/ActivePChangeTolerance) determine, together, when to stop control iterations by the InvControl. '+CRLF+CRLF+
-
- 'If an InvControl is controlling more than one PVSystem/Storage, each PVSystem/Storage has this quantity calculated independently, and so an individual '+
- 'PVSystem/Storage may reach the tolerance within different numbers of control iterations.';
-
- PropertyHelp[16] := 'Required for VOLTVAR and DYNAMICREACCURR modes. Defaults to 0.025 per unit of the base provided or absorbed reactive power described in the RefReactivePower property '+
-
- 'This parameter should only be modified by advanced users of the InvControl. '+CRLF+CRLF+
-
- 'Tolerance in pu of the convergence of the control loop associated with reactive power. ' +
- 'For the same control iteration, this value is compared to the difference, as an absolute value (without sign), between the desired reactive power value in pu and the output reactive power in pu of the controlled element.'+CRLF+CRLF+
-
- 'This reactive power tolerance value plus the voltage tolerance value (VoltageChangeTolerance) determine, together, when to stop control iterations by the InvControl. '+CRLF+CRLF+
-
- 'If an InvControl is controlling more than one PVSystem/Storage, each PVSystem/Storage has this quantity calculated independently, and so an individual '+
- 'PVSystem/Storage may reach the tolerance within different numbers of control iterations.';
-
- PropertyHelp[17] := 'Required for VOLTWATT mode. Must be one of: {PMPPPU* | PAVAILABLEPU| PCTPMPPPU | KVARATINGPU}. The default is PMPPPU. '+CRLF+CRLF+
- 'Units for the y-axis of the volt-watt curve while in volt-watt mode. '+CRLF+CRLF+
- 'When set to PMPPPU. The y-axis corresponds to the value in pu of Pmpp property of the PVSystem. '+CRLF+CRLF+
- 'When set to PAVAILABLEPU. The y-axis corresponds to the value in pu of the available active power of the PVSystem. '+CRLF+CRLF+
- 'When set to PCTPMPPPU. The y-axis corresponds to the value in pu of the power Pmpp multiplied by 1/100 of the %Pmpp property of the PVSystem.' +CRLF+CRLF+
- 'When set to KVARATINGPU. The y-axis corresponds to the value in pu of the kVA property of the PVSystem.';
-
-
- PropertyHelp[18] := 'Required for VOLTWATT and VOLTVAR mode. Must be one of: {INACTIVE* | LPF | RISEFALL }. The default is INACTIVE. '+CRLF+CRLF+
- 'Auxiliary option that aims to limit the changes of the desired reactive power and the active power limit between time steps, the alternatives are listed below: '
- +CRLF+CRLF+ 'INACTIVE. It indicates there is no limit on rate of change imposed for either active or reactive power output. '
- +CRLF+CRLF+ 'LPF. A low-pass RC filter is applied to the desired reactive power and/or the active power limit to determine the output power as a function of a time constant defined in the LPFTau property. '
- +CRLF+CRLF+ 'RISEFALL. A rise and fall limit in the change of active and/or reactive power expressed in terms of pu power per second, defined in the RiseFallLimit, is applied to the desired reactive power and/or the active power limit. ';
-
-
- PropertyHelp[19] := 'Not required. Defaults to 0 seconds. '+CRLF+CRLF+
- 'Filter time constant of the LPF option of the RateofChangeMode property. ' +
- 'The time constant will cause the low-pass filter to achieve 95% of the target value in 3 time constants.';
-
- PropertyHelp[20] := 'Not required. Defaults to no limit (-1). Must be -1 (no limit) or a positive value. '+CRLF+CRLF+
- 'Limit in power in pu per second used by the RISEFALL option of the RateofChangeMode property.' +
- 'The base value for this ramp is defined in the RefReactivePower property and/or in VoltwattYAxis.';
-
- PropertyHelp[21] := 'Required for the VOLTWATT modes. Defaults to -1.0. '+CRLF+CRLF+
- 'Defining -1.0, OpenDSS takes care internally of delta_P itself. It tries to improve convergence as well as speed up process'+CRLF+CRLF+
- 'Defining between 0.05 and 1.0, it sets the maximum change (in unit of the y-axis) from the prior active power output level to the desired active power output level during each control iteration. '+CRLF+CRLF+CRLF+
- 'If numerical instability is noticed in solutions such as active power changing substantially from one control iteration to the next and/or voltages oscillating between two values with some separation, '+
- 'this is an indication of numerical instability (use the EventLog to diagnose). '+CRLF+CRLF+
- 'If the maximum control iterations are exceeded, and no numerical instability is seen in the EventLog of via monitors, then try increasing the value of this parameter to reduce the number '+
- 'of control iterations needed to achieve the control criteria, and move to the power flow solution.';
-
- PropertyHelp[22] := '{Yes/True* | No/False} Default is YES for InvControl. Log control actions to Eventlog.';
-
- PropertyHelp[23] := 'Required for any mode that has VOLTVAR, DYNAMICREACCURR and WATTVAR. Defaults to VARAVAL.' +CRLF+CRLF+
- 'Defines the base reactive power for both the provided and absorbed reactive power, according to one of the following options: '
- +CRLF+CRLF+ 'VARAVAL. The base values for the provided and absorbed reactive power are equal to the available reactive power.'
- +CRLF+CRLF+ 'VARMAX: The base values of the provided and absorbed reactive power are equal to the value defined in the kvarMax and kvarMaxAbs properties, respectively.';
-
- PropertyHelp[24] := 'Required for VOLTWATT. Default is 0.01'+CRLF+CRLF+
- 'Tolerance in pu of the convergence of the control loop associated with active power. ' +
- 'For the same control iteration, this value is compared to the difference between the active power limit in pu resulted from the convergence process and the one resulted from the volt-watt function.'+CRLF+CRLF+
-
- 'This reactive power tolerance value plus the voltage tolerance value (VoltageChangeTolerance) determine, together, when to stop control iterations by the InvControl. '+CRLF+CRLF+
-
- 'If an InvControl is controlling more than one PVSystem/Storage, each PVSystem/Storage has this quantity calculated independently, and so an individual '+
- 'PVSystem/Storage may reach the tolerance within different numbers of control iterations.';
-
- PropertyHelp[25] := 'Number of the phase being monitored or one of {AVG | MAX | MIN} for all phases. Default=AVG. ';
-
- PropertyHelp[26] := 'Name of monitored bus used by the voltage-dependente control modes. Default is bus of the controlled PVSystem/Storage or Storage.' ;
-
- PropertyHelp[27] := 'Array list of rated voltages of the buses and their nodes presented in the monBus property. This list may have different line-to-line and/or line-to-ground voltages.' ;
-
- PropertyHelp[28] := 'Required for VOLTWATT mode for Storage element in CHARGING state. '+CRLF+CRLF+
- 'The name of an XYCurve object that describes the variation in active power output (in per unit of maximum active power outut for the Storage). '+CRLF+CRLF+
- 'Units for the x-axis are per-unit voltage, which may be in per unit of the rated voltage for the Storage, or may be in per unit of the average voltage at the terminals over a user-defined number of prior solutions. '+CRLF+CRLF+
- 'Units for the y-axis are either in: (1) per unit of maximum active power output capability of the Storage, or (2) maximum available active power output capability (defined by the parameter: VoltwattYAxis), '+
- 'corresponding to the terminal voltage (x-axis value in per unit). '+CRLF+CRLF+
- 'No default -- must be specified for VOLTWATT mode for Storage element in CHARGING state.';
-
- PropertyHelp[29] := 'Required for WATTPF mode.' +CRLF+CRLF+
- 'Name of the XYCurve object containing the watt-pf curve.' +CRLF+
- 'The positive values of the y-axis are positive power factor values. ' +
- 'The negative values of the the y-axis are negative power factor values. ' +
- 'When positive, the output reactive power has the same direction of the output active power, and when negative, it has the opposite direction.' +CRLF+
- 'Units for the x-axis are per-unit output active power, and the base active power is the Pmpp for PVSystem and kWrated for Storage.'+CRLF+CRLF+
- 'The y-axis represents the power factor and the reference is power factor equal to 0. '+CRLF+CRLF+
-
- 'For example, if the user wants to define the following XY coordinates: (0, 0.9); (0.2, 0.9); (0.5, -0.9); (1, -0.9).'+CRLF+
- 'Try to plot them considering the y-axis reference equal to unity power factor.' +CRLF+CRLF+
- 'The user needs to translate this curve into a plot in which the y-axis reference is equal to 0 power factor.' +
- 'It means that two new XY coordinates need to be included, in this case they are: (0.35, 1); (0.35, -1).'+CRLF+
- 'Try to plot them considering the y-axis reference equal to 0 power factor.' +CRLF+
- 'The discontinity in 0.35pu is not a problem since var is zero for either power factor equal to 1 or -1.';
-
- PropertyHelp[30] := 'Required for WATTVAR mode. '+CRLF+CRLF+
- 'Name of the XYCurve object containing the watt-var curve. The positive values of the y-axis of the watt-var curve represent values in pu of the provided base reactive power. ' +
- 'The negative values of the y-axis are values in pu of the absorbed base reactive power. ' +CRLF+
- 'Provided and absorbed base reactive power values are defined in the RefReactivePower property.' +CRLF+CRLF+
- 'Units for the x-axis are per-unit output active power, and the base active power is the Pmpp for PVSystem and kWrated for Storage.';
- PropertyHelp[31] := 'Deprecated, use RefReactivePower instead.';
- PropertyHelp[32] := 'Deprecated, use DERList instead.';
- PropertyHelp[33] := 'Required for Active Voltage Regulation (AVR).';
-
-
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- end;
-
-function TInvControl2.NewObject(const ObjName:AnsiString):Integer;
- begin
- // Make a new InvControl and add it to InvControl class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TInvControl2Obj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
- end;
-
-function TInvControl2.Edit():Integer;
- VAR
- CharPos,
- ParamPointer,
- i,
- j,
- NNode : Integer;
-
- StrTemp,
- ParamName,
- Param : AnsiString;
-
- NodeBuffer : Array[1..10] of Integer;
-
- begin
-
- // continue parsing with contents of Parser
- DSS.ActiveInvControl2Obj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveInvControl2Obj;
-
- Result := 0;
-
- with DSS.ActiveInvControl2Obj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
-
- WHILE Length(Param)>0 do
- begin
- if Length(ParamName) = 0 then Inc(ParamPointer)
- else ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer>0) and (ParamPointer<=NumProperties) then PropertyValue[ParamPointer]:= Param;
-
- CASE ParamPointer OF
- 0: DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name +'.'+ Name + '"', 364);
- 1: InterpretTStringListArray(Param, FDERNameList); // Read list of PVSystem and Storage objects in OpenDSS format and add to FDERNameList StringList.
- 2: begin
- if CompareTextShortest(Parser.StrValue, 'voltvar')= 0 then
- begin
- ControlMode := VOLTVAR;
- CombiControlMode := NONE_COMBMODE;
- end
- else if CompareTextShortest(Parser.StrValue, 'voltwatt')= 0 then
- begin
- ControlMode := VOLTWATT;
- CombiControlMode := NONE_COMBMODE;
- end
- else if CompareTextShortest(Parser.StrValue, 'dynamicreaccurr')= 0 then
- begin
- ControlMode := DRC;
- CombiControlMode := NONE_COMBMODE;
- end
-// else if CompareTextShortest(Parser.StrValue, 'fixedpf')= 0 then // (PR) what is this?
-// begin
-// ControlMode := 'FIXEDPF';
-// CombiControlMode := '';
-// end
- else if CompareTextShortest(Parser.StrValue, 'wattpf')= 0 then
- begin
- ControlMode := WATTPF;
- CombiControlMode := NONE_COMBMODE;
- end
- else if CompareTextShortest(Parser.StrValue, 'wattvar')= 0 then
- begin
- ControlMode := WATTVAR;
- CombiControlMode := NONE_COMBMODE;
- end
- else if CompareTextShortest(Parser.StrValue, 'avr')= 0 then
- begin
- ControlMode := AVR;
- CombiControlMode := NONE_COMBMODE;
- end
- else
- begin
- if ControlMode = NONE_MODE then DoSimpleMsg('Invalid Control Mode selected', 1366);
- CombiControlMode := NONE_COMBMODE;
- DSS.SolutionAbort := True;
- exit;
- end;
- end;
-
- 3: begin
- if CompareTextShortest(Parser.StrValue, 'vv_vw')= 0 then
- begin
- ControlMode := NONE_MODE;
- CombiControlMode := VV_VW;
- end
- else if CompareTextShortest(Parser.StrValue, 'vv_drc')= 0 then
- begin
- ControlMode := NONE_MODE;
- CombiControlMode := VV_DRC;
- end
- else
- begin
- if CombiControlMode = NONE_COMBMODE then DoSimpleMsg('Invalid CombiControl Mode selected', 1367);
- CombiControlMode := NONE_COMBMODE;
- DSS.SolutionAbort := True;
- exit;
- end;
- end;
-
- 4: begin
- Fvvc_curvename := Parser.StrValue;
- if Length(Fvvc_curvename) > 0 then
- begin
- Fvvc_curve := GetXYCurve(Fvvc_curvename, VOLTVAR);
- Fvvc_curve_size := Fvvc_curve.NumPoints;
- end;
- end;
-
- 5: begin
- if(Parser.DblValue > 0.0) then
- DoSimpleMsg('Hysteresis offset should be a negative value, or 0 "' + ParamName + '" for Object "' + Class_Name +'.'+ Name + '"', 1364)
- else
- Fvvc_curveOffset := Parser.DblValue;
- end;
-
- 6: begin
- if CompareTextShortest(Parser.StrValue, 'rated') = 0 then FVoltage_CurveX_ref := 0
- else if CompareTextShortest(Parser.StrValue, 'avg')= 0 then FVoltage_CurveX_ref := 1
- else if CompareTextShortest(Parser.StrValue, 'ravg')= 0 then FVoltage_CurveX_ref := 2
- end;
-
- 7: FRollAvgWindowLength := InterpretAvgVWindowLen(Param);
-
- 8: begin
- Fvoltwatt_curvename := Parser.StrValue;
- if Length(Fvoltwatt_curvename) > 0 then
- begin
- Fvoltwatt_curve := GetXYCurve(Fvoltwatt_curvename, VOLTWATT);
- Fvoltwatt_curve_size := Fvoltwatt_curve.NumPoints;
- end;
- end;
-
- 9: begin
- FDbVMin := Parser.DblValue;
- if(FDbVMax > 0.0) and (FDbVmin > FDbVMax) then
- begin
- DoSimpleMsg('Minimum dead-band voltage value should be less than the maximum dead-band voltage value. Value set to 0.0 "' + ParamName + '" for Object "' + Class_Name +'.'+ Name + '"', 1365);
- FDbvMin := 0.0;
- end;
- end;
-
- 10: begin
- FDbVMax := Parser.DblValue;
- if(FDbVMin > 0.0) and (FDbVMax < FDbVmin) then
- begin
- DoSimpleMsg('Maximum dead-band voltage value should be greater than the minimum dead-band voltage value. Value set to 0.0 "' + ParamName + '" for Object "' + Class_Name +'.'+ Name + '"', 1366);
- FDbvMax := 0.0;
- end;
- end;
-
- 11: FArGraLowV := Parser.DblValue;
- 12: FArGraHiV := Parser.DblValue;
- 13: FDRCRollAvgWindowLength := InterpretDRCAvgVWindowLen(Param);
- 14: FdeltaQ_factor := Parser.DblValue;
- 15: FVoltageChangeTolerance := Parser.DblValue;
- 16: FVarChangeTolerance := Parser.DblValue;
-
- 17: begin
- if CompareTextShortest(Parser.StrValue, 'pavailablepu')= 0 then FVoltwattYAxis := 0
- else if CompareTextShortest(Parser.StrValue, 'pmpppu')= 0 then FVoltwattYAxis := 1
- else if CompareTextShortest(Parser.StrValue, 'pctpmpppu')= 0 then FVoltwattYAxis := 2
- else if CompareTextShortest(Parser.StrValue, 'kvaratingpu')= 0 then FVoltwattYAxis := 3
- end;
-
- 18: begin
- if CompareTextShortest(Parser.StrValue, 'inactive')= 0 then RateofChangeMode := INACTIVE
- else if CompareTextShortest(Parser.StrValue, 'lpf')= 0 then RateofChangeMode := LPF
- else if CompareTextShortest(Parser.StrValue, 'risefall')= 0 then RateofChangeMode := RISEFALL
- end;
-
- 19: begin
- if Parser.DblValue > 0 then FLPFTau := Parser.DblValue
- else RateofChangeMode := INACTIVE;
- end;
-
- 20: begin
- if Parser.DblValue > 0 then FRiseFallLimit := Parser.DblValue
- else RateofChangeMode := INACTIVE;
- end;
-
- 21: FdeltaP_factor := Parser.DblValue;
- 22: ShowEventLog := InterpretYesNo(param);
-
- 23: begin
- if CompareTextShortest(Parser.StrValue, 'varaval')= 0 then FReacPower_ref := 'VARAVAL'
- else if CompareTextShortest(Parser.StrValue, 'varmax')= 0 then FReacPower_ref := 'VARMAX'
- end;
-
- 24: FActivePChangeTolerance := Parser.DblValue;
-
- 25: begin
- if CompareTextShortest(param, 'avg') = 0 then FMonBusesPhase := AVGPHASES
- else if CompareTextShortest(param, 'max') = 0 then FMonBusesPhase := MAXPHASE
- else if CompareTextShortest(param, 'min') = 0 then FMonBusesPhase := MINPHASE
- else FMonBusesPhase := max(1, Parser.IntValue);
- end;
-
- 26: begin //FMonBuses := Param;
-
- InterpretTStringListArray(Param, FMonBusesNameList);
- SetLength(FMonBuses, FMonBusesNameList.Count);
- SetLength(FMonBusesNodes, FMonBusesNameList.Count);
-
- AuxParser.CmdString := Param; //Parser.StrValue; // load AuxParser
-
- for i := 0 to FMonBusesNameList.Count-1 do begin
- AuxParser.NextParam; // Gets the next token
-
- FMonBuses[i] := AuxParser.ParseAsBusName(NNode, pIntegerArray(@NodeBuffer));
- SetLength(FMonBusesNodes[i], NNode);
-
- for j := 0 to NNode-1 do FMonBusesNodes[i,j] := NodeBuffer[j+1];
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // object references
+ PropertyType[ord(TProp.vvc_curve1)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.vvc_curve1)] := ptruint(@obj.Fvvc_curve);
+ PropertyOffset2[ord(TProp.vvc_curve1)] := ptruint(DSS.XYCurveClass);
+
+ PropertyType[ord(TProp.voltwatt_curve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.voltwatt_curve)] := ptruint(@obj.Fvoltwatt_curve);
+ PropertyOffset2[ord(TProp.voltwatt_curve)] := ptruint(DSS.XYCurveClass);
+
+ PropertyType[ord(TProp.voltwattCH_curve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.voltwattCH_curve)] := ptruint(@obj.FvoltwattCH_curve);
+ PropertyOffset2[ord(TProp.voltwattCH_curve)] := ptruint(DSS.XYCurveClass);
+
+ PropertyType[ord(TProp.wattpf_curve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.wattpf_curve)] := ptruint(@obj.Fwattpf_curve);
+ PropertyOffset2[ord(TProp.wattpf_curve)] := ptruint(DSS.XYCurveClass);
+
+ PropertyType[ord(TProp.wattvar_curve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.wattvar_curve)] := ptruint(@obj.Fwattvar_curve);
+ PropertyOffset2[ord(TProp.wattvar_curve)] := ptruint(DSS.XYCurveClass);
+
+ // enum properties
+ PropertyType[ord(TProp.Mode)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.Mode)] := ptruint(@obj.ControlMode);
+ PropertyOffset2[ord(TProp.Mode)] := PtrInt(ModeEnum);
+
+ PropertyType[ord(TProp.CombiMode)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.CombiMode)] := ptruint(@obj.CombiMode);
+ PropertyOffset2[ord(TProp.CombiMode)] := PtrInt(CombiModeEnum);
+
+ PropertyType[ord(TProp.voltage_curvex_ref)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.voltage_curvex_ref)] := ptruint(@obj.FVoltage_CurveX_ref);
+ PropertyOffset2[ord(TProp.voltage_curvex_ref)] := PtrInt(VoltageCurveXRefEnum);
+
+ PropertyType[ord(TProp.VoltwattYAxis)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.VoltwattYAxis)] := ptruint(@obj.FVoltwattYAxis);
+ PropertyOffset2[ord(TProp.VoltwattYAxis)] := PtrInt(VoltWattYAxisEnum);
+
+ PropertyType[ord(TProp.RateofChangeMode)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.RateofChangeMode)] := ptruint(@obj.RateofChangeMode);
+ PropertyOffset2[ord(TProp.RateofChangeMode)] := PtrInt(RoCEnum);
+
+ PropertyType[ord(TProp.RefReactivePower)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.RefReactivePower)] := ptruint(@obj.FReacPower_ref);
+ PropertyOffset2[ord(TProp.RefReactivePower)] := PtrInt(RefQEnum);
+
+ PropertyType[ord(TProp.monVoltageCalc)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.monVoltageCalc)] := ptruint(@obj.FMonBusesPhase);
+ PropertyOffset2[ord(TProp.monVoltageCalc)] := PtrInt(DSS.MonPhaseEnum);
+
+ // boolean properties
+ PropertyType[ord(TProp.EventLog)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.EventLog)] := ptruint(@obj.ShowEventLog);
+
+ // string lists
+ PropertyType[ord(TProp.DERList)] := TPropertyType.StringListProperty;
+ PropertyType[ord(TProp.monBus)] := TPropertyType.StringListProperty;
+ PropertyType[ord(TProp.PVSystemList)] := TPropertyType.StringListProperty;
+ PropertyOffset[ord(TProp.DERList)] := ptruint(@obj.DERNameList);
+ PropertyOffset[ord(TProp.monBus)] := ptruint(@obj.MonBusesNameList);
+ PropertyOffset[ord(TProp.PVSystemList)] := ptruint(@obj.DERNameList);
+ PropertyFlags[ord(TProp.PVSystemList)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.PVSystemList)] := ord(TProp.DERList);
+
+ // array of doubles
+ PropertyType[ord(TProp.MonBusesVbase)] := TPropertyType.DoubleVArrayProperty;
+ PropertyOffset[ord(TProp.MonBusesVbase)] := ptruint(@obj.FMonBusesVbase);
+ PropertyOffset3[ord(TProp.MonBusesVbase)] := ptruint(@GetMonBusesCount);
+ PropertyFlags[ord(TProp.MonBusesVbase)] := [TPropertyFlag.SizeIsFunction]; // MonBusesNameList.Count
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.DbVMin)] := ptruint(@obj.FDbVMin);
+ PropertyOffset[ord(TProp.DbVMax)] := ptruint(@obj.FDbVMax);
+ PropertyOffset[ord(TProp.ArGraLowV)] := ptruint(@obj.FArGraLowV);
+ PropertyOffset[ord(TProp.ArGraHiV)] := ptruint(@obj.FArGraHiV);
+ PropertyOffset[ord(TProp.deltaQ_Factor)] := ptruint(@obj.FdeltaQ_factor);
+ PropertyOffset[ord(TProp.VoltageChangeTolerance)] := ptruint(@obj.FVoltageChangeTolerance);
+ PropertyOffset[ord(TProp.VarChangeTolerance)] := ptruint(@obj.FVarChangeTolerance);
+ PropertyOffset[ord(TProp.deltaP_Factor)] := ptruint(@obj.FdeltaP_factor);
+ PropertyOffset[ord(TProp.ActivePChangeTolerance)] := ptruint(@obj.FActivePChangeTolerance);
+ PropertyOffset[ord(TProp.Vsetpoint)] := ptruint(@obj.Fv_setpoint);
+ PropertyOffset[ord(TProp.LPFTau)] := ptruint(@obj.LPFTau);
+ PropertyOffset[ord(TProp.RiseFallLimit)] := ptruint(@obj.FRiseFallLimit);
+
+ // advanced doubles
+ PropertyOffset[ord(TProp.hysteresis_offset)] := ptruint(@obj.Fvvc_curveOffset);
+ PropertyFlags[ord(TProp.hysteresis_offset)] := [TPropertyFlag.NonPositive];
+
+ // integer
+ PropertyType[ord(TProp.DynReacavgwindowlen)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.DynReacavgwindowlen)] := ptruint(@obj.FDRCRollAvgWindowLength);
+ PropertyFlags[ord(TProp.DynReacavgwindowlen)] := [TPropertyFlag.IntervalUnits];
+
+ PropertyType[ord(TProp.avgwindowlen)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.avgwindowlen)] := ptruint(@obj.FRollAvgWindowLength);
+ PropertyFlags[ord(TProp.avgwindowlen)] := [TPropertyFlag.IntervalUnits];
+
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
+end;
- end;
+function TInvControl2.NewObject(const ObjName: Ansistring; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
+begin
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
+end;
- end;
-
- 27: begin
- ReAllocmem(FMonBusesVbase, Sizeof(FMonBusesVbase^[1])*FMonBusesNameList.Count);
- Parser.ParseAsVector(FMonBusesNameList.Count, FMonBusesVbase);
- end;
-
- 28: begin
- FvoltwattCH_curvename := Parser.StrValue;
- if Length(FvoltwattCH_curvename) > 0 then
- begin
- FvoltwattCH_curve := GetXYCurve(FvoltwattCH_curvename, VOLTWATT);
- FvoltwattCH_curve_size := FvoltwattCH_curve.NumPoints;
- end;
- end;
-
- 29: begin
- Fwattpf_curvename := Parser.StrValue;
- if Length(Fwattpf_curvename) > 0 then
- begin
- Fwattpf_curve := GetXYCurve(Fwattpf_curvename, WATTPF);
- Fwattpf_curve_size := Fwattpf_curve.NumPoints;
- end;
- end;
+procedure ValidateXYCurve(dss: TDSSContext; var curve: TXYcurveObj; InvControl2Mode: TInvControl2ControlMode);
+var
+ i: Integer;
+begin
+ if curve = NIL then
+ Exit;
- 30: begin
- Fwattvar_curvename := Parser.StrValue;
- if Length(Fwattvar_curvename) > 0 then
- begin
- Fwattvar_curve := GetXYCurve(Fwattvar_curvename, WATTVAR);
- Fwattvar_curve_size := Fwattvar_curve.NumPoints;
- end;
- end;
-
- 31: begin
- StrTemp := Parser.StrValue;
- Charpos := ansipos('_', StrTemp);
- if CharPos <> 0 then
- StrTemp := StrTemp.Substring(0,CharPos - 1);
-
- if CompareTextShortest(StrTemp, 'varaval')= 0 then FReacPower_ref := 'VARAVAL'
- else if CompareTextShortest(StrTemp, 'varmax')= 0 then FReacPower_ref := 'VARMAX'
- end;
-
- 32: begin
- InterpretTStringListArray(Param, FDERNameList); // Read list of PVSystem and Storage objects in OpenDSS format and add to FDERNameList StringList.
- // Because is using this command from the previous version of InvControl, we assume that the list includes only
- // PVSystems, so the list is updated
- for CharPos := 0 to (FDERNameList.Count - 1) do
- FDERNameList[CharPos] := 'PVSystem.' + FDERNameList[CharPos];
- end;
- 33:
- Fv_setpoint := Parser.DblValue;
- else
- // Inherited parameters
- ClassEdit( DSS.ActiveInvControl2Obj, ParamPointer - NumPropsthisClass)
+ // if VOLTWATT control mode then check for any negative watt values (pu)
+ // and values greater than 1.0 per-unit (=100 percent output)
+ if InvControl2Mode = VOLTWATT then
+ begin
+ for i := 1 to curve.NumPoints do
+ begin
+ if (curve.YValue_pt[i] < 0.0) or (curve.YValue_pt[i] > 1.0) then
+ begin
+ DoSimpleMsg(dss, 'XY Curve object: "%s" has active power value(s) greater than 1.0 per-unit or less than -1.0 per-unit. Not allowed for VOLTWATT control mode for PVSystem/Storages', [Curve.Name], 381);
+ curve := NIL;
+ Break;
end;
+ end;
+ end;
- CASE ParamPointer OF
- 1: begin // re-alloc based on
- FDERPointerList.Clear;
- FListSize := FDERNameList.count;
- end;
- else
-
+ // if WATTPF control mode then check for any negative pf values
+ // and values greater than 1.0
+ if InvControl2Mode = WATTPF then
+ begin
+ for i := 1 to curve.NumPoints do
+ begin
+ if (curve.YValue_pt[i] < -1.0) or (curve.YValue_pt[i] > 1.0) then
+ begin
+ DoSimpleMsg(dss, 'XY Curve object: "%s" has power factor value(s) greater than 1.0 or less than -1.0. Not allowed for WATTPF control mode for PVSystem/Storages', [Curve.Name], 381);
+ curve := NIL;
+ Break;
end;
+ end;
+ end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
- RecalcElementData();
- end;
-
+ // if WATTVAR control mode then check for any negative pf values
+ // and values greater than 1.0
+ if InvControl2Mode = WATTVAR then
+ begin
+ for i := 1 to curve.NumPoints do
+ begin
+ if (curve.YValue_pt[i] < -1.0) or (curve.YValue_pt[i] > 1.0) then
+ begin
+ DoSimpleMsg(dss, 'XY Curve object: "%s" has reactive power value(s) greater than 1.0 per-unit or less than -1.0 per-unit. Not allowed for WATTVAR control mode for PVSystem/Storages', [Curve.Name], 381);
+ curve := NIL;
+ Break;
+ end;
+ end;
+ end;
end;
-function TInvControl2.MakeLike(const InvControl2Name:AnsiString):Integer;
- VAR
- OtherInvControl : TInvControl2Obj;
- i, j : Integer;
-
- begin
- Result := 0;
- {See if we can find this InvControl name in the present collection}
- OtherInvControl := Find(InvControl2Name);
-
- if OtherInvControl<>Nil then
-
- with DSS.ActiveInvControl2Obj do begin
-
- NPhases := OtherInvControl.Fnphases;
- NConds := OtherInvControl.Fnconds; // Force Reallocation of terminal stuff
-
- for i := 1 to FDERPointerList.Count do
+procedure TInvControl2Obj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
+var
+ CharPos: Integer;
+ NodeBuffer: array[1..10] of Integer;
+ i,
+ j,
+ NNode: Integer;
+begin
+ case Idx of
+ ord(TProp.DERList):
+ begin // re-alloc based on
+ FDERPointerList.Clear;
+ FListSize := DERNameList.count;
+ end;
+ ord(TProp.Mode):
+ CombiMode := NONE_COMBMODE;
+ ord(TProp.DbVMin):
+ if (FDbVMax > 0.0) and (FDbVmin > FDbVMax) then
+ begin
+ DoSimpleMsg('Minimum dead-band voltage value should be less than the maximum dead-band voltage value. Value set to 0.0 "%s" for object "%s"', [ParentClass.PropertyName[Idx], FullName], 1365);
+ FDbvMin := 0.0;
+ end;
+ ord(TProp.DbVMax):
+ if (FDbVMin > 0.0) and (FDbVMax < FDbVmin) then
+ begin
+ DoSimpleMsg('Maximum dead-band voltage value should be greater than the minimum dead-band voltage value. Value set to 0.0 "%s" for Object "%s"', [ParentClass.PropertyName[Idx], FullName], 1366);
+ FDbvMax := 0.0;
+ end;
+ ord(TProp.LPFTau):
+ if LPFTau <= 0 then
+ RateofChangeMode := INACTIVE;
+ ord(TProp.RiseFallLimit):
+ if FRiseFallLimit <= 0 then
+ RateofChangeMode := INACTIVE;
+ ord(TProp.monBus):
+ begin //FMonBuses := Param;
+ SetLength(FMonBuses, MonBusesNameList.Count);
+ SetLength(FMonBusesNodes, MonBusesNameList.Count);
+ for i := 0 to MonBusesNameList.Count - 1 do
+ begin
+ FMonBuses[i] := DSS.AuxParser.ParseAsBusName(MonBusesNameList.Strings[i], NNode, pIntegerArray(@NodeBuffer));
+ SetLength(FMonBusesNodes[i], NNode);
+ for j := 0 to NNode - 1 do
+ FMonBusesNodes[i, j] := NodeBuffer[j + 1];
+ end;
+ end;
+ ord(TProp.PVSystemList):
begin
+ // Because is using this command from the previous version of InvControl, we assume that the list includes only
+ // PVSystems, so the list is updated
+ for CharPos := 0 to (DERNameList.Count - 1) do
+ DERNameList[CharPos] := 'PVSystem.' + DERNameList[CharPos];
+ end;
+ ord(TProp.vvc_curve1):
+ ValidateXYCurve(DSS, Fvvc_curve, VOLTVAR);
+ ord(TProp.voltwatt_curve):
+ ValidateXYCurve(DSS, Fvoltwatt_curve, VOLTWATT);
+ ord(TProp.voltwattCH_curve):
+ ValidateXYCurve(DSS, FvoltwattCH_curve, VOLTWATT);
+ ord(TProp.wattpf_curve):
+ ValidateXYCurve(DSS, Fwattpf_curve, WATTPF);
+ ord(TProp.wattvar_curve):
+ ValidateXYCurve(DSS, Fwattvar_curve, WATTVAR);
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
- ControlledElement[i] := OtherInvControl.ControlledElement[i];
- CondOffset[i] := OtherInvControl.CondOffset[i];
-
- FVBase[i] := OtherInvControl.FVBase[i];
- FVarFollowInverter[i] := OtherInvControl.FVarFollowInverter[i];
- FInverterON[i] := OtherInvControl.FInverterON[i];
- FpresentkW[i] := OtherInvControl.FpresentkW[i];
- FkVARating[i] := OtherInvControl.FkVARating[i];
- Fpresentkvar[i] := OtherInvControl.Fpresentkvar[i];
- FkvarLimit[i] := OtherInvControl.FkvarLimit[i];
- FkvarLimitNeg[i] := OtherInvControl.FkvarLimitNeg[i];
- FCurrentkvarLimit[i] := OtherInvControl.FCurrentkvarLimit[i];
- FCurrentkvarLimitNeg[i] := OtherInvControl.FCurrentkvarLimitNeg[i];
- FDCkWRated[i] := OtherInvControl.FDCkWRated[i];
- FpctDCkWRated[i] := OtherInvControl.FpctDCkWRated[i];
- FEffFactor[i] := OtherInvControl.FEffFactor[i];
- FDCkW[i] := OtherInvControl.FDCkW[i];
- FPPriority[i] := OtherInvControl.FPPriority[i];
- end;
-
- ControlMode := OtherInvControl.ControlMode;
- CombiControlMode := OtherInvControl.CombiControlMode;
- FListSize := OtherInvControl.FListSize;
- Fvvc_curve_size := OtherInvControl.Fvvc_curve_size;
- Fvvc_curve := OtherInvControl.Fvvc_curve;
- Fvvc_curvename := OtherInvControl.Fvvc_curvename;
- Fvvc_curveOffset := OtherInvControl.Fvvc_curveOffset;
- FVoltage_CurveX_ref := OtherInvControl.FVoltage_CurveX_ref;
- FDRCVAvgWindowLengthSec := OtherInvControl.FDRCVAvgWindowLengthSec;
- FVAvgWindowLengthSec := OtherInvControl.FVAvgWindowLengthSec;
- Fvoltwatt_curve_size := OtherInvControl.Fvoltwatt_curve_size;
- Fvoltwatt_curve := OtherInvControl.Fvoltwatt_curve;
- Fvoltwatt_curvename := OtherInvControl.Fvoltwatt_curvename;
- FvoltwattCH_curve_size := OtherInvControl.FvoltwattCH_curve_size;
- FvoltwattCH_curve := OtherInvControl.FvoltwattCH_curve;
- FvoltwattCH_curvename := OtherInvControl.FvoltwattCH_curvename;
- Fwattpf_curve_size := OtherInvControl.Fwattpf_curve_size;
- Fwattpf_curve := OtherInvControl.Fwattpf_curve;
- Fwattpf_curvename := OtherInvControl.Fwattpf_curvename;
- Fwattvar_curve_size := OtherInvControl.Fwattvar_curve_size;
- Fwattvar_curve := OtherInvControl.Fwattvar_curve;
- Fwattvar_curvename := OtherInvControl.Fwattvar_curvename;
- FDbVMin := OtherInvControl.FDbVMin;
- pf_wp_nominal := OtherInvControl.pf_wp_nominal;
- FDbVMax := OtherInvControl.FDbVMax;
- FArGraLowV := OtherInvControl.FArGraLowV;
- FArGraHiV := OtherInvControl.FArGraHiV;
- FActiveVVCurve := OtherInvControl.FActiveVVCurve;
- FRollAvgWindowLength := OtherInvControl.FRollAvgWindowLength;
- FRollAvgWindowLengthIntervalUnit := OtherInvControl.FRollAvgWindowLengthIntervalUnit;
- FDRCRollAvgWindowLength := OtherInvControl.FDRCRollAvgWindowLength;
- FDRCRollAvgWindowLengthIntervalUnit := OtherInvControl.FDRCRollAvgWindowLengthIntervalUnit;
- FActivePChangeTolerance := OtherInvControl.FActivePChangeTolerance;
- FdeltaQ_factor := OtherInvControl.FdeltaQ_factor;
- FdeltaP_factor := OtherInvControl.FdeltaP_factor;
- FVoltageChangeTolerance := OtherInvControl.FVoltageChangeTolerance;
- FVarChangeTolerance := OtherInvControl.FVarChangeTolerance;
- FVoltwattYAxis := OtherInvControl.FVoltwattYAxis;
- RateofChangeMode := OtherInvControl.RateofChangeMode;
- FLPFTau := OtherInvControl.FLPFTau;
- FRiseFallLimit := OtherInvControl.FRiseFallLimit;
- FMonBusesPhase := OtherInvControl.FMonBusesPhase;
- FMonBuses := OtherInvControl.FMonBuses;
- FMonBusesNodes := OtherInvControl.FMonBusesNodes;
-
- ReallocMem(FMonBusesVbase, SizeOf(FMonBusesVbase^[1])*FMonBusesNameList.Count);
- for j := 1 to FMonBusesNameList.Count do FMonBusesVbase^[j] := OtherInvControl.FMonBusesVbase^[j];
-
- TimeDelay := OtherInvControl.TimeDelay;
- for j := 1 to ParentClass.NumProperties do PropertyValue[j] := OtherInvControl.PropertyValue[j];
+procedure TInvControl2Obj.MakeLike(OtherPtr: Pointer);
+var
+ Other: TObj;
+ i, j: Integer;
+begin
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
- end
- else DoSimpleMsg('Error in InvControl MakeLike: "' + InvControl2Name + '" Not Found.', 370);
+ for i := 1 to FDERPointerList.Count do
+ begin
+ ControlledElement[i] := Other.ControlledElement[i];
+ CondOffset[i] := Other.CondOffset[i];
+
+ FVBase[i] := Other.FVBase[i];
+ FVarFollowInverter[i] := Other.FVarFollowInverter[i];
+ FInverterON[i] := Other.FInverterON[i];
+ FpresentkW[i] := Other.FpresentkW[i];
+ FkVARating[i] := Other.FkVARating[i];
+ Fpresentkvar[i] := Other.Fpresentkvar[i];
+ FkvarLimit[i] := Other.FkvarLimit[i];
+ FkvarLimitNeg[i] := Other.FkvarLimitNeg[i];
+ FCurrentkvarLimit[i] := Other.FCurrentkvarLimit[i];
+ FCurrentkvarLimitNeg[i] := Other.FCurrentkvarLimitNeg[i];
+ FDCkWRated[i] := Other.FDCkWRated[i];
+ FpctDCkWRated[i] := Other.FpctDCkWRated[i];
+ FEffFactor[i] := Other.FEffFactor[i];
+ FDCkW[i] := Other.FDCkW[i];
+ FPPriority[i] := Other.FPPriority[i];
+ end;
+ ControlMode := Other.ControlMode;
+ CombiMode := Other.CombiMode;
+ FListSize := Other.FListSize;
+ Fvvc_curve := Other.Fvvc_curve;
+ Fvvc_curveOffset := Other.Fvvc_curveOffset;
+ FVoltage_CurveX_ref := Other.FVoltage_CurveX_ref;
+ Fvoltwatt_curve := Other.Fvoltwatt_curve;
+ FvoltwattCH_curve := Other.FvoltwattCH_curve;
+ Fwattpf_curve := Other.Fwattpf_curve;
+ Fwattvar_curve := Other.Fwattvar_curve;
+ FDbVMin := Other.FDbVMin;
+ pf_wp_nominal := Other.pf_wp_nominal;
+ FDbVMax := Other.FDbVMax;
+ FArGraLowV := Other.FArGraLowV;
+ FArGraHiV := Other.FArGraHiV;
+ FActiveVVCurve := Other.FActiveVVCurve;
+ FRollAvgWindowLength := Other.FRollAvgWindowLength;
+ FDRCRollAvgWindowLength := Other.FDRCRollAvgWindowLength;
+ FActivePChangeTolerance := Other.FActivePChangeTolerance;
+ FdeltaQ_factor := Other.FdeltaQ_factor;
+ FdeltaP_factor := Other.FdeltaP_factor;
+ FVoltageChangeTolerance := Other.FVoltageChangeTolerance;
+ FVarChangeTolerance := Other.FVarChangeTolerance;
+ FVoltwattYAxis := Other.FVoltwattYAxis;
+ RateofChangeMode := Other.RateofChangeMode;
+ LPFTau := Other.LPFTau;
+ FRiseFallLimit := Other.FRiseFallLimit;
+ FMonBusesPhase := Other.FMonBusesPhase;
+ FMonBuses := Other.FMonBuses;
+ FMonBusesNodes := Other.FMonBusesNodes;
+
+ ReallocMem(FMonBusesVbase, SizeOf(FMonBusesVbase^[1]) * MonBusesNameList.Count);
+ for j := 1 to MonBusesNameList.Count do
+ FMonBusesVbase^[j] := Other.FMonBusesVbase^[j];
+
+ TimeDelay := Other.TimeDelay;
end;
-{==========================================================================}
-{ TInvControl2Obj }
-{==========================================================================}
-constructor TInvControl2Obj.Create(ParClass:TDSSClass; const InvControl2Name:AnsiString);
-
- begin
-
- Inherited Create(ParClass);
- Name := LowerCase(InvControl2Name);
- DSSObjType := ParClass.DSSClassType;
-
- ElementName := '';
-
- {
- Control elements are zero current sources that attach to a terminal of a
- power-carrying device, but do not alter voltage or current flow.
- Define a default number of phases and conductors here and update in
- RecalcElementData routine if necessary. This allocates arrays for voltages
- and currents and gives more direct access to the values,if needed
- }
- NPhases := 3; // Directly set conds and phases
- Fnconds := 3;
- Nterms := 1; // this forces allocation of terminals and conductors
- // in base class
- ControlMode := NONE_MODE;
- CombiControlMode := NONE_COMBMODE;
- ControlledElement := nil;
-
- {Variables for voltages}
- FAvgpVpuPrior := nil;
- FAvgpDRCVpuPrior := nil;
- FPresentVpu := nil;
- FPresentDRCVpu := nil;
- FVpuSolution := nil;
- FVpuSolutionIdx := 0;
-
-
- {Variables for convergence process}
- FdeltaQ_factor := FLAGDELTAQ;
- FdeltaP_factor := FLAGDELTAP;
-
- FdeltaQFactor := nil;
- FdeltaPFactor := nil;
- DeltaV_old := nil;
-
- FVoltageChangeTolerance := 0.0001;
- FVarChangeTolerance := 0.025;
- FActivePChangeTolerance := 0.01;
+constructor TInvControl2Obj.Create(ParClass: TDSSClass; const InvControl2Name: Ansistring);
+begin
+ inherited Create(ParClass);
+ Name := AnsiLowerCase(InvControl2Name);
+ DSSObjType := ParClass.DSSClassType;
+
+ // Control elements are zero current sources that attach to a terminal of a
+ // power-carrying device, but do not alter voltage or current flow.
+ // Define a default number of phases and conductors here and update in
+ // RecalcElementData routine if necessary. This allocates arrays for voltages
+ // and currents and gives more direct access to the values,if needed
+ FNPhases := 3; // Directly set conds and phases
+ Fnconds := 3;
+ Nterms := 1; // this forces allocation of terminals and conductors in base class
+ ControlMode := NONE_MODE;
+ CombiMode := NONE_COMBMODE;
+ ControlledElement := NIL;
+
+ // Variables for voltages
+ FAvgpVpuPrior := NIL;
+ FAvgpDRCVpuPrior := NIL;
+ FPresentVpu := NIL;
+ FPresentDRCVpu := NIL;
+ FVpuSolution := NIL;
+ FVpuSolutionIdx := 0;
+
+ // Variables for convergence process
+ FdeltaQ_factor := FLAGDELTAQ;
+ FdeltaP_factor := FLAGDELTAP;
+
+ FdeltaQFactor := NIL;
+ FdeltaPFactor := NIL;
+ DeltaV_old := NIL;
+
+ FVoltageChangeTolerance := 0.0001;
+ FVarChangeTolerance := 0.025;
+ FActivePChangeTolerance := 0.01;
// Active power
- PLimitVW := nil;
- POldVWpu := nil;
- FFlagVWOperates := nil;
- PLimitVWpu := nil;
- PLimitLimitedpu := nil;
- PLimitEndpu := nil;
- PLimitOptionpu := nil;
- kW_out_desiredpu := nil;
- kW_out_desired := nil;
+ PLimitVW := NIL;
+ POldVWpu := NIL;
+ FFlagVWOperates := NIL;
+ PLimitVWpu := NIL;
+ PLimitLimitedpu := NIL;
+ PLimitEndpu := NIL;
+ PLimitOptionpu := NIL;
+ kW_out_desiredpu := NIL;
+ kW_out_desired := NIL;
// Reactive power
- QDesireEndpu := nil;
- QDesireVVpu := nil;
- QDesireWPpu := nil;
- QDesireWVpu := nil;
- QDesireDRCpu := nil;
- QDesireAVRpu := nil;
- QDesireLimitedpu := nil;
- QDesireOptionpu := nil;
- QDesiredVV := nil;
- QDesiredWP := nil;
- QDesiredWV := nil;
- QOld := nil;
- QOldVV := nil;
- QOldAVR := nil;
- QOldDRC := nil;
- QOldVVDRC := nil;
- QDesiredDRC := nil;
- QDesiredVVDRC := nil;
- QDesiredAVR := nil;
-
- {Variables of functions that CONTROL reactive power}
- QHeadRoom := nil;
- QHeadRoomNeg := nil;
- Qoutputpu := nil;
- QoutputVVpu := nil;
- QoutputDRCpu := nil;
- QoutputVVDRCpu := nil;
- QoutputAVRpu := nil;
-
- FPriorvarspu := nil;
- FPriorvars := nil;
-
- {Variables of functions that LIMIT active power}
- PBase := nil;
-
- FPriorWattspu := nil;
- FPriorWatts := nil;
-
- {Variables of DER element}
- FDERNameList := nil;
- FDERPointerList := nil;
- FDERPointerList := TDSSPointerList.Create(20); // Default size and increment
- FDERNameList := TSTringList.Create;
- FVBase := nil;
- FVarFollowInverter := nil;
- FInverterON := nil;
- FpresentkW := nil;
- FkVARating := nil;
- Fpresentkvar := nil;
- FkvarLimit := nil;
- FkvarLimitNeg := nil;
- FCurrentkvarLimit := nil;
- FCurrentkvarLimitNeg := nil;
- FDCkWRated := nil;
- FpctDCkWRated := nil;
- FEffFactor := nil;
- FDCkW := nil;
- FPPriority := nil;
- NPhasesDER := nil;
- NCondsDER := nil;
-
- {Variables for monitored Bus/buses}
- FMonBusesNameList := nil;
- FMonBusesNameList := TStringList.Create;
- FMonBusesPhase := AVGPHASES;
- FMonBuses := nil;
- FMonBusesVbase := nil;
- FMonBusesNodes := nil;
-
- {Variables for LPF and RF options}
- RateofChangeMode := INACTIVE;
- FLPFTau := 0.001;
- FRiseFallLimit := 0.001;
- FPriorPLimitOptionpu := nil;
- FPriorQDesireOptionpu := nil;
-
- {Variables of the smart inverter functions}
- FVoltage_CurveX_ref := 0;
- FReacPower_ref := 'VARAVAL';
- FVoltwattYAxis := 1;
+ QDesireEndpu := NIL;
+ QDesireVVpu := NIL;
+ QDesireWPpu := NIL;
+ QDesireWVpu := NIL;
+ QDesireDRCpu := NIL;
+ QDesireAVRpu := NIL;
+ QDesireLimitedpu := NIL;
+ QDesireOptionpu := NIL;
+ QDesiredVV := NIL;
+ QDesiredWP := NIL;
+ QDesiredWV := NIL;
+ QOld := NIL;
+ QOldVV := NIL;
+ QOldAVR := NIL;
+ QOldDRC := NIL;
+ QOldVVDRC := NIL;
+ QDesiredDRC := NIL;
+ QDesiredVVDRC := NIL;
+ QDesiredAVR := NIL;
+
+ // Variables of functions that CONTROL reactive power
+ QHeadRoom := NIL;
+ QHeadRoomNeg := NIL;
+ Qoutputpu := NIL;
+ QoutputVVpu := NIL;
+ QoutputDRCpu := NIL;
+ QoutputVVDRCpu := NIL;
+ QoutputAVRpu := NIL;
+
+ FPriorvarspu := NIL;
+ FPriorvars := NIL;
+
+ // Variables of functions that LIMIT active power
+ PBase := NIL;
+
+ FPriorWattspu := NIL;
+ FPriorWatts := NIL;
+
+ // Variables of DER element
+ DERNameList := NIL;
+ FDERPointerList := NIL;
+ FDERPointerList := TDSSPointerList.Create(20); // Default size and increment
+ DERNameList := TSTringList.Create;
+ FVBase := NIL;
+ FVarFollowInverter := NIL;
+ FInverterON := NIL;
+ FpresentkW := NIL;
+ FkVARating := NIL;
+ Fpresentkvar := NIL;
+ FkvarLimit := NIL;
+ FkvarLimitNeg := NIL;
+ FCurrentkvarLimit := NIL;
+ FCurrentkvarLimitNeg := NIL;
+ FDCkWRated := NIL;
+ FpctDCkWRated := NIL;
+ FEffFactor := NIL;
+ FDCkW := NIL;
+ FPPriority := NIL;
+ NPhasesDER := NIL;
+ NCondsDER := NIL;
+
+ // Variables for monitored Bus/buses
+ MonBusesNameList := TStringList.Create;
+ FMonBusesPhase := AVGPHASES;
+ FMonBuses := NIL;
+ FMonBusesVbase := NIL;
+ FMonBusesNodes := NIL;
+
+ // Variables for LPF and RF options
+ RateofChangeMode := INACTIVE;
+ LPFTau := 0.001;
+ FRiseFallLimit := 0.001;
+ FPriorPLimitOptionpu := NIL;
+ FPriorQDesireOptionpu := NIL;
+
+ // Variables of the smart inverter functions
+ FVoltage_CurveX_ref := 0;
+ FReacPower_ref := ReacPower_VARAVAL;
+ FVoltwattYAxis := 1;
// volt-var
- Fvvc_curve_size := 0;
- Fvvc_curve := nil;
- Fvvc_curvename := '';
- Fvvc_curveOffset := 0.0;
- Fvvc_curve2 := nil;
- FActiveVVCurve := nil;
- FlagChangeCurve := nil;
- FVAvgWindowLengthSec := 1.0;
- FRollAvgWindow := nil;
- FRollAvgWindowLength := 1;
- FRollAvgWindowLengthIntervalUnit := 's';
- priorRollAvgWindow := nil;
+ Fvvc_curve := NIL;
+ Fvvc_curveOffset := 0.0;
+ FActiveVVCurve := NIL;
+ FlagChangeCurve := NIL;
+ FRollAvgWindow := NIL;
+ FRollAvgWindowLength := 1;
+ priorRollAvgWindow := NIL;
// watt-pf
- Fwattpf_curve_size := 0;
- Fwattpf_curve := nil;
- Fwattpf_curvename := '';
- pf_wp_nominal := 0.0;
+ Fwattpf_curve := NIL;
+ pf_wp_nominal := 0.0;
// watt-var
- Fwattvar_curve_size := 0;
- Fwattvar_curve := nil;
- Fwattvar_curvename := '';
+ Fwattvar_curve := NIL;
// DRC
- FDbVMin := 0.95;
- FDbVMax := 1.05;
- FArGraLowV := 0.1;
- FArGraHiV := 0.1;
- FDRCRollAvgWindow := nil;
- FDRCRollAvgWindowLength := 1;
- FDRCRollAvgWindowLengthIntervalUnit := 's';
- FDRCVAvgWindowLengthSec := 1.0;
- priorDRCRollAvgWindow := nil;
- deltaVDynReac := nil;
+ FDbVMin := 0.95;
+ FDbVMax := 1.05;
+ FArGraLowV := 0.1;
+ FArGraHiV := 0.1;
+ FDRCRollAvgWindow := NIL;
+ FDRCRollAvgWindowLength := 1;
+ priorDRCRollAvgWindow := NIL;
+ deltaVDynReac := NIL;
// volt-watt
- Fvoltwatt_curve_size := 0;
- Fvoltwatt_curve := nil;
- Fvoltwatt_curvename := '';
- FvoltwattCH_curve_size := 0;
- FvoltwattCH_curve := nil;
- FvoltwattCH_curvename := '';
+ Fvoltwatt_curve := NIL;
+ FvoltwattCH_curve := NIL;
// AVR
Fv_setpoint := 1.0;
- DQDV := nil;
- Fv_setpointLimited := nil;
- FAvgpAVRVpuPrior := nil;
-
- {Flags used to record function states. They are interval variables of DER}
- FVVOperation := nil;
- FVWOperation := nil;
- FDRCOperation := nil;
- FVVDRCOperation := nil;
- FWPOperation := nil;
- FWVOperation := nil;
- FAVROperation := nil;
-
- {Others}
- FPendingChange := nil;
- cBuffer := nil;
- CondOffset := nil;
- InitPropertyValues(0);
-
- end;
+ DQDV := NIL;
+ Fv_setpointLimited := NIL;
+ FAvgpAVRVpuPrior := NIL;
+
+ // Flags used to record function states. They are interval variables of DER
+ FVVOperation := NIL;
+ FVWOperation := NIL;
+ FDRCOperation := NIL;
+ FVVDRCOperation := NIL;
+ FWPOperation := NIL;
+ FWVOperation := NIL;
+ FAVROperation := NIL;
+
+ // Others
+ FPendingChange := NIL;
+ cBuffer := NIL;
+ CondOffset := NIL;
+end;
destructor TInvControl2Obj.Destroy;
- begin
- ElementName := '';
-
- //TODO: remove? This is probably not required
+begin
Finalize(ControlledElement);
Finalize(NPhasesDER);
Finalize(NCondsDER);
Finalize(cBuffer);
Finalize(CondOffset);
- Finalize(FRollAvgWindow);
- Finalize(FDRCRollAvgWindow);
- Finalize(FDRCRollAvgWindowpu);
- Finalize(FAvgpVpuPrior);
- Finalize(FAvgpDRCVpuPrior);
- Finalize(FPresentVpu);
- Finalize(FPresentDRCVpu);
- Finalize(FPendingChange);
- Finalize(QDesiredVV);
- Finalize(QDesiredWP);
- Finalize(QDesiredWV);
- Finalize(QDesiredAVR);
- Finalize(QOld);
- Finalize(QOldVV);
- Finalize(QOldAVR);
- Finalize(QOldDRC);
- Finalize(QOldVVDRC);
- Finalize(QHeadroom);
- Finalize(QHeadroomNeg);
- Finalize(Qoutputpu);
- Finalize(QoutputVVpu);
- Finalize(QoutputAVRpu);
- Finalize(QoutputDRCpu);
- Finalize(QoutputVVDRCpu);
- Finalize(QDesireEndpu);
- Finalize(QDesireVVpu);
- Finalize(QDesireWPpu);
- Finalize(QDesireWVpu);
- Finalize(QDesireAVRpu);
- Finalize(QDesireLimitedpu);
- Finalize(QDesireOptionpu);
- Finalize(PLimitEndpu);
- Finalize(PLimitVWpu);
- Finalize(PLimitLimitedpu);
- Finalize(PLimitOptionpu);
- Finalize(QDesireDRCpu);
- Finalize(QDesiredDRC);
- Finalize(QDesiredVVDRC);
- Finalize(PLimitVW);
- Finalize(POldVWpu);
- Finalize(PBase);
- Finalize(deltaVDynReac);
- Finalize(priorRollAvgWindow);
- Finalize(priorDRCRollAvgWindow);
- Finalize(FVpuSolution);
Finalize(FlagChangeCurve);
Finalize(FActiveVVCurve);
- Finalize(FPriorWattspu);
- Finalize(FPriorWatts);
- Finalize(FPriorPLimitOptionpu);
- Finalize(FPriorQDesireOptionpu);
- Finalize(kW_out_desiredpu);
- Finalize(kW_out_desired);
- Finalize(FPriorvarspu);
- Finalize(FPriorvars);
- Finalize(FdeltaQFactor);
- Finalize(FdeltaPFactor);
- Finalize(DeltaV_old);
Finalize(FFlagVWOperates);
- Finalize(FVVOperation);
- Finalize(FAVROperation);
- Finalize(FVWOperation);
- Finalize(FDRCOperation);
- Finalize(FVVDRCOperation);
- Finalize(FWPOperation);
- Finalize(FWVOperation);
Finalize(FMonBuses);
Finalize(FMonBusesNodes);
- Finalize(FVBase);
Finalize(FVarFollowInverter);
- Finalize(FInverterON);
- Finalize(FpresentkW);
- Finalize(FkVARating);
- Finalize(Fpresentkvar);
- Finalize(FkvarLimit);
- Finalize(FkvarLimitNeg);
- Finalize(FCurrentkvarLimit);
- Finalize(FCurrentkvarLimitNeg);
- Finalize(FDCkWRated);
- Finalize(FpctDCkWRated);
- Finalize(FEffFactor);
- Finalize(FDCkW);
- Finalize(FPPriority);
Finalize(Fv_setpoint);
- Finalize(DQDV);
- Finalize(Fv_setpointLimited);
- Finalize(FAvgpAVRVpuPrior);
- if Assigned(FMonBusesVbase) then ReallocMem(FMonBusesVbase, 0);
+ if Assigned(FMonBusesVbase) then
+ ReallocMem(FMonBusesVbase, 0);
- Inherited Destroy;
- end;
+ inherited Destroy;
+end;
procedure TInvControl2Obj.RecalcElementData();
+var
+ i: Integer;
+begin
+ if FDERPointerList.Count = 0 then
+ MakeDERList;
- VAR
- i :Integer;
-
- begin
-
- if FDERPointerList.Count = 0 then MakeDERList;
-
- if FDERPointerList.Count > 0 then
+ if FDERPointerList.Count > 0 then
{Setting the terminal of the InvControl device to same as the 1st PVSystem/Storage element}
{ This sets it to a realistic value to avoid crashes later }
- begin
- MonitoredElement := TDSSCktElement(FDERPointerList.Get(1)); // Set MonitoredElement to 1st elemnent in list
+ begin
+ MonitoredElement := TDSSCktElement(FDERPointerList.Get(1)); // Set MonitoredElement to 1st elemnent in list
Setbus(1, MonitoredElement.Firstbus);
- end;
+ end;
for i := 1 to FDERPointerList.Count do
- begin
-
+ begin
// User ControlledElement[] as the pointer to the PVSystem/Storage elements
- ControlledElement[i] := TPCElement(FDERPointerList.Get(i)); // pointer to i-th PVSystem/Storage element
- SetLength(cBuffer[i], SizeOF(Complex) * ControlledElement[i].Yorder );
+ ControlledElement[i] := TPCElement(FDERPointerList.Get(i)); // pointer to i-th PVSystem/Storage element
+ SetLength(cBuffer[i], SizeOF(Complex) * ControlledElement[i].Yorder);
ControlledElement[i].ActiveTerminalIdx := 1; // Make the 1 st terminal active
- Nphases := ControlledElement[i].NPhases;
- Nconds := Nphases;
- FRollAvgWindow[i].BuffLength := FRollAvgWindowLength; // TEMc
- FDRCRollAvgWindow[i].BuffLength := FDRCRollAvgWindowLength;
+ FNphases := ControlledElement[i].NPhases;
+ Nconds := Nphases;
+ FRollAvgWindow[i].SetLength(FRollAvgWindowLength);
+ FDRCRollAvgWindow[i].SetLength(FDRCRollAvgWindowLength);
// for all modes other than VW and WATTPF, PF priority is not allowed
- if ((Mode <> VOLTWATT) and (Mode <> WATTPF)) Then
- Begin
- if ControlledElement[i].DSSClassName = 'PVSystem' then TPVSystemObj(ControlledElement[i]).PVSystemVars.PF_Priority := FALSE
- else if ControlledElement[i].DSSClassName = 'Storage' then TStorageObj(ControlledElement[i]).StorageVars.PF_Priority := FALSE;
- End;
+ if ((ControlMode <> VOLTWATT) and (ControlMode <> WATTPF)) then
+ begin
+ if ControlledElement[i].DSSClassName = 'PVSystem' then
+ TPVSystemObj(ControlledElement[i]).PVSystemVars.PF_Priority := FALSE
+ else
+ if ControlledElement[i].DSSClassName = 'Storage' then
+ TStorageObj(ControlledElement[i]).StorageVars.PF_Priority := FALSE;
+ end;
//FdeltaQFactor[i] := FdeltaQ_factor;
//FdeltaPFactor[i] := FdeltaP_factor;
- if Length(FMonBuses)=0 then FUsingMonBuses := FALSE else FUsingMonBuses := TRUE;
+ if Length(FMonBuses) = 0 then
+ FUsingMonBuses := FALSE
+ else
+ FUsingMonBuses := TRUE;
- if (ControlledElement[i] <> Nil) then UpdateDERParameters(i)
+ if (ControlledElement[i] <> NIL) then
+ UpdateDERParameters(i)
else
begin
- ControlledElement[i] := nil;
- DoErrorMsg('InvControl2: "' + Self.Name + '"',
- 'Controlled Element "' + FDERNameList.Strings[i-1] + '" Not Found.',
- ' PVSystem or Storage object must be defined previously.', 361);
+ ControlledElement[i] := NIL;
+ DoErrorMsg(Format(_('InvControl: "%s"'), [Self.Name]),
+ Format(_('Controlled Element "%s" not found.'), [DERNameList.Strings[i - 1]]),
+ _('PVSystem or Storage object must be defined previously.'), 361);
end;
- end;
-
- end;
+ end;
+end;
procedure TInvControl2Obj.MakePosSequence();
-
// *** This assumes the PVSystem/Storage devices have already been converted to pos seq
-
- begin
-
- if FDERPointerList.Count = 0 then RecalcElementData();
- Nphases := 3;
+begin
+ if FDERPointerList.Count = 0 then
+ RecalcElementData();
+ FNphases := 3;
Nconds := 3;
Setbus(1, MonitoredElement.GetBus(ElementTerminal));
- if FDERPointerList.Count > 0 then
+ if FDERPointerList.Count > 0 then
{Setting the terminal of the InvControl device to same as the 1st PVSystem/Storage element}
{ This sets it to a realistic value to avoid crashes later }
- begin
- MonitoredElement := TDSSCktElement(FDERPointerList.Get(1)); // Set MonitoredElement to 1st PVSystem/Storage in list
+ begin
+ MonitoredElement := TDSSCktElement(FDERPointerList.Get(1)); // Set MonitoredElement to 1st PVSystem/Storage in list
Setbus(1, MonitoredElement.Firstbus);
- Nphases := MonitoredElement.NPhases;
+ FNphases := MonitoredElement.NPhases;
Nconds := Nphases;
- end;
+ end;
inherited;
- end;
-
-procedure TInvControl2Obj.CalcYPrim();
- begin
- // leave YPrims as nil and they will be ignored
- // Yprim is zeroed when created. Leave it as is.
- // if YPrim=nil then YPrim := TcMatrix.CreateMatrix(Yorder);
- end;
-
-procedure TInvControl2Obj.GetCurrents(Curr: pComplexArray);
- VAR
- i:Integer;
- begin
- // Control is a zero current source
- for i := 1 to Fnconds do Curr^[i] := CZERO;
- end;
-
-procedure TInvControl2Obj.DumpProperties(F: TFileStream; Complete:Boolean);
- VAR
- i:Integer;
-
- begin
- Inherited DumpProperties(F,Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F,'~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
- if Complete then
- begin
- FSWriteln(F);
- end;
- end;
-
-procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
-
- VAR
- k : Integer;
- DERelem : TPCElement;
-
- begin
+end;
+procedure TInvControl2Obj.DoPendingAction(const Code, ProxyHdl: Integer);
+var
+ k: Integer;
+ DERelem: TPCElement;
+begin
for k := 1 to FDERPointerList.Count do
- begin
-
+ begin
DERelem := ControlledElement[k];
// Calculates QHeadRoom
Calc_QHeadRoom(k);
- if QHeadRoom[k] <> 0.0 then FPriorvarspu[k] := FPriorvars[k]/QHeadRoom[k];
+ if QHeadRoom[k] <> 0.0 then
+ FPriorvarspu[k] := FPriorvars[k] / QHeadRoom[k];
// Calculates PBase
Calc_PBase(k);
- FPriorWattspu[k] := FPriorWatts[k]/PBase[k];
+ FPriorWattspu[k] := FPriorWatts[k] / PBase[k];
// Calculates kW_out_desiredpu. Used for VW and VV_VW
kW_out_desiredpu[k] := kW_out_desired[k] / PBase[k];
// -------------------Smart Inverter Functions------------------------//
- {Smart Inverter volt-var function}
- if(ControlMode = VOLTVAR) and (CombiControlMode = NONE_COMBMODE) and (PendingChange[k]=CHANGEVARLEVEL) then
- begin
+ // Smart Inverter volt-var function
+ if (ControlMode = VOLTVAR) and (CombiMode = NONE_COMBMODE) and (PendingChange[k] = CHANGEVARLEVEL) then
+ begin
// Set var mode to VARMODEKVAR to indicate we might change kvar
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- TPVSystemObj(DERelem).VWmode := FALSE;
+ begin
+ TPVSystemObj(DERelem).VWmode := FALSE;
TPVSystemObj(DERelem).Varmode := VARMODEKVAR;
TPVSystemObj(DERelem).VVmode := TRUE;
- end
+ end
else
- begin
- TStorageObj(DERelem).VWmode := FALSE;
- TStorageObj(DERelem).Varmode := VARMODEKVAR;
- TStorageObj(DERelem).VVmode := TRUE;
- end;
+ begin
+ TStorageObj(DERelem).VWmode := FALSE;
+ TStorageObj(DERelem).Varmode := VARMODEKVAR;
+ TStorageObj(DERelem).VVmode := TRUE;
+ end;
//--------------------------------------------- Main process ---------------------------------------------//
@@ -1565,25 +1071,26 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// LPF or RF activated
if (RateofChangeMode = LPF) then
- begin
+ begin
CalcLPF(k, 'VARS', QDesireVVpu[k]);
// Checks kVA (watt priority) and kvarlimit limits
Check_Qlimits(k, QDesireOptionpu[k]);
QDesireEndpu[k] := Min(abs(QDesireLimitedpu[k]), abs(QDesireOptionpu[k])) * sign(QDesireOptionpu[k]);
- end
- else if (RateofChangeMode = RISEFALL) then
- begin
+ end
+ else
+ if (RateofChangeMode = RISEFALL) then
+ begin
CalcRF(k, 'VARS', QDesireVVpu[k]);
// Checks kVA (watt priority) and kvarlimit limits
Check_Qlimits(k, QDesireOptionpu[k]);
QDesireEndpu[k] := Min(abs(QDesireLimitedpu[k]), abs(QDesireOptionpu[k])) * sign(QDesireOptionpu[k]);
- end
+ end
else
- begin
+ begin
// Checks kVA (watt priority) and kvarlimit limits
Check_Qlimits(k, QDesireVVpu[k]);
QDesireEndpu[k] := Min(abs(QDesireVVpu[k]), abs(QDesireLimitedpu[k])) * sign(QDesireVVpu[k]);
- end;
+ end;
// Calculates QDesiredVV[k] through the convergence algorithm
CalcVoltVar_vars(k);
@@ -1591,24 +1098,30 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
//--------------------------------------------- end Main process ---------------------------------------------//
// Sets PVSystem/Storage's kvar_out
- if ControlledElement[k].DSSClassName = 'PVSystem' then TPVSystemObj(DERelem).Presentkvar := QDesiredVV[k]
- else TStorageObj(DERelem).kvarRequested := QDesiredVV[k];
+ if ControlledElement[k].DSSClassName = 'PVSystem' then
+ TPVSystemObj(DERelem).Presentkvar := QDesiredVV[k]
+ else
+ TStorageObj(DERelem).kvarRequested := QDesiredVV[k];
// Uptates PresentkW and Presentkvar considering watt and var priorities
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
+ begin
TPVSystemObj(DERelem).SetNominalPVSystemOuput();
- if QDesiredVV[k] >= 0.0 then Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end
+ if QDesiredVV[k] >= 0.0 then
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end
else
- begin
+ begin
TStorageObj(DERelem).SetNominalStorageOutput();
- if QDesiredVV[k] >= 0.0 then Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end;
+ if QDesiredVV[k] >= 0.0 then
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end;
// Values used in convergence
QoutputVVpu[k] := Qoutputpu[k];
@@ -1616,66 +1129,72 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// Values used in CalcQVVcurve_desiredpu
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- QOld[k] := TPVSystemObj(DERelem).Presentkvar;
+ begin
+ QOld[k] := TPVSystemObj(DERelem).Presentkvar;
QOldVV[k] := TPVSystemObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TPVSystemObj(DERelem).QualifiedName,
- Format('VOLTVAR mode requested PVSystem output var level to**, kvar= %.5g. Actual output set to kvar= %.5g.',
- [QDesiredVV[k], TPVSystemObj(DERelem).Presentkvar]));
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TPVSystemObj(DERelem).FullName,
+ Format('VOLTVAR mode requested PVSystem output var level to**, kvar= %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredVV[k], TPVSystemObj(DERelem).Presentkvar]));
+ end
else
- begin
- QOld[k] := TStorageObj(DERelem).Presentkvar;
+ begin
+ QOld[k] := TStorageObj(DERelem).Presentkvar;
QOldVV[k] := TStorageObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TStorageObj(DERelem).QualifiedName,
- Format('VOLTVAR mode requested Storage output var level to **, kvar = %.5g. Actual output set to kvar= %.5g.',
- [QDesiredVV[k], TStorageObj(DERelem).Presentkvar]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TStorageObj(DERelem).FullName,
+ Format('VOLTVAR mode requested Storage output var level to **, kvar = %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredVV[k], TStorageObj(DERelem).Presentkvar]));
- end;
- end
+ end;
+ end
{Smart Inverter active voltage regulation function}
- else if(ControlMode = AVR) and (CombiControlMode = NONE_COMBMODE) and (PendingChange[k]=CHANGEVARLEVEL) then
- begin
+ else
+ if (ControlMode = AVR) and (CombiMode = NONE_COMBMODE) and (PendingChange[k] = CHANGEVARLEVEL) then
+ begin
// Set var mode to VARMODEKVAR to indicate we might change kvar
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- TPVSystemObj(DERelem).VWmode := FALSE;
+ begin
+ TPVSystemObj(DERelem).VWmode := FALSE;
TPVSystemObj(DERelem).Varmode := VARMODEKVAR;
TPVSystemObj(DERelem).AVRmode := TRUE;
- end
+ end
else
- begin
- TStorageObj(DERelem).VWmode := FALSE;
- TStorageObj(DERelem).Varmode := VARMODEKVAR;
+ begin
+ TStorageObj(DERelem).VWmode := FALSE;
+ TStorageObj(DERelem).Varmode := VARMODEKVAR;
// TStorageObj(DERelem).VVmode := TRUE;
- end;
+ end;
//--------------------------------------------- Main process ---------------------------------------------//
if ActiveCircuit.Solution.ControlIteration = 1 then
- begin
+ begin
FAvgpVpuPrior[k] := FPresentVpu[k];
FAvgpAVRVpuPrior[k] := FPresentVpu[k];
// Sets PVSystem/Storage's kvar_out
- if ControlledElement[k].DSSClassName = 'PVSystem' then TPVSystemObj(DERelem).Presentkvar := QHeadRoom[k] / 2
- else TStorageObj(DERelem).kvarRequested := QHeadRoom[k] / 2;
- end
+ if ControlledElement[k].DSSClassName = 'PVSystem' then
+ TPVSystemObj(DERelem).Presentkvar := QHeadRoom[k] / 2
+ else
+ TStorageObj(DERelem).kvarRequested := QHeadRoom[k] / 2;
+ end
- else if ActiveCircuit.Solution.ControlIteration = 2 then
- begin
+ else
+ if ActiveCircuit.Solution.ControlIteration = 2 then
+ begin
// Sets PVSystem/Storage's kvar_out
if ControlledElement[k].DSSClassName = 'PVSystem' then
- DQDV[k] := abs(TPVSystemObj(DERelem).Presentkvar / QHeadRoom[k] / (FPresentVpu[k] - FAvgpVpuPrior[k]))
+ DQDV[k] := abs(TPVSystemObj(DERelem).Presentkvar / QHeadRoom[k] / (FPresentVpu[k] - FAvgpVpuPrior[k]))
else
- DQDV[k] := abs(TStorageObj(DERelem).kvarRequested / QHeadRoom[k] / (FPresentVpu[k] - FAvgpVpuPrior[k]));
- end
+ DQDV[k] := abs(TStorageObj(DERelem).kvarRequested / QHeadRoom[k] / (FPresentVpu[k] - FAvgpVpuPrior[k]));
+ end
else
- begin
+ begin
// Calculates QDesireAVRpu[k]
CalcQAVR_desiredpu(k);
@@ -1684,10 +1203,10 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
Check_Qlimits(k, QDesireAVRpu[k]);
QDesireEndpu[k] := Min(abs(QDesireAVRpu[k]), abs(QDesireLimitedpu[k])) * sign(QDesireAVRpu[k]);
- if abs(QDesireEndpu[k] - QDesireLimitedpu[k]) < 0.05 then
- Fv_setpointLimited[k] := FPresentVpu[k]
+ if abs(QDesireEndpu[k] - QDesireLimitedpu[k]) < 0.05 then
+ Fv_setpointLimited[k] := FPresentVpu[k]
else
- Fv_setpointLimited[k] := Fv_setpoint;
+ Fv_setpointLimited[k] := Fv_setpoint;
// Calculates QDesiredVV[k] through the convergence algorithm
CalcAVR_vars(k);
@@ -1695,68 +1214,77 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
//--------------------------------------------- end Main process ---------------------------------------------//
// Sets PVSystem/Storage's kvar_out
- if ControlledElement[k].DSSClassName = 'PVSystem' then TPVSystemObj(DERelem).Presentkvar := QDesiredAVR[k]
- else TStorageObj(DERelem).kvarRequested := QDesiredAVR[k];
+ if ControlledElement[k].DSSClassName = 'PVSystem' then
+ TPVSystemObj(DERelem).Presentkvar := QDesiredAVR[k]
+ else
+ TStorageObj(DERelem).kvarRequested := QDesiredAVR[k];
// Uptates PresentkW and Presentkvar considering watt and var priorities
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
+ begin
TPVSystemObj(DERelem).SetNominalPVSystemOuput();
- if QDesiredAVR[k] >= 0.0 then Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end
+ if QDesiredAVR[k] >= 0.0 then
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end
else
- begin
+ begin
TStorageObj(DERelem).SetNominalStorageOutput();
- if QDesiredAVR[k] >= 0.0 then Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end;
+ if QDesiredAVR[k] >= 0.0 then
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end;
// Values used in convergence
- QoutputAVRpu[k] := Qoutputpu[k];
- FAvgpVpuPrior[k] := FPresentVpu[k];
+ QoutputAVRpu[k] := Qoutputpu[k];
+ FAvgpVpuPrior[k] := FPresentVpu[k];
// Values used in CalcQVVcurve_desiredpu
- if ControlledElement[k].DSSClassName = 'PVSystem' then
+ if ControlledElement[k].DSSClassName = 'PVSystem' then
begin
- QOld[k] := TPVSystemObj(DERelem).Presentkvar;
- QOldAVR[k] := TPVSystemObj(DERelem).Presentkvar;
+ QOld[k] := TPVSystemObj(DERelem).Presentkvar;
+ QOldAVR[k] := TPVSystemObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TPVSystemObj(DERelem).QualifiedName,
- Format('VOLTVAR mode requested PVSystem output var level to**, kvar= %.5g. Actual output set to kvar= %.5g.',
- [QDesiredAVR[k], TPVSystemObj(DERelem).Presentkvar]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TPVSystemObj(DERelem).FullName,
+ Format('VOLTVAR mode requested PVSystem output var level to**, kvar= %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredAVR[k], TPVSystemObj(DERelem).Presentkvar]));
end
- else
+ else
begin
- QOld[k] := TStorageObj(DERelem).Presentkvar;
- QOldAVR[k] := TStorageObj(DERelem).Presentkvar;
+ QOld[k] := TStorageObj(DERelem).Presentkvar;
+ QOldAVR[k] := TStorageObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TStorageObj(DERelem).QualifiedName,
- Format('VOLTVAR mode requested Storage output var level to **, kvar = %.5g. Actual output set to kvar= %.5g.',
- [QDesiredAVR[k], TStorageObj(DERelem).Presentkvar]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TStorageObj(DERelem).FullName,
+ Format('VOLTVAR mode requested Storage output var level to **, kvar = %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredAVR[k], TStorageObj(DERelem).Presentkvar]));
end;
- end;
- end
+ end;
+ end
{Smart Inverter watt-pf function}
- else if(ControlMode = WATTPF) and (CombiControlMode = NONE_COMBMODE) and (PendingChange[k]=CHANGEVARLEVEL) then
- begin
+ else
+ if (ControlMode = WATTPF) and (CombiMode = NONE_COMBMODE) and (PendingChange[k] = CHANGEVARLEVEL) then
+ begin
// Set var mode to VARMODEKVAR to indicate we might change kvar
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- TPVSystemObj(DERelem).VWmode := FALSE;
+ begin
+ TPVSystemObj(DERelem).VWmode := FALSE;
TPVSystemObj(DERelem).Varmode := VARMODEKVAR;
- TPVSystemObj(DERelem).WPmode := TRUE;
- end
+ TPVSystemObj(DERelem).WPmode := TRUE;
+ end
else
- begin
- TStorageObj(DERelem).VWmode := FALSE;
- TStorageObj(DERelem).Varmode := VARMODEKVAR;
- TStorageObj(DERelem).WPmode := TRUE;
- end;
+ begin
+ TStorageObj(DERelem).VWmode := FALSE;
+ TStorageObj(DERelem).Varmode := VARMODEKVAR;
+ TStorageObj(DERelem).WPmode := TRUE;
+ end;
//--------------------------------------------- Main process ---------------------------------------------//
@@ -1772,28 +1300,36 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
//--------------------------------------------- end Main process ---------------------------------------------//
// Sets PVSystem/Storage's pf_wp_nominal
- if ControlledElement[k].DSSClassName = 'PVSystem' then TPVSystemObj(DERelem).pf_wp_nominal := pf_wp_nominal
- else TStorageObj(DERelem).kvarRequested := QDesiredWP[k];
+ if ControlledElement[k].DSSClassName = 'PVSystem' then
+ TPVSystemObj(DERelem).pf_wp_nominal := pf_wp_nominal
+ else
+ TStorageObj(DERelem).kvarRequested := QDesiredWP[k];
// Sets PVSystem/Storage's kvar_out
- if ControlledElement[k].DSSClassName = 'PVSystem' then TPVSystemObj(DERelem).Presentkvar := QDesiredWP[k]
- else TStorageObj(DERelem).kvarRequested := QDesiredWP[k];
+ if ControlledElement[k].DSSClassName = 'PVSystem' then
+ TPVSystemObj(DERelem).Presentkvar := QDesiredWP[k]
+ else
+ TStorageObj(DERelem).kvarRequested := QDesiredWP[k];
// Uptates PresentkW and Presentkvar considering watt and var priorities
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
+ begin
TPVSystemObj(DERelem).SetNominalPVSystemOuput();
- if QDesiredWP[k] >= 0.0 then Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end
+ if QDesiredWP[k] >= 0.0 then
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end
else
- begin
+ begin
TStorageObj(DERelem).SetNominalStorageOutput();
- if QDesiredWP[k] >= 0.0 then Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end;
+ if QDesiredWP[k] >= 0.0 then
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end;
// Values used in convergence
QoutputVVpu[k] := Qoutputpu[k];
@@ -1801,42 +1337,45 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// Values used in CalcQVVcurve_desiredpu
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- QOld[k] := TPVSystemObj(DERelem).Presentkvar;
+ begin
+ QOld[k] := TPVSystemObj(DERelem).Presentkvar;
QOldVV[k] := TPVSystemObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TPVSystemObj(DERelem).QualifiedName,
- Format('WATTPF mode requested PVSystem output var level to**, kvar= %.5g. Actual output set to kvar= %.5g.',
- [QDesiredWP[k], TPVSystemObj(DERelem).Presentkvar]));
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TPVSystemObj(DERelem).FullName,
+ Format('WATTPF mode requested PVSystem output var level to**, kvar= %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredWP[k], TPVSystemObj(DERelem).Presentkvar]));
+ end
else
- begin
- QOld[k] := TStorageObj(DERelem).Presentkvar;
+ begin
+ QOld[k] := TStorageObj(DERelem).Presentkvar;
QOldVV[k] := TStorageObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TStorageObj(DERelem).QualifiedName,
- Format('WATTPF mode requested Storage output var level to **, kvar = %.5g. Actual output set to kvar= %.5g.',
- [QDesiredWP[k], TStorageObj(DERelem).Presentkvar]));
-
- end;
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TStorageObj(DERelem).FullName,
+ Format('WATTPF mode requested Storage output var level to **, kvar = %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredWP[k], TStorageObj(DERelem).Presentkvar]));
+
+ end;
+ end
{Smart Inverter watt-var function}
- else if(ControlMode = WATTVAR) and (CombiControlMode = NONE_COMBMODE) and (PendingChange[k]=CHANGEVARLEVEL) then
- begin
+ else
+ if (ControlMode = WATTVAR) and (CombiMode = NONE_COMBMODE) and (PendingChange[k] = CHANGEVARLEVEL) then
+ begin
// Set var mode to VARMODEKVAR to indicate we might change kvar
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- TPVSystemObj(DERelem).VWmode := FALSE;
+ begin
+ TPVSystemObj(DERelem).VWmode := FALSE;
TPVSystemObj(DERelem).Varmode := VARMODEKVAR;
- TPVSystemObj(DERelem).WVmode := TRUE;
- end
+ TPVSystemObj(DERelem).WVmode := TRUE;
+ end
else
- begin
- TStorageObj(DERelem).VWmode := FALSE;
- TStorageObj(DERelem).Varmode := VARMODEKVAR;
- TStorageObj(DERelem).WVmode := TRUE;
- end;
+ begin
+ TStorageObj(DERelem).VWmode := FALSE;
+ TStorageObj(DERelem).Varmode := VARMODEKVAR;
+ TStorageObj(DERelem).WVmode := TRUE;
+ end;
//--------------------------------------------- Main process ---------------------------------------------//
@@ -1858,24 +1397,28 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
TPVSystemObj(DERelem).Presentkvar := QDesiredWV[k];
TPVSystemObj(DERelem).PresentkW := PLimitEndpu[k] * Min(FkVARating[k], FDCkWRated[k]);
end
- else
+ else
TStorageObj(DERelem).kvarRequested := QDesiredWV[k];
// Uptates PresentkW and Presentkvar considering watt and var priorities
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
+ begin
TPVSystemObj(DERelem).SetNominalPVSystemOuput();
- if QDesiredWV[k] >= 0.0 then Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end
+ if QDesiredWV[k] >= 0.0 then
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end
else
- begin
+ begin
TStorageObj(DERelem).SetNominalStorageOutput();
- if QDesiredWV[k] >= 0.0 then Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end;
+ if QDesiredWV[k] >= 0.0 then
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end;
// Values used in convergence
QoutputVVpu[k] := Qoutputpu[k];
@@ -1883,43 +1426,45 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// Values used in CalcQVVcurve_desiredpu
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- QOld[k] := TPVSystemObj(DERelem).Presentkvar;
+ begin
+ QOld[k] := TPVSystemObj(DERelem).Presentkvar;
QOldVV[k] := TPVSystemObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TPVSystemObj(DERelem).QualifiedName,
- Format('WATTVAR mode requested PVSystem output var level to**, kvar= %.5g. Actual output set to kvar= %.5g.',
- [QDesiredWV[k], TPVSystemObj(DERelem).Presentkvar]));
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TPVSystemObj(DERelem).FullName,
+ Format('WATTVAR mode requested PVSystem output var level to**, kvar= %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredWV[k], TPVSystemObj(DERelem).Presentkvar]));
+ end
else
- begin
- QOld[k] := TStorageObj(DERelem).Presentkvar;
+ begin
+ QOld[k] := TStorageObj(DERelem).Presentkvar;
QOldVV[k] := TStorageObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TStorageObj(DERelem).QualifiedName,
- Format('WATTVAR mode requested Storage output var level to **, kvar = %.5g. Actual output set to kvar= %.5g.',
- [QDesiredWV[k], TStorageObj(DERelem).Presentkvar]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TStorageObj(DERelem).FullName,
+ Format('WATTVAR mode requested Storage output var level to **, kvar = %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredWV[k], TStorageObj(DERelem).Presentkvar]));
- end;
- end
+ end;
+ end
{Smart Inverter DRC function}
- else if(ControlMode = DRC) and (CombiControlMode = NONE_COMBMODE) and (PendingChange[k]=CHANGEVARLEVEL) then
- begin
-
+ else
+ if (ControlMode = DRC) and (CombiMode = NONE_COMBMODE) and (PendingChange[k] = CHANGEVARLEVEL) then
+ begin
// Set var mode to VARMODEKVAR to indicate we might change kvar
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- TPVSystemObj(DERelem).VWmode := FALSE;
+ begin
+ TPVSystemObj(DERelem).VWmode := FALSE;
TPVSystemObj(DERelem).Varmode := VARMODEKVAR;
TPVSystemObj(DERelem).DRCmode := TRUE;
- end
+ end
else
- begin
- TStorageObj(DERelem).VWmode := FALSE;
+ begin
+ TStorageObj(DERelem).VWmode := FALSE;
TStorageObj(DERelem).Varmode := VARMODEKVAR;
TStorageObj(DERelem).DRCmode := TRUE;
- end;
+ end;
//--------------------------------------------- Main process ---------------------------------------------//
@@ -1928,25 +1473,26 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// LPF or RF activated
if (RateofChangeMode = LPF) then
- begin
+ begin
CalcLPF(k, 'VARS', QDesireDRCpu[k]);
// Checks kVA (watt priority) and kvarlimit limits
Check_Qlimits(k, QDesireOptionpu[k]);
QDesireEndpu[k] := Min(abs(QDesireLimitedpu[k]), abs(QDesireOptionpu[k])) * sign(QDesireOptionpu[k]);
- end
- else if (RateofChangeMode = RISEFALL) then
- begin
+ end
+ else
+ if (RateofChangeMode = RISEFALL) then
+ begin
CalcRF(k, 'VARS', QDesireDRCpu[k]);
// Checks kVA (watt priority) and kvarlimit limits
Check_Qlimits(k, QDesireOptionpu[k]);
QDesireEndpu[k] := Min(abs(QDesireLimitedpu[k]), abs(QDesireOptionpu[k])) * sign(QDesireOptionpu[k]);
- end
+ end
else
- begin
+ begin
// Checks kVA (watt priority) and kvarlimit limits
Check_Qlimits(k, QDesireDRCpu[k]);
QDesireEndpu[k] := Min(abs(QDesireDRCpu[k]), abs(QDesireLimitedpu[k])) * sign(QDesireDRCpu[k]);
- end;
+ end;
// Calculates QDesiredDRC[k]
CalcDRC_vars(k);
@@ -1954,24 +1500,30 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
//--------------------------------------------- end main process ---------------------------------------------//
// Sets DER kvar_out
- if ControlledElement[k].DSSClassName = 'PVSystem' then TPVSystemObj(DERelem).Presentkvar := QDesiredDRC[k]
- else TStorageObj(DERelem).kvarRequested := QDesiredDRC[k];
+ if ControlledElement[k].DSSClassName = 'PVSystem' then
+ TPVSystemObj(DERelem).Presentkvar := QDesiredDRC[k]
+ else
+ TStorageObj(DERelem).kvarRequested := QDesiredDRC[k];
// Uptates PresentkW and Presentkvar considering watt and var priorities
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
+ begin
TPVSystemObj(DERelem).SetNominalPVSystemOuput();
- if QDesiredDRC[k] >= 0.0 then Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end
+ if QDesiredDRC[k] >= 0.0 then
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end
else
- begin
+ begin
TStorageObj(DERelem).SetNominalStorageOutput();
- if QDesiredDRC[k] >= 0.0 then Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end;
+ if QDesiredDRC[k] >= 0.0 then
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end;
// Values used in convergence
QoutputDRCpu[k] := Qoutputpu[k];
@@ -1979,45 +1531,47 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// Values used in CalcDRC_vars
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- QOld[k] := TPVSystemObj(DERelem).Presentkvar;
+ begin
+ QOld[k] := TPVSystemObj(DERelem).Presentkvar;
QOldDRC[k] := TPVSystemObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TPVSystemObj(DERelem).QualifiedName,
- Format('DRC mode requested PVSystem output var level to **, kvar= %.5g. Actual output set to kvar= %.5g.',
- [QDesiredDRC[k], TPVSystemObj(DERelem).Presentkvar]));
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TPVSystemObj(DERelem).FullName,
+ Format('DRC mode requested PVSystem output var level to **, kvar= %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredDRC[k], TPVSystemObj(DERelem).Presentkvar]));
+ end
else
- begin
- QOld[k] := TStorageObj(DERelem).Presentkvar;
+ begin
+ QOld[k] := TStorageObj(DERelem).Presentkvar;
QOldDRC[k] := TStorageObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TStorageObj(DERelem).QualifiedName,
- Format('DRC mode requested Storage output var level to **, kvar= %.5g. Actual output set to kvar= %.5g.',
- [QDesiredDRC[k], TStorageObj(DERelem).Presentkvar]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TStorageObj(DERelem).FullName,
+ Format('DRC mode requested Storage output var level to **, kvar= %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredDRC[k], TStorageObj(DERelem).Presentkvar]));
- end;
- end
-
- {Smart Inverter VV_DRC function}
- else if(ControlMode = NONE_MODE) and (CombiControlMode = VV_DRC) and (PendingChange[k]=CHANGEDRCVVARLEVEL) then
- begin
+ end;
+ end
+ // Smart Inverter VV_DRC function
+ else
+ if (ControlMode = NONE_MODE) and (CombiMode = VV_DRC) and (PendingChange[k] = CHANGEDRCVVARLEVEL) then
+ begin
// Set var mode to VARMODEKVAR to indicate we might change kvar
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- TPVSystemObj(DERelem).VWmode := FALSE;
+ begin
+ TPVSystemObj(DERelem).VWmode := FALSE;
TPVSystemObj(DERelem).Varmode := VARMODEKVAR;
- TPVSystemObj(DERelem).VVmode := TRUE;
+ TPVSystemObj(DERelem).VVmode := TRUE;
TPVSystemObj(DERelem).DRCmode := TRUE;
- end
+ end
else
- begin
- TStorageObj(DERelem).VWmode := FALSE;
+ begin
+ TStorageObj(DERelem).VWmode := FALSE;
TStorageObj(DERelem).Varmode := VARMODEKVAR;
- TStorageObj(DERelem).VVmode := TRUE;
+ TStorageObj(DERelem).VVmode := TRUE;
TStorageObj(DERelem).DRCmode := TRUE;
- end;
+ end;
//--------------------------------------------- Main process ---------------------------------------------//
@@ -2027,25 +1581,26 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// LPF or RF activated
if (RateofChangeMode = LPF) then
- begin
+ begin
CalcLPF(k, 'VARS', QDesireVVpu[k] + QDesireDRCpu[k]);
// Checks kVA (watt priority) and kvarlimit limits
Check_Qlimits(k, QDesireOptionpu[k]);
QDesireEndpu[k] := Min(abs(QDesireLimitedpu[k]), abs(QDesireOptionpu[k])) * sign(QDesireOptionpu[k]);
- end
- else if (RateofChangeMode = RISEFALL) then
- begin
+ end
+ else
+ if (RateofChangeMode = RISEFALL) then
+ begin
CalcRF(k, 'VARS', QDesireVVpu[k] + QDesireDRCpu[k]);
// Checks kVA (watt priority) and kvarlimit limits
Check_Qlimits(k, QDesireOptionpu[k]);
QDesireEndpu[k] := Min(abs(QDesireLimitedpu[k]), abs(QDesireOptionpu[k])) * sign(QDesireOptionpu[k]);
- end
+ end
else
- begin
+ begin
// Checks kVA (watt priority) and kvarlimit limits
Check_Qlimits(k, QDesireVVpu[k] + QDesireDRCpu[k]);
QDesireEndpu[k] := Min(abs(QDesireVVpu[k] + QDesireDRCpu[k]), abs(QDesireLimitedpu[k])) * sign(QDesireVVpu[k] + QDesireDRCpu[k]);
- end;
+ end;
// Calculates QDesiredVVDRC[k]
CalcVVDRC_vars(k);
@@ -2053,24 +1608,30 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
//--------------------------------------------- end main process ---------------------------------------------//
// Sets DER kvar_out
- if ControlledElement[k].DSSClassName = 'PVSystem' then TPVSystemObj(DERelem).Presentkvar := QDesiredVVDRC[k]
- else TStorageObj(DERelem).kvarRequested := QDesiredVVDRC[k];
+ if ControlledElement[k].DSSClassName = 'PVSystem' then
+ TPVSystemObj(DERelem).Presentkvar := QDesiredVVDRC[k]
+ else
+ TStorageObj(DERelem).kvarRequested := QDesiredVVDRC[k];
// Uptates PresentkW and Presentkvar considering watt and var priorities
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
+ begin
TPVSystemObj(DERelem).SetNominalPVSystemOuput();
- if QDesiredVVDRC[k] >= 0.0 then Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end
+ if QDesiredVVDRC[k] >= 0.0 then
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end
else
- begin
+ begin
TStorageObj(DERelem).SetNominalStorageOutput();
- if QDesiredVVDRC[k] >= 0.0 then Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end;
+ if QDesiredVVDRC[k] >= 0.0 then
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end;
// Values used in convergence
QoutputVVDRCpu[k] := Qoutputpu[k];
@@ -2079,37 +1640,39 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// Values used in CalcQVVcurve_desiredpu and CalcVVDRC_vars
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- QOld[k] := TPVSystemObj(DERelem).Presentkvar;
+ begin
+ QOld[k] := TPVSystemObj(DERelem).Presentkvar;
QOldVVDRC[k] := TPVSystemObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TPVSystemObj(DERelem).QualifiedName,
- Format('**VV_DRC mode requested PVSystem output var level to **, kvar= %.5g. Actual output set to kvar= %.5g.',
- [QDesiredVVDRC[k], TPVSystemObj(DERelem).Presentkvar]));
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TPVSystemObj(DERelem).FullName,
+ Format('**VV_DRC mode requested PVSystem output var level to **, kvar= %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredVVDRC[k], TPVSystemObj(DERelem).Presentkvar]));
+ end
else
- begin
- QOld[k] := TStorageObj(DERelem).Presentkvar;
+ begin
+ QOld[k] := TStorageObj(DERelem).Presentkvar;
QOldVVDRC[k] := TStorageObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TStorageObj(DERelem).QualifiedName,
- Format('**VV_DRC mode requested Storage output var level to **, kvar= %.5g. Actual output set to kvar= %.5g.',
- [QDesiredVVDRC[k], TStorageObj(DERelem).Presentkvar]));
- end;
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TStorageObj(DERelem).FullName,
+ Format('**VV_DRC mode requested Storage output var level to **, kvar= %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredVVDRC[k], TStorageObj(DERelem).Presentkvar]));
+ end;
+ end
{Smart Inverter volt-watt function}
- else if(ControlMode = VOLTWATT) and (CombiControlMode = NONE_COMBMODE) and (PendingChange[k]=CHANGEWATTLEVEL) then
- begin
-
+ else
+ if (ControlMode = VOLTWATT) and (CombiMode = NONE_COMBMODE) and (PendingChange[k] = CHANGEWATTLEVEL) then
+ begin
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- TPVSystemObj(DERelem).VWmode := TRUE;
- end
+ begin
+ TPVSystemObj(DERelem).VWmode := TRUE;
+ end
else
- begin
- TStorageObj(DERelem).VWmode := TRUE;
- end;
+ begin
+ TStorageObj(DERelem).VWmode := TRUE;
+ end;
//--------------------------------------------- Main process ---------------------------------------------//
@@ -2118,25 +1681,26 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// LPF or RF activated
if (RateofChangeMode = LPF) then
- begin
+ begin
CalcLPF(k, 'WATTS', PLimitVWpu[k]);
// Checks kVA (var priority) and pctPmpp limits
Check_Plimits(k, PLimitOptionpu[k]);
PLimitEndpu[k] := Min(PLimitLimitedpu[k], PLimitOptionpu[k]);
- end
- else if (RateofChangeMode = RISEFALL) then
- begin
+ end
+ else
+ if (RateofChangeMode = RISEFALL) then
+ begin
CalcRF(k, 'WATTS', PLimitVWpu[k]);
// Checks kVA (var priority) and pctPmpp limits
Check_Plimits(k, PLimitOptionpu[k]);
PLimitEndpu[k] := Min(PLimitLimitedpu[k], PLimitOptionpu[k]);
- end
+ end
else
- begin
+ begin
// Checks kVA (var priority) and pctPmpp limits
Check_Plimits(k, PLimitVWpu[k]);
PLimitEndpu[k] := Min(abs(PLimitLimitedpu[k]), abs(PLimitVWpu[k])) * sign(PLimitVWpu[k]);
- end;
+ end;
// Calculates PLimitVW[k] through the convergence algorithm
CalcVoltWatt_watts(k);
@@ -2145,20 +1709,20 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// Sets DER kW_out
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
+ begin
TPVSystemObj(DERelem).PresentkW := PLimitVW[k];
// Uptates PresentkW and Presentkvar considering watt and var priorities
TPVSystemObj(DERelem).SetNominalPVSystemOuput();
- end
+ end
else
- begin
+ begin
TStorageObj(DERelem).kWRequested := PLimitVW[k];
// Uptates PresentkW and Presentkvar considering watt and var priorities
TStorageObj(DERelem).SetNominalStorageOutput();
- end;
+ end;
// Values used in convergence
@@ -2167,39 +1731,43 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// Flag has to do set to 0 when kW_out is lower than Ptemp (max power allowed from volt-watt function)
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- if ((abs(PLimitVW[k]) > 0.0) and (abs(TPVSystemObj(DERelem).presentkW - PLimitVW[k]) / PLimitVW[k] > 0.0001)) then FVWOperation[k] := 0; // 0.01% is the value chosen at the moment
+ begin
+ if ((abs(PLimitVW[k]) > 0.0) and (abs(TPVSystemObj(DERelem).presentkW - PLimitVW[k]) / PLimitVW[k] > 0.0001)) then
+ FVWOperation[k] := 0; // 0.01% is the value chosen at the moment
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+TPVSystemObj(DERelem).QualifiedName,
- Format('**VOLTWATT mode set PVSystem kw output limit to **, kw= %.5g. Actual output is kw= %.5g.',
- [PLimitVW[k], TPVSystemObj(DERelem).presentkW]));
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TPVSystemObj(DERelem).FullName,
+ Format('**VOLTWATT mode set PVSystem kw output limit to **, kw= %.5g. Actual output is kw= %.5g.',
+ [PLimitVW[k], TPVSystemObj(DERelem).presentkW]));
+ end
else
- begin
- if abs(abs(TStorageObj(DERelem).presentkW) - PLimitVW[k]) / PLimitVW[k] > 0.0001 then FVWOperation[k] := 0; // 0.01% is the value chosen at the moment
-
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+ TStorageObj(DERelem).QualifiedName,
- Format('**VOLTWATT mode set Storage kw output limit to ** kw= %.5g. Actual output is kw= %.5g.',
- [PLimitVW[k], TStorageObj(DERelem).presentkW]));
+ begin
+ if abs(abs(TStorageObj(DERelem).presentkW) - PLimitVW[k]) / PLimitVW[k] > 0.0001 then
+ FVWOperation[k] := 0; // 0.01% is the value chosen at the moment
- end;
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TStorageObj(DERelem).FullName,
+ Format('**VOLTWATT mode set Storage kw output limit to ** kw= %.5g. Actual output is kw= %.5g.',
+ [PLimitVW[k], TStorageObj(DERelem).presentkW]));
- else if(ControlMode = NONE_MODE) and (CombiControlMode = VV_VW) and (PendingChange[k]=CHANGEWATTVARLEVEL) then
- begin
+ end;
+ end
+ else
+ if (ControlMode = NONE_MODE) and (CombiMode = VV_VW) and (PendingChange[k] = CHANGEWATTVARLEVEL) then
+ begin
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- TPVSystemObj(DERelem).VWmode := TRUE;
+ begin
+ TPVSystemObj(DERelem).VWmode := TRUE;
TPVSystemObj(DERelem).Varmode := VARMODEKVAR;
- TPVSystemObj(DERelem).VVmode := TRUE;
- end
+ TPVSystemObj(DERelem).VVmode := TRUE;
+ end
else
- begin
- TStorageObj(DERelem).VWmode := TRUE;
+ begin
+ TStorageObj(DERelem).VWmode := TRUE;
TStorageObj(DERelem).Varmode := VARMODEKVAR;
- TStorageObj(DERelem).VVmode := TRUE;
- end;
+ TStorageObj(DERelem).VVmode := TRUE;
+ end;
//--------------------------------------------- Main process ---------------------------------------------//
@@ -2209,7 +1777,7 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// LPF or RF activated
if (RateofChangeMode = LPF) then
- begin
+ begin
CalcLPF(k, 'VARS', QDesireVVpu[k]);
// Checks kVA (watt priority) and kvarlimit limits
Check_Qlimits(k, QDesireOptionpu[k]);
@@ -2219,9 +1787,10 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// Checks kVA (var priority) and pctPmpp limits
Check_Plimits(k, PLimitOptionpu[k]);
PLimitEndpu[k] := Min(PLimitLimitedpu[k], PLimitOptionpu[k]);
- end
- else if (RateofChangeMode = RISEFALL) then
- begin
+ end
+ else
+ if (RateofChangeMode = RISEFALL) then
+ begin
CalcRF(k, 'VARS', QDesireVVpu[k]);
// Checks kVA (watt priority) and kvarlimit limits
Check_Qlimits(k, QDesireOptionpu[k]);
@@ -2231,9 +1800,9 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// Checks kVA (var priority) and pctPmpp limits
Check_Plimits(k, PLimitOptionpu[k]);
PLimitEndpu[k] := Min(PLimitLimitedpu[k], PLimitOptionpu[k]);
- end
+ end
else
- begin
+ begin
// Checks kVA (watt priority) and kvarlimit limits
Check_Qlimits(k, QDesireVVpu[k]);
QDesireEndpu[k] := Min(abs(QDesireVVpu[k]), abs(QDesireLimitedpu[k])) * sign(QDesireVVpu[k]);
@@ -2241,7 +1810,7 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// Checks kVA (var priority) and pctPmpp limits
Check_Plimits(k, PLimitVWpu[k]);
PLimitEndpu[k] := Min(abs(PLimitLimitedpu[k]), abs(PLimitVWpu[k])) * sign(PLimitVWpu[k]);
- end;
+ end;
// Calculates PLimitVW[k] and QDesiredVV[k] through the convergence algorithm
CalcVoltWatt_watts(k);
@@ -2251,31 +1820,35 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// Sets DER kvar_out and kW_out
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
+ begin
TPVSystemObj(DERelem).Presentkvar := QDesiredVV[k];
- TPVSystemObj(DERelem).presentkW := PLimitVW[k];
- end
+ TPVSystemObj(DERelem).presentkW := PLimitVW[k];
+ end
else
- begin
+ begin
TStorageObj(DERelem).kvarRequested := QDesiredVV[k];
- TStorageObj(DERelem).kWRequested := PLimitVW[k];
- end;
+ TStorageObj(DERelem).kWRequested := PLimitVW[k];
+ end;
// Uptates PresentkW and Presentkvar considering watt and var priorities
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
+ begin
TPVSystemObj(DERelem).SetNominalPVSystemOuput();
- if QDesiredVV[k] >= 0.0 then Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end
+ if QDesiredVV[k] >= 0.0 then
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TPVSystemObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end
else
- begin
+ begin
TStorageObj(DERelem).SetNominalStorageOutput();
- if QDesiredVV[k] >= 0.0 then Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
- else Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
- end;
+ if QDesiredVV[k] >= 0.0 then
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroom[k]
+ else
+ Qoutputpu[k] := TStorageObj(DERelem).Presentkvar / QHeadroomNeg[k];
+ end;
// Values used in convergence
QoutputVVpu[k] := Qoutputpu[k];
@@ -2284,221 +1857,218 @@ procedure TInvControl2Obj.DoPendingAction(Const Code, ProxyHdl:Integer);
// Values used in CalcQVVcurve_desiredpu
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- QOld[k] := TPVSystemObj(DERelem).Presentkvar;
+ begin
+ QOld[k] := TPVSystemObj(DERelem).Presentkvar;
QOldVV[k] := TPVSystemObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TPVSystemObj(DERelem).QualifiedName,
- Format('**VV_VW mode requested PVSystem output var level to **, kvar= %.5g. Actual output set to kvar= %.5g.',
- [QDesiredVV[k], TPVSystemObj(DERelem).presentkvar]));
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TPVSystemObj(DERelem).FullName,
+ Format('**VV_VW mode requested PVSystem output var level to **, kvar= %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredVV[k], TPVSystemObj(DERelem).presentkvar]));
+ end
else
- begin
- QOld[k] := TStorageObj(DERelem).Presentkvar;
+ begin
+ QOld[k] := TStorageObj(DERelem).Presentkvar;
QOldVV[k] := TStorageObj(DERelem).Presentkvar;
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name +', '+ TStorageObj(DERelem).QualifiedName,
- Format('**VV_VW mode requested Storage output var level to **, kvar= %.5g. Actual output set to kvar= %.5g.',
- [QDesiredVV[k], TStorageObj(DERelem).presentkvar]));
- end;
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TStorageObj(DERelem).FullName,
+ Format('**VV_VW mode requested Storage output var level to **, kvar= %.5g. Actual output set to kvar= %.5g.',
+ [QDesiredVV[k], TStorageObj(DERelem).presentkvar]));
+ end;
// Flag has to do set to 0 when kW_out is lower than Ptemp (max power allowed from volt-watt function)
if ControlledElement[k].DSSClassName = 'PVSystem' then
- begin
- if abs(TPVSystemObj(DERelem).presentkW - PLimitVW[k]) / PLimitVW[k] > 0.0001 then FVWOperation[k] := 0; // 0.01% is the value chosen at the moment
+ begin
+ if abs(TPVSystemObj(DERelem).presentkW - PLimitVW[k]) / PLimitVW[k] > 0.0001 then
+ FVWOperation[k] := 0; // 0.01% is the value chosen at the moment
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+TPVSystemObj(DERelem).QualifiedName,
- Format('**VV_VW mode set PVSystem kw output limit to **, kw= %.5g. Actual output is kw= %.5g.',
- [PLimitVW[k], TPVSystemObj(DERelem).presentkW]));
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TPVSystemObj(DERelem).FullName,
+ Format('**VV_VW mode set PVSystem kw output limit to **, kw= %.5g. Actual output is kw= %.5g.',
+ [PLimitVW[k], TPVSystemObj(DERelem).presentkW]));
+ end
else
- begin
- if abs(abs(TStorageObj(DERelem).presentkW) - PLimitVW[k]) / PLimitVW[k] > 0.0001 then FVWOperation[k] := 0; // 0.01% is the value chosen at the moment
+ begin
+ if abs(abs(TStorageObj(DERelem).presentkW) - PLimitVW[k]) / PLimitVW[k] > 0.0001 then
+ FVWOperation[k] := 0; // 0.01% is the value chosen at the moment
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+ TStorageObj(DERelem).QualifiedName,
- Format('**VV_VW mode set Storage kw output limit to** kw= %.5g. Actual output is kw= %.5g.',
- [PLimitVW[k], TStorageObj(DERelem).presentkW]));
- end;
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + TStorageObj(DERelem).FullName,
+ Format('**VV_VW mode set Storage kw output limit to** kw= %.5g. Actual output is kw= %.5g.',
+ [PLimitVW[k], TStorageObj(DERelem).presentkW]));
+ end;
- end;
+ end;
ActiveCircuit.Solution.LoadsNeedUpdating := TRUE;
- Set_PendingChange(NONE,k);
- DERelem := Nil;
+ Set_PendingChange(NONE, k);
+ DERelem := NIL;
end;
-
end;
procedure TInvControl2Obj.GetmonVoltage(var Vpresent: Double; i: Integer; BasekV: Double);
- Var
- j : integer;
- rBus : TDSSBus;
- numNodes : Integer;
- // v : Complex;
- vi : Complex;
- vj : Complex;
-
- begin
-
+var
+ j: Integer;
+ rBus: TDSSBus;
+ numNodes: Integer;
+ vi: Complex;
+ vj: Complex;
+begin
if FUsingMonBuses then
- begin
-
- for j := 0 to Length(FMonBuses)-1 do
- begin
+ begin
+ for j := 0 to Length(FMonBuses) - 1 do
+ begin
FMonBusesIndex := ActiveCircuit.BusList.Find(FMonBuses[j]);
rBus := ActiveCircuit.Buses^[FMonBusesIndex];
if (length(FMonBusesNodes[j]) = 2) then
- begin
- vi := (ActiveCircuit.Solution.NodeV^[rBus.GetRef(FMonBusesNodes[j][0])]);
- vj := (ActiveCircuit.Solution.NodeV^[rBus.GetRef(FMonBusesNodes[j][1])]);
- cBuffer[i,j] := cmulreal(Csub(vi, vj), BasekV * 1000.0 / FMonBusesVbase[j+1]);
- // v := cBuffer[i,j];
- end
+ begin
+ vi := (ActiveCircuit.Solution.NodeV^[rBus.GetRef(FMonBusesNodes[j][0])]);
+ vj := (ActiveCircuit.Solution.NodeV^[rBus.GetRef(FMonBusesNodes[j][1])]);
+ cBuffer[i, j] := (vi - vj) * (BasekV * 1000.0 / FMonBusesVbase[j + 1]); // TODO: NIL check?
+ end
else
- begin
- cBuffer[i,j] := cmulreal(ActiveCircuit.Solution.NodeV^[rBus.GetRef(FMonBusesNodes[j][0])], BasekV * 1000.0 / FMonBusesVbase[j+1]);
- // v := cBuffer[i,j];
- end;
- end;
+ begin
+ cBuffer[i, j] := ActiveCircuit.Solution.NodeV^[rBus.GetRef(FMonBusesNodes[j][0])] * (BasekV * 1000.0 / FMonBusesVbase[j + 1]);
+ end;
+ end;
- CASE FMonBusesPhase of
+ case FMonBusesPhase of
AVGPHASES:
- begin
+ begin
Vpresent := 0.0;
- for j := 0 to Length(FMonBuses)-1 do
- Vpresent := Vpresent + Cabs(cBuffer[i,j]);
+ for j := 0 to Length(FMonBuses) - 1 do
+ Vpresent := Vpresent + Cabs(cBuffer[i, j]);
Vpresent := Vpresent / Length(FMonBuses);
- end;
+ end;
MAXPHASE:
- begin
+ begin
Vpresent := 0.0;
- for j := 0 to Length(FMonBuses)-1 do
- Vpresent := Max(Vpresent, Cabs(cBuffer[i,j]));
- end;
+ for j := 0 to Length(FMonBuses) - 1 do
+ Vpresent := Max(Vpresent, Cabs(cBuffer[i, j]));
+ end;
MINPHASE:
- begin
+ begin
Vpresent := 1.0E50;
- for j := 0 to Length(FMonBuses)-1 do
- Vpresent := Min(Vpresent, Cabs(cBuffer[i,j]));
- end;
- else
- Vpresent := Cabs(cBuffer[i, FMonBusesPhase]);
- end;
- end
-
+ for j := 0 to Length(FMonBuses) - 1 do
+ Vpresent := Min(Vpresent, Cabs(cBuffer[i, j]));
+ end;
+ else
+ Vpresent := Cabs(cBuffer[i, FMonBusesPhase]);
+ end;
+ end
else
- begin
+ begin
ControlledElement[i].ComputeVTerminal();
numNodes := ControlledElement[i].NPhases;
for j := 1 to numNodes do
- cBuffer[i,j] := ControlledElement[i].Vterminal^[j];
+ cBuffer[i, j] := ControlledElement[i].Vterminal^[j];
- CASE FMonBusesPhase of
- AVGPHASES:
+ case FMonBusesPhase of
+ AVGPHASES:
begin
- Vpresent := 0.0;
- for j := 1 to numNodes do
- Vpresent := Vpresent + Cabs(cBuffer[i,j]);
- Vpresent := Vpresent / numNodes;
+ Vpresent := 0.0;
+ for j := 1 to numNodes do
+ Vpresent := Vpresent + Cabs(cBuffer[i, j]);
+ Vpresent := Vpresent / numNodes;
end;
- MAXPHASE:
+ MAXPHASE:
begin
- Vpresent := 0.0;
- for j := 1 to numNodes do
- Vpresent := Max(Vpresent, Cabs(cBuffer[i,j]));
+ Vpresent := 0.0;
+ for j := 1 to numNodes do
+ Vpresent := Max(Vpresent, Cabs(cBuffer[i, j]));
end;
- MINPHASE:
+ MINPHASE:
begin
- Vpresent := 1.0E50;
- for j := 1 to numNodes do
- Vpresent := Min(Vpresent, Cabs(cBuffer[i,j]));
+ Vpresent := 1.0E50;
+ for j := 1 to numNodes do
+ Vpresent := Min(Vpresent, Cabs(cBuffer[i, j]));
end;
- else
+ else
Vpresent := Cabs(cBuffer[i, FMonBusesPhase]);
end;
- end;
-
- end;
+ end;
+end;
procedure TInvControl2Obj.UpdateDERParameters(i: Integer);
- begin
-
+begin
with ControlledElement[i] do
- if ControlledElement[i].DSSClassName = 'PVSystem' then
+ if ControlledElement[i].DSSClassName = 'PVSystem' then
begin
- with TPVSystemObj(ControlledElement[i]) do
+ with TPVSystemObj(ControlledElement[i]) do
begin
- CondOffset[i] := (NTerms-1) * NCondsDER[i]; // for speedy sampling
-
- FVBase[i] := Vbase;
- FVarFollowInverter[i] := VarFollowInverter;
- FInverterON[i] := InverterON;
- FpresentkW[i] := PresentkW;
- FkVARating[i] := kVARating;
- Fpresentkvar[i] := Presentkvar;
- FkvarLimit[i] := kvarLimit;
- FkvarLimitNeg[i] := kvarLimitNeg;
- FCurrentkvarLimit[i] := CurrentkvarLimit;
- FCurrentkvarLimitNeg[i] := CurrentkvarLimitNeg;
- FDCkWRated[i] := Pmpp;
- FpctDCkWRated[i] := puPmpp;
- FEffFactor[i] := PVSystemVars.EffFactor;
- FDCkW[i] := PVSystemVars.PanelkW;
- FPPriority[i] := PVSystemVars.P_Priority;
+ CondOffset[i] := (NTerms - 1) * NCondsDER[i]; // for speedy sampling
+
+ FVBase[i] := Vbase;
+ FVarFollowInverter[i] := VarFollowInverter;
+ FInverterON[i] := InverterON;
+ FpresentkW[i] := PresentkW;
+ FkVARating[i] := kVARating;
+ Fpresentkvar[i] := Presentkvar;
+ FkvarLimit[i] := kvarLimit;
+ FkvarLimitNeg[i] := kvarLimitNeg;
+ FCurrentkvarLimit[i] := CurrentkvarLimit;
+ FCurrentkvarLimitNeg[i] := CurrentkvarLimitNeg;
+ FDCkWRated[i] := Pmpp;
+ FpctDCkWRated[i] := puPmpp;
+ FEffFactor[i] := PVSystemVars.EffFactor;
+ FDCkW[i] := PVSystemVars.PanelkW;
+ FPPriority[i] := PVSystemVars.P_Priority;
end;
end
- else if ControlledElement[i].DSSClassName = 'Storage' then
+ else
+ if ControlledElement[i].DSSClassName = 'Storage' then
begin
- with TStorageObj(ControlledElement[i]) do
+ with TStorageObj(ControlledElement[i]) do
begin
- FVBase[i] := Vbase;
- FVarFollowInverter[i] := VarFollowInverter;
- FInverterON[i] := InverterON;
- FpresentkW[i] := PresentkW;
- FkVARating[i] := kVARating;
- Fpresentkvar[i] := Presentkvar;
- FkvarLimit[i] := kvarLimit;
- FkvarLimitNeg[i] := kvarLimitNeg;
- FCurrentkvarLimit[i] := CurrentkvarLimit;
- FCurrentkvarLimitNeg[i] := CurrentkvarLimitNeg;
- FDCkWRated[i] := StorageVars.kWrating;
- FpctDCkWRated[i] := pctkWrated;
- FEffFactor[i] := Storagevars.EffFactor;
- FDCkW[i] := 0.0; // not using it (using TStorageObj.DCkW directly)
- FPPriority[i] := StorageVars.P_priority;
-
+ FVBase[i] := Vbase;
+ FVarFollowInverter[i] := VarFollowInverter;
+ FInverterON[i] := InverterON;
+ FpresentkW[i] := PresentkW;
+ FkVARating[i] := kVARating;
+ Fpresentkvar[i] := Presentkvar;
+ FkvarLimit[i] := kvarLimit;
+ FkvarLimitNeg[i] := kvarLimitNeg;
+ FCurrentkvarLimit[i] := CurrentkvarLimit;
+ FCurrentkvarLimitNeg[i] := CurrentkvarLimitNeg;
+ FDCkWRated[i] := StorageVars.kWrating;
+ FpctDCkWRated[i] := StorageVars.pctkWrated;
+ FEffFactor[i] := Storagevars.EffFactor;
+ FDCkW[i] := 0.0; // not using it (using TStorageObj.DCkW directly)
+ FPPriority[i] := StorageVars.P_priority;
end
end;
- end;
+end;
procedure TInvControl2Obj.Sample();
-
- VAR
- i :Integer;
- basekV :Double;
- Vpresent :Double;
- PVSys :TPVSystemObj;
- Storage :TStorageObj;
-
- begin
+var
+ i: Integer;
+ basekV: Double;
+ Vpresent: Double;
+ PVSys: TPVSystemObj = NIL;
+ Storage: TStorageObj = NIL;
+begin
// if list is not defined, go make one from all PVSystem/Storage in circuit
- if FDERPointerList.Count=0 then RecalcElementData();
+ if FDERPointerList.Count = 0 then
+ RecalcElementData();
- if (FListSize>0) then
- begin
+ if (FListSize > 0) then
+ begin
// if an InvControl2 controls more than one PVSystem/Storage, control each one
// separately based on the PVSystem/Storage's terminal voltages, etc.
for i := 1 to FDERPointerList.Count do
- begin
+ begin
UpdateDERParameters(i);
- if ControlledElement[i].DSSClassName = 'PVSystem' then PVSys := ControlledElement[i] as TPVSystemObj
- else Storage := ControlledElement[i] as TStorageObj;
+ if ControlledElement[i].DSSClassName = 'PVSystem' then
+ PVSys := ControlledElement[i] as TPVSystemObj
+ else
+ Storage := ControlledElement[i] as TStorageObj;
BasekV := FVBase[i] / 1000.0; // It's a line-to-ground voltage
@@ -2507,8 +2077,8 @@ procedure TInvControl2Obj.Sample();
// for reporting Vpriorpu correctly in EventLog (this update is normally perform at DoPendingAction)
if ActiveCircuit.Solution.ControlIteration = 1 then
begin
- FAvgpVpuPrior[i] := FPresentVpu[i];
- FAvgpDRCVpuPrior[i] := FPresentDRCVpu[i];
+ FAvgpVpuPrior[i] := FPresentVpu[i];
+ FAvgpDRCVpuPrior[i] := FPresentDRCVpu[i];
end;
kW_out_desired[i] := FpresentkW[i]; // necessary to update kW_out_desired at every control iteration for Storage with SC
@@ -2516,11 +2086,13 @@ procedure TInvControl2Obj.Sample();
// Help says that it must be used just for vv and vw
// convert to per-unit on bus' kvbase, or
// if using averaging window values, then set prior voltage to averaging window
- if(FVoltage_CurveX_ref = 1) and (FRollAvgWindow[i].Get_AvgVal <> 0.0) then
- FPresentVpu[i] := Vpresent / (FRollAvgWindow[i].Get_AvgVal)
- else if(FVoltage_CurveX_ref = 2) and (FRollAvgWindow[i].Get_AvgVal <> 0.0) then
- FPresentVpu[i] := (FRollAvgWindow[i].Get_AvgVal) / (basekV * 1000.0)
- else FPresentVpu[i] := Vpresent / (BasekV * 1000.0);
+ if (FVoltage_CurveX_ref = 1) and (FRollAvgWindow[i].AvgVal <> 0.0) then
+ FPresentVpu[i] := Vpresent / (FRollAvgWindow[i].AvgVal)
+ else
+ if (FVoltage_CurveX_ref = 2) and (FRollAvgWindow[i].AvgVal <> 0.0) then
+ FPresentVpu[i] := (FRollAvgWindow[i].AvgVal) / (basekV * 1000.0)
+ else
+ FPresentVpu[i] := Vpresent / (BasekV * 1000.0);
FPresentDRCVpu[i] := Vpresent / (BasekV * 1000.0);
@@ -2528,256 +2100,269 @@ procedure TInvControl2Obj.Sample();
// FVreg is the pu voltage used in the volt-var and volt-watt curves
FVreg := FPresentVpu[i];
- if CombiControlMode = VV_DRC then
- begin
+ if CombiMode = VV_DRC then
+ begin
// Sets internal variables of controlled element.
// FVVDRCOperation is a flag which indicates if VVDRC function operates or not (-1=absorbing Q, 1=injecting Q, 0=No operation)
- if ControlledElement[i].DSSClassName = 'PVSystem' then
- begin
- PVSys.Set_Variable(5,FVreg);
- PVSys.Set_Variable(6,FDRCRollAvgWindow[i].Get_AvgVal / (basekV * 1000.0)); // save rolling average voltage in monitor
- PVSys.Set_Variable(10,FVVDRCOperation[i]);
- end
- else
- begin
- Storage.Set_Variable(14,FVreg);
- Storage.Set_Variable(15,FDRCRollAvgWindow[i].Get_AvgVal / (basekV * 1000.0)); // save rolling average voltage in monitor
- Storage.Set_Variable(19,FVVDRCOperation[i]);
- end;
+ if ControlledElement[i].DSSClassName = 'PVSystem' then
+ begin
+ PVSys.Set_Variable(5, FVreg);
+ PVSys.Set_Variable(6, FDRCRollAvgWindow[i].AvgVal / (basekV * 1000.0)); // save rolling average voltage in monitor
+ PVSys.Set_Variable(10, FVVDRCOperation[i]);
+ end
+ else
+ begin
+ Storage.Set_Variable(14, FVreg);
+ Storage.Set_Variable(15, FDRCRollAvgWindow[i].AvgVal / (basekV * 1000.0)); // save rolling average voltage in monitor
+ Storage.Set_Variable(19, FVVDRCOperation[i]);
+ end;
// if inverter is off then exit
- if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then continue;
+ if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then
+ continue;
// if the volt-var curve does not exist, exit
- if Length(Fvvc_curvename) = 0 then
- begin
- DoSimpleMsg('XY Curve object representing vvc1_curve does not exist or is not tied to InvControl.', 382);
- exit
- end;
+ if Fvvc_curve = NIL then
+ begin
+ DoSimpleMsg(_('XY Curve object representing vvc1_curve does not exist or is not tied to InvControl.'), 382);
+ exit
+ end;
- if (ControlledElement[i].DSSClassName = 'PVSystem') then
- Begin
- PVSys.VVmode := TRUE;
- PVSys.DRCmode := TRUE;
- End
- else
- Begin
- Storage.VVmode := TRUE;
- Storage.DRCmode := TRUE;
- End;
+ if (ControlledElement[i].DSSClassName = 'PVSystem') then
+ begin
+ PVSys.VVmode := TRUE;
+ PVSys.DRCmode := TRUE;
+ end
+ else
+ begin
+ Storage.VVmode := TRUE;
+ Storage.DRCmode := TRUE;
+ end;
//DRC triggers
- if(priorDRCRollAvgWindow[i] = 0.0) then
+ if (priorDRCRollAvgWindow[i] = 0.0) then
+ begin
+ if (Abs(FPresentDRCVpu[i] - FAvgpDRCVpuPrior[i]) > FVoltageChangeTolerance) or
+ (Abs(FPresentVpu[i] - FAvgpVpuPrior[i]) > FVoltageChangeTolerance) then
begin
-
- if (Abs(FPresentDRCVpu[i] - FAvgpDRCVpuPrior[i]) > FVoltageChangeTolerance) or
- (Abs(FPresentVpu[i] - FAvgpVpuPrior[i]) > FVoltageChangeTolerance) then
- begin
// Resets DER state variable only if it has not converged yet
- FVVDRCOperation[i] := 0.0;
+ FVVDRCOperation[i] := 0.0;
- Set_PendingChange(CHANGEDRCVVARLEVEL,i);
+ Set_PendingChange(CHANGEDRCVVARLEVEL, i);
- with ActiveCircuit.Solution.DynaVars do
+ with ActiveCircuit.Solution.DynaVars do
ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
-
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+ControlledElement[i].QualifiedName,
- Format('**Ready to change var output due to DRC trigger in VV_DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g',
- [FPresentDRCVpu[i],FAvgpDRCVpuPrior[i]]));
- end;
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + ControlledElement[i].FullName,
+ Format(_('**Ready to change var output due to DRC trigger in VV_DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g'),
+ [FPresentDRCVpu[i], FAvgpDRCVpuPrior[i]]));
end;
+ end;
+
//Trigger from volt-var mode
- if (((Abs(FPresentVpu[i] - FAvgpVpuPrior[i]) > FVoltageChangeTolerance) or
- (Abs(FPresentDRCVpu[i] - FAvgpDRCVpuPrior[i]) > FVoltageChangeTolerance) or
- ((Abs(Abs(QoutputVVDRCpu[i]) - Abs(QDesireEndpu[i])) > FVarChangeTolerance))) or
- (ActiveCircuit.Solution.ControlIteration = 1)) then
- begin
+ if (((Abs(FPresentVpu[i] - FAvgpVpuPrior[i]) > FVoltageChangeTolerance) or
+ (Abs(FPresentDRCVpu[i] - FAvgpDRCVpuPrior[i]) > FVoltageChangeTolerance) or
+ ((Abs(Abs(QoutputVVDRCpu[i]) - Abs(QDesireEndpu[i])) > FVarChangeTolerance))) or
+ (ActiveCircuit.Solution.ControlIteration = 1)) then
+ begin
// Resets DER state variable only if it has not converged yet
- FVVDRCOperation[i] := 0.0;
+ FVVDRCOperation[i] := 0.0;
- Set_PendingChange(CHANGEDRCVVARLEVEL,i);
- with ActiveCircuit.Solution.DynaVars do
+ Set_PendingChange(CHANGEDRCVVARLEVEL, i);
+ with ActiveCircuit.Solution.DynaVars do
ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+ControlledElement[i].QualifiedName,
- Format('**Ready to change VV_DRC output due to volt-var trigger in VV_DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g',
- [FPresentVpu[i],FAvgpVpuPrior[i]]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + ControlledElement[i].FullName,
+ Format(_('**Ready to change VV_DRC output due to volt-var trigger in VV_DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g'),
+ [FPresentVpu[i], FAvgpVpuPrior[i]]));
- end;
- end
+ end;
+ end
- else if CombiControlMode = VV_VW then
- begin
+ else
+ if CombiMode = VV_VW then
+ begin
// Sets internal variables of controlled element.
// FVVOperation is a flag which indicates if volt-var function operates or not (-1=absorbing Q, 1=injecting Q, 0=No operation)
// FVWOperation is a flag which indicates if volt-watt function operates or not
// Combined modes operation is shown through TWO flags. It allows us to verify which of the individual function operates or not
if ControlledElement[i].DSSClassName = 'PVSystem' then
- begin
- PVSys.Set_Variable(5,FVreg);
- PVSys.Set_Variable(7,FVVOperation[i]);
- PVSys.Set_Variable(8,FVWOperation[i]);
- end
+ begin
+ PVSys.Set_Variable(5, FVreg);
+ PVSys.Set_Variable(7, FVVOperation[i]);
+ PVSys.Set_Variable(8, FVWOperation[i]);
+ end
else
- begin
- Storage.Set_Variable(14,FVreg);
- Storage.Set_Variable(16,FVVOperation[i]);
- Storage.Set_Variable(17,FVWOperation[i]);
- end;
+ begin
+ Storage.Set_Variable(14, FVreg);
+ Storage.Set_Variable(16, FVVOperation[i]);
+ Storage.Set_Variable(17, FVWOperation[i]);
+ end;
// if inverter is off then exit
- if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then continue;
+ if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then
+ continue;
// if volt-watt curve does not exist, exit
if ControlledElement[i].DSSClassName = 'PVSystem' then
begin
- if Length(Fvoltwatt_curvename) = 0 then
- begin
- DoSimpleMsg('XY Curve object representing voltwatt_curve does not exist or is not tied to InvControl.', 381);
- exit
- end;
+ if Fvoltwatt_curve = NIL then
+ begin
+ DoSimpleMsg(_('XY Curve object representing voltwatt_curve does not exist or is not tied to InvControl.'), 381);
+ exit
+ end;
end
else
begin
- if (Length(Fvoltwatt_curvename) = 0) and (Length(FvoltwattCH_curvename) = 0) then
- begin
- DoSimpleMsg('XY Curve object representing voltwatt_curve does not exist or is not tied to InvControl.', 381);
- exit
- end;
+ if (Fvoltwatt_curve = NIL) and (FvoltwattCH_curve = NIL) then
+ begin
+ DoSimpleMsg(_('XY Curve object representing voltwatt_curve does not exist or is not tied to InvControl.'), 381);
+ exit
+ end;
end;
// if the volt-var curve does not exist, exit
- if Length(Fvvc_curvename) = 0 then
- begin
- DoSimpleMsg('XY Curve object representing vvc1_curve does not exist or is not tied to InvControl.', 382);
- exit
- end;
+ if Fvvc_curve = NIL then
+ begin
+ DoSimpleMsg(_('XY Curve object representing vvc1_curve does not exist or is not tied to InvControl.'), 382);
+ exit
+ end;
if (ControlledElement[i].DSSClassName = 'PVSystem') then
- Begin
- PVSys.VVmode := TRUE;
- PVSys.VWmode := TRUE
- End
+ begin
+ PVSys.VVmode := TRUE;
+ PVSys.VWmode := TRUE
+ end
else
- Begin
- Storage.VVmode := TRUE;
- Storage.VWmode := TRUE;
- End;
+ begin
+ Storage.VVmode := TRUE;
+ Storage.VWmode := TRUE;
+ end;
// Trigger from volt-watt mode
- if ((Abs(FPresentVpu[i] - FAvgpVpuPrior[i]) > FVoltageChangeTolerance) or (Abs(PLimitEndpu[i]-POldVWpu[i])>FActivePChangeTolerance) or
- (ActiveCircuit.Solution.ControlIteration = 1)) then
-
- begin
+ if ((Abs(FPresentVpu[i] - FAvgpVpuPrior[i]) > FVoltageChangeTolerance) or (Abs(PLimitEndpu[i] - POldVWpu[i]) > FActivePChangeTolerance) or
+ (ActiveCircuit.Solution.ControlIteration = 1)) then
+ begin
// Resets DER state variable only if it has not converged yet
FVWOperation[i] := 0;
- Set_PendingChange(CHANGEWATTVARLEVEL,i);
+ Set_PendingChange(CHANGEWATTVARLEVEL, i);
with ActiveCircuit.Solution.DynaVars do
- ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ ControlActionHandle := ActiveCircuit.ControlQueue.Push
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+ControlledElement[i].QualifiedName,
- Format('**Ready to change VV_VW output due to volt-watt trigger**, Vavgpu= %.5g, VPriorpu=%.5g',
- [FPresentVpu[i],FAvgpVpuPrior[i]]));;
- end;
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + ControlledElement[i].FullName,
+ Format('**Ready to change VV_VW output due to volt-watt trigger**, Vavgpu= %.5g, VPriorpu=%.5g',
+ [FPresentVpu[i], FAvgpVpuPrior[i]]));
+ ;
+ end;
//Trigger from volt-var mode
if (((Abs(FPresentVpu[i] - FAvgpVpuPrior[i]) > FVoltageChangeTolerance) or
- ((Abs(Abs(Qoutputpu[i]) - Abs(QDesireEndpu[i])) > FVarChangeTolerance))) or
- (ActiveCircuit.Solution.ControlIteration = 1)) then
-
- begin
+ ((Abs(Abs(Qoutputpu[i]) - Abs(QDesireEndpu[i])) > FVarChangeTolerance))) or
+ (ActiveCircuit.Solution.ControlIteration = 1)) then
+ begin
// Resets DER state variable only if it has not converged yet
FVVOperation[i] := 0;
- Set_PendingChange(CHANGEWATTVARLEVEL,i);
+ Set_PendingChange(CHANGEWATTVARLEVEL, i);
with ActiveCircuit.Solution.DynaVars do
- ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ ControlActionHandle := ActiveCircuit.ControlQueue.Push
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+ControlledElement[i].QualifiedName,
- Format('**Ready to change VV_VW output due to volt-var trigger**, Vavgpu= %.5g, VPriorpu=%.5g',
- [FPresentVpu[i],FAvgpVpuPrior[i]]));
- end;
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + ControlledElement[i].FullName,
+ Format('**Ready to change VV_VW output due to volt-var trigger**, Vavgpu= %.5g, VPriorpu=%.5g',
+ [FPresentVpu[i], FAvgpVpuPrior[i]]));
+ end;
+ end
- else if ControlMode = VOLTWATT then // volt-watt control mode
- begin
+ else
+ if ControlMode = VOLTWATT then // volt-watt control mode
+ begin
// Sets internal variables of controlled element.
// FVWOperation is a flag which indicates if volt-watt function operates or not
if ControlledElement[i].DSSClassName = 'PVSystem' then
- begin
- PVSys.Set_Variable(5,FVreg);
- PVSys.Set_Variable(8,FVWOperation[i]);
- end
+ begin
+ PVSys.Set_Variable(5, FVreg);
+ PVSys.Set_Variable(8, FVWOperation[i]);
+ end
else
- begin
- Storage.Set_Variable(14,FVreg);
- Storage.Set_Variable(17,FVWOperation[i]);
- end;
+ begin
+ Storage.Set_Variable(14, FVreg);
+ Storage.Set_Variable(17, FVWOperation[i]);
+ end;
- if (FInverterON[i] = FALSE) then continue;
+ if (FInverterON[i] = FALSE) then
+ continue;
if ControlledElement[i].DSSClassName = 'PVSystem' then
- begin
- if Length(Fvoltwatt_curvename) = 0 then
- begin
- DoSimpleMsg('XY Curve object representing voltwatt_curve does not exist or is not tied to InvControl.', 381);
+ begin
+ if Fvoltwatt_curve = NIL then
+ begin
+ DoSimpleMsg(_('XY Curve object representing voltwatt_curve does not exist or is not tied to InvControl.'), 381);
exit
- end;
- end
+ end;
+ end
else
- begin
- if (Length(Fvoltwatt_curvename) = 0) and (Length(FvoltwattCH_curvename) = 0) then
- begin
- DoSimpleMsg('XY Curve object representing voltwatt_curve does not exist or is not tied to InvControl.', 381);
- exit
- end;
-
- end;
+ begin
+ if (Fvoltwatt_curve = NIL) and (FvoltwattCH_curve = NIL) then
+ begin
+ DoSimpleMsg(_('XY Curve object representing voltwatt_curve does not exist or is not tied to InvControl.'), 381);
+ exit
+ end;
- if (ControlledElement[i].DSSClassName = 'PVSystem') then PVSys.VWmode := TRUE
- else Storage.VWmode := TRUE;
+ end;
- if ((Abs(FPresentVpu[i] - FAvgpVpuPrior[i]) > FVoltageChangeTolerance) or (Abs(PLimitEndpu[i]-POldVWpu[i])>FActivePChangeTolerance) or
- (ActiveCircuit.Solution.ControlIteration = 1)) then
- begin
+ if (ControlledElement[i].DSSClassName = 'PVSystem') then
+ PVSys.VWmode := TRUE
+ else
+ Storage.VWmode := TRUE;
+ if ((Abs(FPresentVpu[i] - FAvgpVpuPrior[i]) > FVoltageChangeTolerance) or (Abs(PLimitEndpu[i] - POldVWpu[i]) > FActivePChangeTolerance) or
+ (ActiveCircuit.Solution.ControlIteration = 1)) then
+ begin
// Resets DER state variable only if it has not converged yet
FVWOperation[i] := 0;
- Set_PendingChange(CHANGEWATTLEVEL,i);
+ Set_PendingChange(CHANGEWATTLEVEL, i);
with ActiveCircuit.Solution.DynaVars do
- ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+ControlledElement[i].QualifiedName,
- Format('**Ready to limit watt output due to VOLTWATT mode**, Vavgpu= %.5g, VPriorpu=%.5g',
- [FPresentVpu[i],FAvgpVpuPrior[i]]));
- end;
- end
-
- else if ControlMode = AVR then // Active voltage regulation control mode
- begin
+ ControlActionHandle := ActiveCircuit.ControlQueue.Push
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + ControlledElement[i].FullName,
+ Format('**Ready to limit watt output due to VOLTWATT mode**, Vavgpu= %.5g, VPriorpu=%.5g',
+ [FPresentVpu[i], FAvgpVpuPrior[i]]));
+ end;
+ end
+
+ else
+ if ControlMode = AVR then // Active voltage regulation control mode
+ begin
// Sets internal variables of PVSystem/Storage.
// FAVROperation is a flag which indicates if volt-var function operates or not (-1=absorbing Q, 1=injecting Q, 0=No operation)
// if inverter is off then exit
- if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then continue;
+ if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then
+ continue;
- if (ControlledElement[i].DSSClassName = 'PVSystem') then PVSys.AVRmode := TRUE
- else Storage.VVmode := TRUE;
+ if (ControlledElement[i].DSSClassName = 'PVSystem') then
+ PVSys.AVRmode := TRUE
+ else
+ Storage.VVmode := TRUE;
//Trigger from AVR mode
@@ -2786,1402 +2371,1176 @@ procedure TInvControl2Obj.Sample();
(Abs(FPresentVpu[i] - Fv_setpointLimited[i]) > FVoltageChangeTolerance)) or
(ActiveCircuit.Solution.ControlIteration = 1)) then
- begin
-
+ begin
// Resets DER state variable only if it has not converged yet
FAVROperation[i] := 0;
- Set_PendingChange(CHANGEVARLEVEL,i);
+ Set_PendingChange(CHANGEVARLEVEL, i);
with ActiveCircuit.Solution.DynaVars do
- ControlActionHandle := ActiveCircuit.ControlQueue.Push(intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ ControlActionHandle := ActiveCircuit.ControlQueue.Push(intHour, t + TimeDelay, PendingChange[i], 0, Self);
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+ControlledElement[i].QualifiedName,
- Format('**Ready to change var output due to AVR trigger in AVR mode**, Vavgpu= %.5g, VPriorpu=%.5g, Vsetpoint=%.5g, VsetpointLimited=%.5g',
- [FPresentVpu[i],FAvgpVpuPrior[i], Fv_setpoint, Fv_setpointLimited[i]]));
- end;
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + ControlledElement[i].FullName,
+ Format('**Ready to change var output due to AVR trigger in AVR mode**, Vavgpu= %.5g, VPriorpu=%.5g, Vsetpoint=%.5g, VsetpointLimited=%.5g',
+ [FPresentVpu[i], FAvgpVpuPrior[i], Fv_setpoint, Fv_setpointLimited[i]]));
+ end;
+ end
- else if ControlMode = VOLTVAR then // volt-var control mode
- begin
+ else
+ if ControlMode = VOLTVAR then // volt-var control mode
+ begin
// Sets internal variables of PVSystem/Storage.
// FVVOperation is a flag which indicates if volt-var function operates or not (-1=absorbing Q, 1=injecting Q, 0=No operation)
if ControlledElement[i].DSSClassName = 'PVSystem' then
- begin
- PVSys.Set_Variable(5,FVreg);
- PVSys.Set_Variable(7,FVVOperation[i]);
- end
+ begin
+ PVSys.Set_Variable(5, FVreg);
+ PVSys.Set_Variable(7, FVVOperation[i]);
+ end
else
- begin
- Storage.Set_Variable(14,FVreg);
- Storage.Set_Variable(16,FVVOperation[i]);
- end;
+ begin
+ Storage.Set_Variable(14, FVreg);
+ Storage.Set_Variable(16, FVVOperation[i]);
+ end;
// if inverter is off then exit
- if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then continue;
+ if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then
+ continue;
- if Length(Fvvc_curvename) = 0 then
- begin
- DoSimpleMsg('XY Curve object representing vvc1_curve does not exist or is not tied to InvControl.', 382);
- exit
- end;
+ if Fvvc_curve = NIL then
+ begin
+ DoSimpleMsg(_('XY Curve object representing vvc1_curve does not exist or is not tied to InvControl.'), 382);
+ exit
+ end;
- if (ControlledElement[i].DSSClassName = 'PVSystem') then PVSys.VVmode := TRUE
- else Storage.VVmode := TRUE;
+ if (ControlledElement[i].DSSClassName = 'PVSystem') then
+ PVSys.VVmode := TRUE
+ else
+ Storage.VVmode := TRUE;
//Trigger from volt-var mode
if (((Abs(FPresentVpu[i] - FAvgpVpuPrior[i]) > FVoltageChangeTolerance) or
((Abs(Abs(QoutputVVpu[i]) - Abs(QDesireEndpu[i])) > FVarChangeTolerance))) or
(ActiveCircuit.Solution.ControlIteration = 1)) then
- begin
-
+ begin
// Resets DER state variable only if it has not converged yet
FVVOperation[i] := 0;
- Set_PendingChange(CHANGEVARLEVEL,i);
+ Set_PendingChange(CHANGEVARLEVEL, i);
with ActiveCircuit.Solution.DynaVars do
- ControlActionHandle := ActiveCircuit.ControlQueue.Push(intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ ControlActionHandle := ActiveCircuit.ControlQueue.Push(intHour, t + TimeDelay, PendingChange[i], 0, Self);
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+ControlledElement[i].QualifiedName,
- Format('**Ready to change var output due to volt-var trigger in volt-var mode**, Vavgpu= %.5g, VPriorpu=%.5g',
- [FPresentVpu[i],FAvgpVpuPrior[i]]));
- end;
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + ControlledElement[i].FullName,
+ Format('**Ready to change var output due to volt-var trigger in volt-var mode**, Vavgpu= %.5g, VPriorpu=%.5g',
+ [FPresentVpu[i], FAvgpVpuPrior[i]]));
+ end;
+ end
- else if ControlMode = WATTPF then // watt-pf control mode
- begin
+ else
+ if ControlMode = WATTPF then // watt-pf control mode
+ begin
// Sets internal variables of PVSystem/Storage.
// FWPOperation is a flag which indicates if watt-pf function operates or not (-1=absorbing Q, 1=injecting Q, 0=No operation)
if ControlledElement[i].DSSClassName = 'PVSystem' then
- begin
- PVSys.Set_Variable(5,FVreg);
- PVSys.Set_Variable(11,FWPOperation[i]);
- end
+ begin
+ PVSys.Set_Variable(5, FVreg);
+ PVSys.Set_Variable(11, FWPOperation[i]);
+ end
else
- begin
- Storage.Set_Variable(14,FVreg);
- Storage.Set_Variable(16,FWPOperation[i]);
- end;
+ begin
+ Storage.Set_Variable(14, FVreg);
+ Storage.Set_Variable(16, FWPOperation[i]);
+ end;
// if inverter is off then exit
- if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then continue;
+ if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then
+ continue;
- if Length(Fwattpf_curvename) = 0 then
- begin
- DoSimpleMsg('XY Curve object representing wattpf_curve does not exist or is not tied to InvControl.', 382);
- exit
- end;
+ if Fwattpf_curve = NIL then
+ begin
+ DoSimpleMsg(_('XY Curve object representing wattpf_curve does not exist or is not tied to InvControl.'), 382);
+ exit
+ end;
- if (ControlledElement[i].DSSClassName = 'PVSystem') then PVSys.WPmode := TRUE
- else Storage.WPmode := TRUE;
+ if (ControlledElement[i].DSSClassName = 'PVSystem') then
+ PVSys.WPmode := TRUE
+ else
+ Storage.WPmode := TRUE;
//Trigger from volt-var mode
if (((Abs(FPresentVpu[i] - FAvgpVpuPrior[i]) > FVoltageChangeTolerance) or
((Abs(Abs(QoutputVVpu[i]) - Abs(QDesireEndpu[i])) > FVarChangeTolerance))) or
(ActiveCircuit.Solution.ControlIteration = 1)) then
- begin
-
+ begin
// Resets DER state variable only if it has not converged yet
FWPOperation[i] := 0;
- Set_PendingChange(CHANGEVARLEVEL,i);
+ Set_PendingChange(CHANGEVARLEVEL, i);
with ActiveCircuit.Solution.DynaVars do
- ControlActionHandle := ActiveCircuit.ControlQueue.Push(intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ ControlActionHandle := ActiveCircuit.ControlQueue.Push(intHour, t + TimeDelay, PendingChange[i], 0, Self);
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+ControlledElement[i].QualifiedName,
- Format('**Ready to change var output due to watt-pf trigger in watt-pf mode**, Vavgpu= %.5g, VPriorpu=%.5g',
- [FPresentVpu[i],FAvgpVpuPrior[i]]));
- end;
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + ControlledElement[i].FullName,
+ Format('**Ready to change var output due to watt-pf trigger in watt-pf mode**, Vavgpu= %.5g, VPriorpu=%.5g',
+ [FPresentVpu[i], FAvgpVpuPrior[i]]));
+ end;
+ end
- else if ControlMode = WATTVAR then // watt-var control mode
- begin
+ else
+ if ControlMode = WATTVAR then // watt-var control mode
+ begin
// Sets internal variables of PVSystem/Storage.
// FWVOperation is a flag which indicates if watt-var function operates or not (-1=absorbing Q, 1=injecting Q, 0=No operation)
if ControlledElement[i].DSSClassName = 'PVSystem' then
- begin
- PVSys.Set_Variable(5,FVreg);
- PVSys.Set_Variable(12,FWVOperation[i]); //CHANGE HERE
- end
+ begin
+ PVSys.Set_Variable(5, FVreg);
+ PVSys.Set_Variable(12, FWVOperation[i]); //CHANGE HERE
+ end
else
- begin
- Storage.Set_Variable(14,FVreg);
- Storage.Set_Variable(16,FWVOperation[i]);
- end;
+ begin
+ Storage.Set_Variable(14, FVreg);
+ Storage.Set_Variable(16, FWVOperation[i]);
+ end;
// if inverter is off then exit
- if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then continue;
+ if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then
+ continue;
- if Length(Fwattvar_curvename) = 0 then
- begin
- DoSimpleMsg('XY Curve object representing wattvar_curve does not exist or is not tied to InvControl.', 382);
- exit
- end;
+ if Fwattvar_curve = NIL then
+ begin
+ DoSimpleMsg(_('XY Curve object representing wattvar_curve does not exist or is not tied to InvControl.'), 382);
+ exit
+ end;
- if (ControlledElement[i].DSSClassName = 'PVSystem') then PVSys.WVmode := TRUE
- else Storage.WVmode := TRUE;
+ if (ControlledElement[i].DSSClassName = 'PVSystem') then
+ PVSys.WVmode := TRUE
+ else
+ Storage.WVmode := TRUE;
//Trigger from volt-var mode
if (((Abs(FPresentVpu[i] - FAvgpVpuPrior[i]) > FVoltageChangeTolerance) or
((Abs(Abs(QoutputVVpu[i]) - Abs(QDesireEndpu[i])) > FVarChangeTolerance))) or
(ActiveCircuit.Solution.ControlIteration = 1)) then
- begin
-
+ begin
// Resets DER state variable only if it has not converged yet
FWVOperation[i] := 0;
- Set_PendingChange(CHANGEVARLEVEL,i);
+ Set_PendingChange(CHANGEVARLEVEL, i);
with ActiveCircuit.Solution.DynaVars do
- ControlActionHandle := ActiveCircuit.ControlQueue.Push(intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ ControlActionHandle := ActiveCircuit.ControlQueue.Push(intHour, t + TimeDelay, PendingChange[i], 0, Self);
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+ControlledElement[i].QualifiedName,
- Format('**Ready to change var output due to watt-var trigger in watt-var mode**, Vavgpu= %.5g, VPriorpu=%.5g',
- [FPresentVpu[i],FAvgpVpuPrior[i]]));
- end;
- end
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + ControlledElement[i].FullName,
+ Format('**Ready to change var output due to watt-var trigger in watt-var mode**, Vavgpu= %.5g, VPriorpu=%.5g',
+ [FPresentVpu[i], FAvgpVpuPrior[i]]));
+ end;
+ end
- else if ControlMode = DRC then // dynamic reactive current control mode
- begin
+ else
+ if ControlMode = DRC then // dynamic reactive current control mode
+ begin
// Sets internal variables of PVSystem/Storage.
// FDRCOperation is a flag which indicates if DRC function operates or not (-1=absorbing Q, 1=injecting Q, 0=No operation)
if ControlledElement[i].DSSClassName = 'PVSystem' then
begin
- PVSys.Set_Variable(5,FVreg);
- PVSys.Set_Variable(6,FDRCRollAvgWindow[i].Get_AvgVal / (basekV * 1000.0)); // save rolling average voltage in monitor
- PVSys.Set_Variable(9,FDRCOperation[i]);
+ PVSys.Set_Variable(5, FVreg);
+ PVSys.Set_Variable(6, FDRCRollAvgWindow[i].AvgVal / (basekV * 1000.0)); // save rolling average voltage in monitor
+ PVSys.Set_Variable(9, FDRCOperation[i]);
end
else
begin
- Storage.Set_Variable(14,FVreg);
- Storage.Set_Variable(15,FDRCRollAvgWindow[i].Get_AvgVal / (basekV * 1000.0)); // save rolling average voltage in monitor
- Storage.Set_Variable(18,FDRCOperation[i]);
+ Storage.Set_Variable(14, FVreg);
+ Storage.Set_Variable(15, FDRCRollAvgWindow[i].AvgVal / (basekV * 1000.0)); // save rolling average voltage in monitor
+ Storage.Set_Variable(18, FDRCOperation[i]);
end;
// if inverter is off then exit
- if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then continue;
+ if (FInverterON[i] = FALSE) and (FVarFollowInverter[i] = TRUE) then
+ continue;
//DRC triggers
- if(priorDRCRollAvgWindow[i] = 0.0) then
- begin
-
- if ((Abs(FPresentDRCVpu[i] - FAvgpDRCVpuPrior[i]) > FVoltageChangeTolerance)) then
- begin
-
+ if (priorDRCRollAvgWindow[i] = 0.0) then
+ begin
+ if ((Abs(FPresentDRCVpu[i] - FAvgpDRCVpuPrior[i]) > FVoltageChangeTolerance)) then
+ begin
// Resets DER state variable only if it has not converged yet
FDRCOperation[i] := 0;
- Set_PendingChange(CHANGEVARLEVEL,i);
+ Set_PendingChange(CHANGEVARLEVEL, i);
with ActiveCircuit.Solution.DynaVars do
- ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ ControlActionHandle := ActiveCircuit.ControlQueue.Push
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+ControlledElement[i].QualifiedName,
- Format('**Ready to change var output due to DRC trigger in DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g',
- [FPresentDRCVpu[i],FAvgpDRCVpuPrior[i]]));
- end;
- end;
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + ControlledElement[i].FullName,
+ Format('**Ready to change var output due to DRC trigger in DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g',
+ [FPresentDRCVpu[i], FAvgpDRCVpuPrior[i]]));
+ end;
+ end;
- if (ControlledElement[i].DSSClassName = 'PVSystem') then PVSys.DRCmode := TRUE
- else Storage.DRCmode := TRUE;
+ if (ControlledElement[i].DSSClassName = 'PVSystem') then
+ PVSys.DRCmode := TRUE
+ else
+ Storage.DRCmode := TRUE;
if ((Abs(FPresentDRCVpu[i] - FAvgpDRCVpuPrior[i]) > FVoltageChangeTolerance) or
- (Abs(Abs(QoutputDRCpu[i]) - Abs(QDesireEndpu[i])) > FVarChangeTolerance) or // TEMc; also tried checking against QDesireEndpu
- (ActiveCircuit.Solution.ControlIteration = 1)) then
- begin
-
- Set_PendingChange(CHANGEVARLEVEL,i);
- with ActiveCircuit.Solution.DynaVars do
+ (Abs(Abs(QoutputDRCpu[i]) - Abs(QDesireEndpu[i])) > FVarChangeTolerance) or // TEMc; also tried checking against QDesireEndpu
+ (ActiveCircuit.Solution.ControlIteration = 1)) then
+ begin
+ Set_PendingChange(CHANGEVARLEVEL, i);
+ with ActiveCircuit.Solution.DynaVars do
ControlActionHandle := ActiveCircuit.ControlQueue.Push
- (intHour, t + TimeDelay, PendingChange[i], 0, Self);
+ (intHour, t + TimeDelay, PendingChange[i], 0, Self);
- if ShowEventLog then AppendtoEventLog('InvControl.' + Self.Name+', '+ControlledElement[i].QualifiedName,
- Format('**Ready to change var output due to DRC trigger in DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g, QoutPU=%.3g, QDesiredEndpu=%.3g',
- [FPresentDRCVpu[i],FAvgpDRCVpuPrior[i],QoutputDRCpu[i],QDesireEndpu[i]]));
+ if ShowEventLog then
+ AppendtoEventLog('InvControl.' + Self.Name + ', ' + ControlledElement[i].FullName,
+ Format('**Ready to change var output due to DRC trigger in DRC mode**, Vavgpu= %.5g, VPriorpu=%.5g, QoutPU=%.3g, QDesiredEndpu=%.3g',
+ [FPresentDRCVpu[i], FAvgpDRCVpuPrior[i], QoutputDRCpu[i], QDesireEndpu[i]]));
- end;
- end;
- end;
- end;
-
- end;
-
-procedure TInvControl2Obj.InitPropertyValues(ArrayOffset: Integer);
- begin
- PropertyValue[1] := ''; //PVSystem/Storage list
- PropertyValue[2] := 'VOLTVAR'; // initial mode
- PropertyValue[3] := ''; // initial combination mode
- PropertyValue[4] := '';
- PropertyValue[5] := '0';
- PropertyValue[6] := 'rated';
- PropertyValue[7] := '0s';
-
- PropertyValue[8] := 'NONE'; // voltwatt_curve
-
- PropertyValue[9] := '0.95'; //'DbVMin';
- PropertyValue[10] := '1.05'; // 'DbVMax';
- PropertyValue[11] := '0.1'; // 'ArGraLowV';
- PropertyValue[12] := '0.1'; // 'ArGraHiV';
- PropertyValue[13] := '0s'; // 'Rollingavgwindowlen';
- PropertyValue[14] := FloatToStr(FLAGDELTAQ); // FdeltaQFactor
- PropertyValue[15] := '0.0001'; //VoltageChangeTolerance
- PropertyValue[16] := '0.025'; // Varchangetolerance
- PropertyValue[17] := 'PMPPPU'; // Voltwatt y axis units
- PropertyValue[18] := 'INACTIVE'; //rate of change limit
- PropertyValue[19] := '0.0'; // LPF tau constant, in seconds
- PropertyValue[20] := '-1.0'; // Rise/fall Limit
- PropertyValue[21] := FloatToStr(FLAGDELTAP); // FdeltaPFactor
- PropertyValue[22] := 'yes'; // show event log
- PropertyValue[23] := 'VARAVAL'; // y-axis reference (and power precedence) for volt-var
- PropertyValue[24] := '0.01';
-
- PropertyValue[28] := 'NONE'; // voltwattCH_curve
-
- inherited InitPropertyValues(NumPropsThisClass);
-
- end;
-
-function TInvControl2Obj.MakeDERList:Boolean;
-
- VAR
- PVSysClass : TDSSClass;
- StorageClass : TDSSClass;
- PVSys : TPVSystemObj;
- Storage : TStorageObj;
- DERElem : TPCElement;
- i,j : Integer;
-
- begin
+ end;
+ end;
+ end;
+ end;
+end;
+function TInvControl2Obj.MakeDERList: Boolean;
+var
+ PVSysClass: TDSSClass;
+ StorageClass: TDSSClass;
+ PVSys: TPVSystemObj = NIL;
+ Storage: TStorageObj = NIL;
+ DERElem: TPCElement;
+ i, j: Integer;
+begin
Result := FALSE;
PVSysClass := GetDSSClassPtr(DSS, 'PVSystem');
StorageClass := GetDSSClassPtr(DSS, 'Storage');
if FListSize > 0 then
- begin // Name list is defined - Use it
-
- SetLength(CondOffset,FListSize+1);
- SetLength(cBuffer,FListSize+1,7); // assuming no more than 6 conductors
- SetLength(ControlledElement,FListSize+1); // Use this as the main pointer to PVSystem and Storage Elements
- SetLength(FAvgpVpuPrior, FListSize+1);
- SetLength(FAvgpDRCVpuPrior, FListSize+1);
- SetLength(FPresentVpu, FListSize+1);
- SetLength(FPresentDRCVpu, FListSize+1);
- SetLength(NPhasesDER,FListSize+1);
- SetLength(NCondsDER,FListSize+1);
- SetLength(FPendingChange,FListSize+1);
- SetLength(QDesiredVV,FListSize+1);
- SetLength(QDesiredWP,FListSize+1);
- SetLength(QDesiredWV,FListSize+1);
- SetLength(QDesiredAVR,FListSize+1);
- SetLength(QOld,FListSize+1);
- SetLength(QOldVV,FListSize+1);
- SetLength(QOldAVR,FListSize+1);
- SetLength(QOldDRC,FListSize+1);
- SetLength(QOldVVDRC,FListSize+1);
- SetLength(QDesiredDRC,FListSize+1);
- SetLength(QDesiredVVDRC,FListSize+1);
- SetLength(QHeadroom,FListSize+1);
- SetLength(QHeadroomNeg,FListSize+1);
- SetLength(PBase,FListSize+1);
- SetLength(Qoutputpu,FListSize+1);
- SetLength(QoutputVVpu,FListSize+1);
- SetLength(QoutputAVRpu,FListSize+1);
- SetLength(QoutputDRCpu,FListSize+1);
- SetLength(QoutputVVDRCpu,FListSize+1);
- SetLength(QDesireEndpu,FListSize+1);
- SetLength(QDesireVVpu, FListSize+1);
- SetLength(QDesireWPpu, FListSize+1);
- SetLength(QDesireWVpu, FListSize+1);
- SetLength(QDesireAVRpu, FListSize+1);
- SetLength(QDesireLimitedpu, FListSize+1);
- SetLength(QDesireOptionpu, FListSize+1);
- SetLength(PLimitEndpu,FListSize+1);
- SetLength(PLimitVWpu, FListSize+1);
- SetLength(PLimitLimitedpu, FListSize+1);
- SetLength(PLimitOptionpu, FListSize+1);
- SetLength(QDesireDRCpu,FListSize+1);
- SetLength(deltaVDynReac,FListSize+1);
- SetLength(PLimitVW,FListSize+1);
- SetLength(POldVWpu,FListSize+1);
- SetLength(FdeltaQFactor,FListSize+1);
- SetLength(FdeltaPFactor,FListSize+1);
- SetLength(DeltaV_old,FListSize+1);
- SetLength(FVpuSolution,FListSize+1,2+1);
- SetLength(FRollAvgWindow,FListSize+1);
- SetLength(FDRCRollAvgWindow, FListSize+1);
- SetLength(FDRCRollAvgWindowpu, FListSize+1);
- SetLength(priorRollAvgWindow,FListSize+1);
- SetLength(priorDRCRollAvgWindow,FListSize+1);
- SetLength(FlagChangeCurve,FListSize+1);
- SetLength(FActiveVVCurve, FListSize+1);
- SetLength(FPriorWattspu, FListSize+1);
- SetLength(FPriorWatts, FListSize+1);
- SetLength(FPriorPLimitOptionpu, FListSize+1);
- SetLength(FPriorQDesireOptionpu, FListSize+1);
- SetLength(kW_out_desiredpu, FListSize+1);
- SetLength(kW_out_desired, FListSize+1);
- SetLength(FPriorvarspu, FListSize+1);
- SetLength(FPriorvars, FListSize+1);
- SetLength(FFlagVWOperates, FListSize+1);
- SetLength(FVVOperation, FListSize+1);
- SetLength(FAVROperation, FListSize+1);
- SetLength(FWPOperation, FListSize+1);
- SetLength(FWVOperation, FListSize+1);
- SetLength(FVWOperation, FListSize+1);
- SetLength(FDRCOperation, FListSize+1);
- SetLength(FVVDRCOperation, FListSize+1);
- SetLength(FVBase, FListSize+1);
- SetLength(FVarFollowInverter, FListSize+1);
- SetLength(FInverterON, FListSize+1);
- SetLength(FpresentkW, FListSize+1);
- SetLength(FkVARating, FListSize+1);
- SetLength(Fpresentkvar, FListSize+1);
- SetLength(FkvarLimit, FListSize+1);
- SetLength(FkvarLimitNeg, FListSize+1);
- SetLength(FCurrentkvarLimit, FListSize+1);
- SetLength(FCurrentkvarLimitNeg, FListSize+1);
- SetLength(FDCkWRated, FListSize+1);
- SetLength(FpctDCkWRated, FListSize+1);
- SetLength(FEffFactor, FListSize+1);
- SetLength(FDCkW, FListSize+1);
- SetLength(FPPriority, FListSize+1);
- SetLength(DQDV, FListSize+1);
- SetLength(Fv_setpointLimited, FListSize+1);
- SetLength(FAvgpAVRVpuPrior, FListSize+1);
+ begin // Name list is defined - Use it
+
+ SetLength(CondOffset, FListSize + 1);
+ SetLength(cBuffer, FListSize + 1, 7); // assuming no more than 6 conductors
+ SetLength(ControlledElement, FListSize + 1); // Use this as the main pointer to PVSystem and Storage Elements
+ SetLength(FAvgpVpuPrior, FListSize + 1);
+ SetLength(FAvgpDRCVpuPrior, FListSize + 1);
+ SetLength(FPresentVpu, FListSize + 1);
+ SetLength(FPresentDRCVpu, FListSize + 1);
+ SetLength(NPhasesDER, FListSize + 1);
+ SetLength(NCondsDER, FListSize + 1);
+ SetLength(FPendingChange, FListSize + 1);
+ SetLength(QDesiredVV, FListSize + 1);
+ SetLength(QDesiredWP, FListSize + 1);
+ SetLength(QDesiredWV, FListSize + 1);
+ SetLength(QDesiredAVR, FListSize + 1);
+ SetLength(QOld, FListSize + 1);
+ SetLength(QOldVV, FListSize + 1);
+ SetLength(QOldAVR, FListSize + 1);
+ SetLength(QOldDRC, FListSize + 1);
+ SetLength(QOldVVDRC, FListSize + 1);
+ SetLength(QDesiredDRC, FListSize + 1);
+ SetLength(QDesiredVVDRC, FListSize + 1);
+ SetLength(QHeadroom, FListSize + 1);
+ SetLength(QHeadroomNeg, FListSize + 1);
+ SetLength(PBase, FListSize + 1);
+ SetLength(Qoutputpu, FListSize + 1);
+ SetLength(QoutputVVpu, FListSize + 1);
+ SetLength(QoutputAVRpu, FListSize + 1);
+ SetLength(QoutputDRCpu, FListSize + 1);
+ SetLength(QoutputVVDRCpu, FListSize + 1);
+ SetLength(QDesireEndpu, FListSize + 1);
+ SetLength(QDesireVVpu, FListSize + 1);
+ SetLength(QDesireWPpu, FListSize + 1);
+ SetLength(QDesireWVpu, FListSize + 1);
+ SetLength(QDesireAVRpu, FListSize + 1);
+ SetLength(QDesireLimitedpu, FListSize + 1);
+ SetLength(QDesireOptionpu, FListSize + 1);
+ SetLength(PLimitEndpu, FListSize + 1);
+ SetLength(PLimitVWpu, FListSize + 1);
+ SetLength(PLimitLimitedpu, FListSize + 1);
+ SetLength(PLimitOptionpu, FListSize + 1);
+ SetLength(QDesireDRCpu, FListSize + 1);
+ SetLength(deltaVDynReac, FListSize + 1);
+ SetLength(PLimitVW, FListSize + 1);
+ SetLength(POldVWpu, FListSize + 1);
+ SetLength(FdeltaQFactor, FListSize + 1);
+ SetLength(FdeltaPFactor, FListSize + 1);
+ SetLength(DeltaV_old, FListSize + 1);
+ SetLength(FVpuSolution, FListSize + 1, 2 + 1);
+ SetLength(FRollAvgWindow, FListSize + 1);
+ SetLength(FDRCRollAvgWindow, FListSize + 1);
+ SetLength(FDRCRollAvgWindowpu, FListSize + 1);
+ SetLength(priorRollAvgWindow, FListSize + 1);
+ SetLength(priorDRCRollAvgWindow, FListSize + 1);
+ SetLength(FlagChangeCurve, FListSize + 1);
+ SetLength(FActiveVVCurve, FListSize + 1);
+ SetLength(FPriorWattspu, FListSize + 1);
+ SetLength(FPriorWatts, FListSize + 1);
+ SetLength(FPriorPLimitOptionpu, FListSize + 1);
+ SetLength(FPriorQDesireOptionpu, FListSize + 1);
+ SetLength(kW_out_desiredpu, FListSize + 1);
+ SetLength(kW_out_desired, FListSize + 1);
+ SetLength(FPriorvarspu, FListSize + 1);
+ SetLength(FPriorvars, FListSize + 1);
+ SetLength(FFlagVWOperates, FListSize + 1);
+ SetLength(FVVOperation, FListSize + 1);
+ SetLength(FAVROperation, FListSize + 1);
+ SetLength(FWPOperation, FListSize + 1);
+ SetLength(FWVOperation, FListSize + 1);
+ SetLength(FVWOperation, FListSize + 1);
+ SetLength(FDRCOperation, FListSize + 1);
+ SetLength(FVVDRCOperation, FListSize + 1);
+ SetLength(FVBase, FListSize + 1);
+ SetLength(FVarFollowInverter, FListSize + 1);
+ SetLength(FInverterON, FListSize + 1);
+ SetLength(FpresentkW, FListSize + 1);
+ SetLength(FkVARating, FListSize + 1);
+ SetLength(Fpresentkvar, FListSize + 1);
+ SetLength(FkvarLimit, FListSize + 1);
+ SetLength(FkvarLimitNeg, FListSize + 1);
+ SetLength(FCurrentkvarLimit, FListSize + 1);
+ SetLength(FCurrentkvarLimitNeg, FListSize + 1);
+ SetLength(FDCkWRated, FListSize + 1);
+ SetLength(FpctDCkWRated, FListSize + 1);
+ SetLength(FEffFactor, FListSize + 1);
+ SetLength(FDCkW, FListSize + 1);
+ SetLength(FPPriority, FListSize + 1);
+ SetLength(DQDV, FListSize + 1);
+ SetLength(Fv_setpointLimited, FListSize + 1);
+ SetLength(FAvgpAVRVpuPrior, FListSize + 1);
for i := 1 to FListSize do
- begin
- if StripExtension(LowerCase(FDERNameList.Strings[i-1])) = 'pvsystem' then
- begin
- PVSys := PVSysClass.Find(StripClassName(FDERNameList.Strings[i-1]));
-
- If Assigned(PVSys) Then Begin
- If PVSys.Enabled Then FDERPointerList.New := PVSys
- End
- Else Begin
- DoSimpleMsg('Error: PVSystem Element "' + FDERNameList.Strings[i-1] + '" not found.', 14403);
+ begin
+ if StripExtension(AnsiLowerCase(DERNameList.Strings[i - 1])) = 'pvsystem' then
+ begin
+ PVSys := PVSysClass.Find(StripClassName(DERNameList.Strings[i - 1]));
+
+ if Assigned(PVSys) then
+ begin
+ if PVSys.Enabled then
+ FDERPointerList.Add(PVSys)
+ end
+ else
+ begin
+ DoSimpleMsg('Error: PVSystem Element "%s" not found.', [DERNameList.Strings[i - 1]], 14403);
Exit;
- End;
-
- end
- else if StripExtension(LowerCase(FDERNameList.Strings[i-1])) = 'storage' then
- begin
- Storage := StorageClass.Find(StripClassName(FDERNameList.Strings[i-1]));
-
- If Assigned(Storage) Then Begin
- If Storage.Enabled Then FDERPointerList.New := Storage
- End
- Else Begin
- DoSimpleMsg('Error: Storage Element "' + FDERNameList.Strings[i-1] + '" not found.', 14403);
+ end;
+
+ end
+ else
+ if StripExtension(AnsiLowerCase(DERNameList.Strings[i - 1])) = 'storage' then
+ begin
+ Storage := StorageClass.Find(StripClassName(DERNameList.Strings[i - 1]));
+
+ if Assigned(Storage) then
+ begin
+ if Storage.Enabled then
+ FDERPointerList.Add(Storage)
+ end
+ else
+ begin
+ DoSimpleMsg('Error: Storage Element "%s" not found.', [DERNameList.Strings[i - 1]], 14403);
Exit;
- End;
+ end;
- end
- end;
+ end
+ end;
- end
+ end
else
- begin
+ begin
{Search through the entire circuit for enabled PVSystem and Storage objects and add them to the list}
// Adding PVSystem elements
for i := 1 to PVSysClass.ElementCount do
- begin
- PVSys := PVSysClass.ElementList.Get(i);
- if PVSys.Enabled then FDERPointerList.New := PVSys;
- FDERNameList.Add(PVSys.QualifiedName);
- end;
+ begin
+ PVSys := PVSysClass.ElementList.Get(i);
+ if PVSys.Enabled then
+ FDERPointerList.Add(PVSys);
+ DERNameList.Add(PVSys.FullName);
+ end;
// Adding Storage elements
for i := 1 to StorageClass.ElementCount do
- begin
- Storage := StorageClass.ElementList.Get(i);
- if Storage.Enabled then FDERPointerList.New := Storage;
- FDERNameList.Add(Storage.QualifiedName);
- end;
+ begin
+ Storage := StorageClass.ElementList.Get(i);
+ if Storage.Enabled then
+ FDERPointerList.Add(Storage);
+ DERNameList.Add(Storage.FullName);
+ end;
FListSize := FDERPointerList.Count;
- SetLength(ControlledElement,FListSize+1);
- SetLength(FAvgpVpuPrior, FListSize+1);
- SetLength(FAvgpDRCVpuPrior, FListSize+1);
- SetLength(FPresentVpu, FListSize+1);
- SetLength(FPresentDRCVpu, FListSize+1);
- SetLength(NPhasesDER,FListSize+1);
- SetLength(NCondsDER,FListSize+1);
- SetLength(CondOffset,FListSize+1);
- SetLength(cBuffer,FListSize+1,7); // assuming no more than 6 conductors
- SetLength(FPendingChange,FListSize+1);
- SetLength(QDesiredVV,FListSize+1);
- SetLength(QDesiredWP,FListSize+1);
- SetLength(QDesiredWV,FListSize+1);
- SetLength(QDesiredAVR,FListSize+1);
- SetLength(QOld,FListSize+1);
- SetLength(QOldVV,FListSize+1);
- SetLength(QOldAVR,FListSize+1);
- SetLength(QOldDRC,FListSize+1);
- SetLength(QOldVVDRC,FListSize+1);
- SetLength(QDesiredDRC,FListSize+1);
- SetLength(QDesiredVVDRC,FListSize+1);
- SetLength(QHeadroom,FListSize+1);
- SetLength(QHeadroomNeg,FListSize+1);
- SetLength(PBase,FListSize+1);
- SetLength(Qoutputpu,FListSize+1);
- SetLength(QoutputVVpu,FListSize+1);
- SetLength(QoutputAVRpu,FListSize+1);
- SetLength(QoutputDRCpu,FListSize+1);
- SetLength(QoutputVVDRCpu,FListSize+1);
- SetLength(QDesireEndpu,FListSize+1);
- SetLength(QDesireVVpu, FListSize+1);
- SetLength(QDesireWPpu, FListSize+1);
- SetLength(QDesireWVpu, FListSize+1);
- SetLength(QDesireAVRpu, FListSize+1);
- SetLength(QDesireLimitedpu, FListSize+1);
- SetLength(QDesireOptionpu, FListSize+1);
- SetLength(PLimitEndpu,FListSize+1);
- SetLength(PLimitVWpu, FListSize+1);
- SetLength(PLimitLimitedpu, FListSize+1);
- SetLength(PLimitOptionpu, FListSize+1);
- SetLength(QDesireDRCpu,FListSize+1);
- SetLength(PLimitVW,FListSize+1);
- SetLength(POldVWpu,FListSize+1);
- SetLength(FdeltaQFactor,FListSize+1);
- SetLength(FdeltaPFactor,FListSize+1);
- SetLength(DeltaV_old,FListSize+1);
- SetLength(FRollAvgWindow,FListSize+1);
- SetLength(FDRCRollAvgWindow, FListSize+1);
- SetLength(FDRCRollAvgWindowpu, FListSize+1);
- SetLength(deltaVDynReac,FListSize+1);
- SetLength(priorRollAvgWindow,FListSize+1);
- SetLength(priorDRCRollAvgWindow,FListSize+1);
- SetLength(FVpuSolution,FListSize+1,2+1);
- SetLength(FlagChangeCurve,FListSize+1);
- SetLength(FActiveVVCurve, FListSize+1);
- SetLength(FPriorWattspu, FListSize+1);
- SetLength(FPriorWatts, FListSize+1);
- SetLength(FPriorPLimitOptionpu, FListSize+1);
- SetLength(FPriorQDesireOptionpu, FListSize+1);
- SetLength(kW_out_desiredpu, FListSize+1);
- SetLength(kW_out_desired, FListSize+1);
- SetLength(FPriorvarspu, FListSize+1);
- SetLength(FPriorvars, FListSize+1);
- SetLength(FFlagVWOperates, FListSize+1);
- SetLength(FVVOperation, FListSize+1);
- SetLength(FAVROperation, FListSize+1);
- SetLength(FWVOperation, FListSize+1);
- SetLength(FWPOperation, FListSize+1);
- SetLength(FVWOperation, FListSize+1);
- SetLength(FDRCOperation, FListSize+1);
- SetLength(FVVDRCOperation, FListSize+1);
- SetLength(FVBase, FListSize+1);
- SetLength(FVarFollowInverter, FListSize+1);
- SetLength(FInverterON, FListSize+1);
- SetLength(FpresentkW, FListSize+1);
- SetLength(FkVARating, FListSize+1);
- SetLength(Fpresentkvar, FListSize+1);
- SetLength(FkvarLimit, FListSize+1);
- SetLength(FkvarLimitNeg, FListSize+1);
- SetLength(FCurrentkvarLimit, FListSize+1);
- SetLength(FCurrentkvarLimitNeg, FListSize+1);
- SetLength(FDCkWRated, FListSize+1);
- SetLength(FpctDCkWRated, FListSize+1);
- SetLength(FEffFactor, FListSize+1);
- SetLength(FDCkW, FListSize+1);
- SetLength(FPPriority, FListSize+1);
- SetLength(DQDV, FListSize+1);
- SetLength(Fv_setpointLimited, FListSize+1);
- SetLength(FAvgpAVRVpuPrior, FListSize+1);
-
-
- end; {else}
+ SetLength(ControlledElement, FListSize + 1);
+ SetLength(FAvgpVpuPrior, FListSize + 1);
+ SetLength(FAvgpDRCVpuPrior, FListSize + 1);
+ SetLength(FPresentVpu, FListSize + 1);
+ SetLength(FPresentDRCVpu, FListSize + 1);
+ SetLength(NPhasesDER, FListSize + 1);
+ SetLength(NCondsDER, FListSize + 1);
+ SetLength(CondOffset, FListSize + 1);
+ SetLength(cBuffer, FListSize + 1, 7); // assuming no more than 6 conductors
+ SetLength(FPendingChange, FListSize + 1);
+ SetLength(QDesiredVV, FListSize + 1);
+ SetLength(QDesiredWP, FListSize + 1);
+ SetLength(QDesiredWV, FListSize + 1);
+ SetLength(QDesiredAVR, FListSize + 1);
+ SetLength(QOld, FListSize + 1);
+ SetLength(QOldVV, FListSize + 1);
+ SetLength(QOldAVR, FListSize + 1);
+ SetLength(QOldDRC, FListSize + 1);
+ SetLength(QOldVVDRC, FListSize + 1);
+ SetLength(QDesiredDRC, FListSize + 1);
+ SetLength(QDesiredVVDRC, FListSize + 1);
+ SetLength(QHeadroom, FListSize + 1);
+ SetLength(QHeadroomNeg, FListSize + 1);
+ SetLength(PBase, FListSize + 1);
+ SetLength(Qoutputpu, FListSize + 1);
+ SetLength(QoutputVVpu, FListSize + 1);
+ SetLength(QoutputAVRpu, FListSize + 1);
+ SetLength(QoutputDRCpu, FListSize + 1);
+ SetLength(QoutputVVDRCpu, FListSize + 1);
+ SetLength(QDesireEndpu, FListSize + 1);
+ SetLength(QDesireVVpu, FListSize + 1);
+ SetLength(QDesireWPpu, FListSize + 1);
+ SetLength(QDesireWVpu, FListSize + 1);
+ SetLength(QDesireAVRpu, FListSize + 1);
+ SetLength(QDesireLimitedpu, FListSize + 1);
+ SetLength(QDesireOptionpu, FListSize + 1);
+ SetLength(PLimitEndpu, FListSize + 1);
+ SetLength(PLimitVWpu, FListSize + 1);
+ SetLength(PLimitLimitedpu, FListSize + 1);
+ SetLength(PLimitOptionpu, FListSize + 1);
+ SetLength(QDesireDRCpu, FListSize + 1);
+ SetLength(PLimitVW, FListSize + 1);
+ SetLength(POldVWpu, FListSize + 1);
+ SetLength(FdeltaQFactor, FListSize + 1);
+ SetLength(FdeltaPFactor, FListSize + 1);
+ SetLength(DeltaV_old, FListSize + 1);
+ SetLength(FRollAvgWindow, FListSize + 1);
+ SetLength(FDRCRollAvgWindow, FListSize + 1);
+ SetLength(FDRCRollAvgWindowpu, FListSize + 1);
+ SetLength(deltaVDynReac, FListSize + 1);
+ SetLength(priorRollAvgWindow, FListSize + 1);
+ SetLength(priorDRCRollAvgWindow, FListSize + 1);
+ SetLength(FVpuSolution, FListSize + 1, 2 + 1);
+ SetLength(FlagChangeCurve, FListSize + 1);
+ SetLength(FActiveVVCurve, FListSize + 1);
+ SetLength(FPriorWattspu, FListSize + 1);
+ SetLength(FPriorWatts, FListSize + 1);
+ SetLength(FPriorPLimitOptionpu, FListSize + 1);
+ SetLength(FPriorQDesireOptionpu, FListSize + 1);
+ SetLength(kW_out_desiredpu, FListSize + 1);
+ SetLength(kW_out_desired, FListSize + 1);
+ SetLength(FPriorvarspu, FListSize + 1);
+ SetLength(FPriorvars, FListSize + 1);
+ SetLength(FFlagVWOperates, FListSize + 1);
+ SetLength(FVVOperation, FListSize + 1);
+ SetLength(FAVROperation, FListSize + 1);
+ SetLength(FWVOperation, FListSize + 1);
+ SetLength(FWPOperation, FListSize + 1);
+ SetLength(FVWOperation, FListSize + 1);
+ SetLength(FDRCOperation, FListSize + 1);
+ SetLength(FVVDRCOperation, FListSize + 1);
+ SetLength(FVBase, FListSize + 1);
+ SetLength(FVarFollowInverter, FListSize + 1);
+ SetLength(FInverterON, FListSize + 1);
+ SetLength(FpresentkW, FListSize + 1);
+ SetLength(FkVARating, FListSize + 1);
+ SetLength(Fpresentkvar, FListSize + 1);
+ SetLength(FkvarLimit, FListSize + 1);
+ SetLength(FkvarLimitNeg, FListSize + 1);
+ SetLength(FCurrentkvarLimit, FListSize + 1);
+ SetLength(FCurrentkvarLimitNeg, FListSize + 1);
+ SetLength(FDCkWRated, FListSize + 1);
+ SetLength(FpctDCkWRated, FListSize + 1);
+ SetLength(FEffFactor, FListSize + 1);
+ SetLength(FDCkW, FListSize + 1);
+ SetLength(FPPriority, FListSize + 1);
+ SetLength(DQDV, FListSize + 1);
+ SetLength(Fv_setpointLimited, FListSize + 1);
+ SetLength(FAvgpAVRVpuPrior, FListSize + 1);
+
+
+ end; {else}
//Initialize arrays
for i := 1 to FlistSize do
- begin
-
- if StripExtension(LowerCase(FDERNameList.Strings[i-1])) = 'pvsystem' then
- begin
- PVSys := PVSysClass.Find(StripClassName(FDERNameList.Strings[i-1]));
- if (PVSys <> nil) then
- DERElem := TPCElement(PVSys)
- end
+ begin
+ if StripExtension(AnsiLowerCase(DERNameList.Strings[i - 1])) = 'pvsystem' then
+ begin
+ PVSys := PVSysClass.Find(StripClassName(DERNameList.Strings[i - 1]));
+ if (PVSys <> NIL) then
+ DERElem := TPCElement(PVSys)
+ end
else
- begin
- Storage := StorageClass.Find(StripClassName(FDERNameList.Strings[i-1]));
- if (Storage <> nil) then
- DERElem := TPCElement(Storage)
- end;
+ begin
+ Storage := StorageClass.Find(StripClassName(DERNameList.Strings[i - 1]));
+ if (Storage <> NIL) then
+ DERElem := TPCElement(Storage)
+ end;
- for j := 1 to 6 do cBuffer[i,j] := cZERO;
+ for j := 1 to 6 do
+ cBuffer[i, j] := cZERO;
Set_NTerms(DERElem.NTerms);
- CondOffset[i] := 0;
- NPhasesDER[i] := DERElem.NPhases;
- NCondsDER[i] := DERElem.NConds;
- FAvgpVpuPrior[i] := 0.0;
- FAvgpDRCVpuPrior[i] := 0.0;
- FPresentVpu[i] := 0.0;
- FPresentDRCVpu[i] := 0.0;
- QDesiredVV[i] := 0.0;
- QDesiredWP[i] := 0.0;
- QDesiredWV[i] := 0.0;
- QOld[i] := -1.0;
- QOldVV[i] := -1.0;
- QOldAVR[i] := -PVSys.kvarLimitNeg / 2.0;
- QOldDRC[i] := -1.0;
- QOldVVDRC[i] := -1.0;
- QDesiredDRC[i] := 0.0;
- QDesiredVVDRC[i] := 0.0;
- PLimitVW[i] := 0.0;
- POldVWpu[i] := 0.0;
- PBase[i] := 0.0;
- QHeadroom[i] := 0.0;
- QHeadroomNeg[i] := 0.0;
- Qoutputpu[i] := 0.0;
- QoutputVVpu[i] := 0.0;
+ CondOffset[i] := 0;
+ NPhasesDER[i] := DERElem.NPhases;
+ NCondsDER[i] := DERElem.NConds;
+ FAvgpVpuPrior[i] := 0.0;
+ FAvgpDRCVpuPrior[i] := 0.0;
+ FPresentVpu[i] := 0.0;
+ FPresentDRCVpu[i] := 0.0;
+ QDesiredVV[i] := 0.0;
+ QDesiredWP[i] := 0.0;
+ QDesiredWV[i] := 0.0;
+ QOld[i] := -1.0;
+ QOldVV[i] := -1.0;
+ if PVSys = nil then
+ QOldAVR[i] := 0.0
+ else
+ QOldAVR[i] := -PVSys.kvarLimitNeg / 2.0;
+ QOldDRC[i] := -1.0;
+ QOldVVDRC[i] := -1.0;
+ QDesiredDRC[i] := 0.0;
+ QDesiredVVDRC[i] := 0.0;
+ PLimitVW[i] := 0.0;
+ POldVWpu[i] := 0.0;
+ PBase[i] := 0.0;
+ QHeadroom[i] := 0.0;
+ QHeadroomNeg[i] := 0.0;
+ Qoutputpu[i] := 0.0;
+ QoutputVVpu[i] := 0.0;
QoutputAVRpu[i] := 0.0;
- QoutputDRCpu[i] := 0.0;
- QoutputVVDRCpu[i] := 0.0;
- QDesireEndpu[i] := 0.0;
- QDesireVVpu[i] := 0.0;
- QDesireWPpu[i] := 0.0;
- QDesireWVpu[i] := 0.0;
+ QoutputDRCpu[i] := 0.0;
+ QoutputVVDRCpu[i] := 0.0;
+ QDesireEndpu[i] := 0.0;
+ QDesireVVpu[i] := 0.0;
+ QDesireWPpu[i] := 0.0;
+ QDesireWVpu[i] := 0.0;
QDesireAVRpu[i] := 0.0;
- QDesireLimitedpu[i] := 0.0;
- QDesireOptionpu[i] := 0.0;
- PLimitVWpu[i] := 0.0;
- PLimitLimitedpu[i] := 0.0;
- PLimitEndpu[i] := 0.0;
- PLimitOptionpu[i] := 0.0;
- QDesireDRCpu[i] := 0.0;
- FRollAvgWindow[i] := TRollAvgWindow.Create;
- FDRCRollAvgWindow[i] := TRollAvgWindow.Create;
-
- FdeltaQFactor[i] := DELTAQDEFAULT;
- FdeltaPFactor[i] := DELTAPDEFAULT;
- DeltaV_old[i] := -1.0;
-
- deltaVDynReac[i] := 0.0;
- FlagChangeCurve[i] := False;
- FActiveVVCurve[i] := 1;
- priorRollAvgWindow[i] := 0.0;
- priorDRCRollAvgWindow[i] := 0.0;
- FPriorWattspu[i] := 0.0;
- FPriorWatts[i] := 0.0;
- FPriorPLimitOptionpu[i] := 0.0;
- FPriorQDesireOptionpu[i] := 0.0;
- kW_out_desiredpu[i] := 0.0;
- kW_out_desired[i] := 0.0;
- FPriorvarspu[i] := 0.0;
- FPriorvars[i] := 0.0;
-
- FFlagVWOperates[i] := False;
-
- FVVOperation[i] := 0.0;
- FVWOperation[i] := 0.0;
- FDRCOperation[i] := 0.0;
- FVVDRCOperation[i] := 0.0;
- FWPOperation[i] := 0.0;
- FWVOperation[i] := 0.0;
+ QDesireLimitedpu[i] := 0.0;
+ QDesireOptionpu[i] := 0.0;
+ PLimitVWpu[i] := 0.0;
+ PLimitLimitedpu[i] := 0.0;
+ PLimitEndpu[i] := 0.0;
+ PLimitOptionpu[i] := 0.0;
+ QDesireDRCpu[i] := 0.0;
+ FRollAvgWindow[i] := TRollAvgWindow.Create();
+ FDRCRollAvgWindow[i] := TRollAvgWindow.Create();
+
+ FdeltaQFactor[i] := DELTAQDEFAULT;
+ FdeltaPFactor[i] := DELTAPDEFAULT;
+ DeltaV_old[i] := -1.0;
+
+ deltaVDynReac[i] := 0.0;
+ FlagChangeCurve[i] := FALSE;
+ FActiveVVCurve[i] := 1;
+ priorRollAvgWindow[i] := 0.0;
+ priorDRCRollAvgWindow[i] := 0.0;
+ FPriorWattspu[i] := 0.0;
+ FPriorWatts[i] := 0.0;
+ FPriorPLimitOptionpu[i] := 0.0;
+ FPriorQDesireOptionpu[i] := 0.0;
+ kW_out_desiredpu[i] := 0.0;
+ kW_out_desired[i] := 0.0;
+ FPriorvarspu[i] := 0.0;
+ FPriorvars[i] := 0.0;
+
+ FFlagVWOperates[i] := FALSE;
+
+ FVVOperation[i] := 0.0;
+ FVWOperation[i] := 0.0;
+ FDRCOperation[i] := 0.0;
+ FVVDRCOperation[i] := 0.0;
+ FWPOperation[i] := 0.0;
+ FWVOperation[i] := 0.0;
FAVROperation[i] := 0.0;
- for j := 1 to 2 do FVpuSolution[i,j] := 0.0;
-
- FPendingChange[i] := NONE;
-
- FVbase[i] := 0.0;
- FVarFollowInverter[i] := False;
- FInverterON[i] := True;
- FpresentkW[i] := 0.0;
- FkVARating[i] := 0.0;
- Fpresentkvar[i] := 0.0;
- FkvarLimit[i] := 0.0;
- FkvarLimitNeg[i] := 0.0;
- FCurrentkvarLimit[i] := 0.0;
- FCurrentkvarLimitNeg[i] := 0.0;
- FDCkWRated[i] := 0.0;
- FpctDCkWRated[i] := 0.0;
- FEffFactor[i] := 0.0;
- FDCkW[i] := 0.0;
- FPPriority[i] := False;
+ for j := 1 to 2 do
+ FVpuSolution[i, j] := 0.0;
+
+ FPendingChange[i] := NONE;
+
+ FVbase[i] := 0.0;
+ FVarFollowInverter[i] := FALSE;
+ FInverterON[i] := TRUE;
+ FpresentkW[i] := 0.0;
+ FkVARating[i] := 0.0;
+ Fpresentkvar[i] := 0.0;
+ FkvarLimit[i] := 0.0;
+ FkvarLimitNeg[i] := 0.0;
+ FCurrentkvarLimit[i] := 0.0;
+ FCurrentkvarLimitNeg[i] := 0.0;
+ FDCkWRated[i] := 0.0;
+ FpctDCkWRated[i] := 0.0;
+ FEffFactor[i] := 0.0;
+ FDCkW[i] := 0.0;
+ FPPriority[i] := FALSE;
DQDV[i] := 0.0;
Fv_setpointLimited[i] := 0.0;
FAvgpAVRVpuPrior[i] := 0.0;
+ end; {for}
-
- end; {for}
-
- RecalcElementData();
- if FDERPointerList.Count>0 then Result := TRUE;
- end;
+ // RecalcElementData(); -- MakeDERList is only called FROM RecalcElementData, no need to call it again.
+ if FDERPointerList.Count > 0 then
+ Result := TRUE;
+end;
procedure TInvControl2Obj.Reset;
- begin
+begin
// inherited;
- end;
-
-function TInvControl2.GetXYCurve(Const CurveName: AnsiString;InvControl2Mode: Integer): TXYcurveObj;
- VAR
- i : Integer;
-
- begin
-
- Result := XY_CurveClass.Find(CurveName);
-
- if Result = NIL then begin
- DoSimpleMsg('XY Curve object: "' + CurveName + '" representing VOLTWATT or VOLTVAR curve (depending on mode) not found.', 380);
- Exit;
- end;
-
-
- // if VOLTWATT control mode then check for any negative watt values (pu)
- // and values greater than 1.0 per-unit (=100 percent output)
- if InvControl2Mode = VOLTWATT then
- begin
- for i:= 1 to Result.NumPoints do
- begin
- if (Result.YValue_pt[i] < 0.0) or (Result.YValue_pt[i] > 1.0) then
- begin
- DoSimpleMsg('XY Curve object: "' + CurveName + '" has active power value(s) greater than 1.0 per-unit or less than -1.0 per-unit. Not allowed for VOLTWATT control mode for PVSystem/Storages', 381);
- Result := NIL;
- Break;
- end;
- end;
- end;
-
- // if WATTPF control mode then check for any negative pf values
- // and values greater than 1.0
- if InvControl2Mode = WATTPF then
- begin
- for i:= 1 to Result.NumPoints do
- begin
- if (Result.YValue_pt[i] < -1.0) or (Result.YValue_pt[i] > 1.0) then
- begin
- DoSimpleMsg('XY Curve object: "' + CurveName + '" has power factor value(s) greater than 1.0 or less than -1.0. Not allowed for WATTPF control mode for PVSystem/Storages', 381);
- Result := NIL;
- Break;
- end;
- end;
- end;
-
- // if WATTVAR control mode then check for any negative pf values
- // and values greater than 1.0
- if InvControl2Mode = WATTVAR then
- begin
- for i:= 1 to Result.NumPoints do
- begin
- if (Result.YValue_pt[i] < -1.0) or (Result.YValue_pt[i] > 1.0) then
- begin
- DoSimpleMsg('XY Curve object: "' + CurveName + '" has reactive power value(s) greater than 1.0 per-unit or less than -1.0 per-unit. Not allowed for WATTVAR control mode for PVSystem/Storages', 381);
- Result := NIL;
- Break;
- end;
- end;
- end;
-
- end;
-
-function TInvControl2Obj.InterpretAvgVWindowLen(const s:AnsiString):Integer;
-
- Var
- Code : Integer;
- ch : char;
- s2 : AnsiString;
-
- begin
- {Try to convert and see if we get an error}
- val(s,Result, Code);
- if Code = 0 then
- begin
- FRollAvgWindowLengthIntervalUnit := 's'; // Only a number was specified, so must be seconds
- FVAvgWindowLengthSec := Result*1.0;
- Exit;
- end;
-
- {Error occurred so must have a units specifier}
- ch := s[Length(s)]; // get last character
- s2 := copy(s, 1, Length(s)-1);
- Val(S2, Result, Code);
-
- if Code>0 then
- begin {check for error}
- FRollAvgWindowLengthIntervalUnit := 's'; // Only a number was specified, so must be seconds
- FVAvgWindowLengthSec := 1.0;
- Result := 1;
- DosimpleMsg('Error in specification of Voltage Averaging Window Length: ' + s, 1134);
- Exit;
- end;
-
- case ch of
- 'h':
- begin
- FRollAvgWindowLengthIntervalUnit := 'h';
- FVAvgWindowLengthSec := Result*3600.0;
- end;
- 'm':
- begin
- FRollAvgWindowLengthIntervalUnit := 'm';
- FVAvgWindowLengthSec := Result*60.0;
- end;
- 's':
- begin
- FRollAvgWindowLengthIntervalUnit := 's';
- FVAvgWindowLengthSec := Result*1.0;
- end;
- else
- FRollAvgWindowLengthIntervalUnit := 's';
- FVAvgWindowLengthSec := Result*1.0;
- Result := 0; // Don't change it
- DosimpleMsg('Error in specification of voltage sample interval size: "' + s +'" Units can only be h, m, or s (single char only) ', 99934);
- end;
- end;
-
-function TInvControl2Obj.InterpretDRCAvgVWindowLen(const s:AnsiString):Integer;
-
- Var
- Code : Integer;
- ch : char;
- s2 : AnsiString;
-
- begin
- {Try to convert and see if we get an error}
- val(s,Result, Code);
- if Code = 0 then
- begin
- FDRCRollAvgWindowLengthIntervalUnit := 's'; // Only a number was specified, so must be seconds
- FDRCVAvgWindowLengthSec := Result*1.0;
- Exit;
- end;
-
- {Error occurred so must have a units specifier}
- ch := s[Length(s)]; // get last character
- s2 := copy(s, 1, Length(s)-1);
- Val(S2, Result, Code);
- if Code>0 then
- begin {check for error}
- FDRCRollAvgWindowLengthIntervalUnit := 's'; // Only a number was specified, so must be seconds
- FDRCVAvgWindowLengthSec := 1.0;
- Result := 1;
- DosimpleMsg('Error in specification of Voltage Averaging Window Length: ' + s, 1134);
- Exit;
- end;
-
- case ch of
- 'h':
- begin
- FDRCRollAvgWindowLengthIntervalUnit := 'h';
- FDRCVAvgWindowLengthSec := Result*3600.0;
- end;
- 'm':
- begin
- FDRCRollAvgWindowLengthIntervalUnit := 'm';
- FDRCVAvgWindowLengthSec := Result*60.0;
- end;
- 's':
- begin
- FDRCRollAvgWindowLengthIntervalUnit := 's';
- FDRCVAvgWindowLengthSec := Result*1.0;
- end;
- else
- FDRCRollAvgWindowLengthIntervalUnit := 's';
- FDRCVAvgWindowLengthSec := Result*1.0;
- Result := 0; // Don't change it
- DosimpleMsg('Error in specification of voltage sample interval size: "' + s +'" Units can only be h, m, or s (single char only) ', 99934);
- end;
end;
-function TInvControl2Obj.GetPropertyValue(Index: Integer): AnsiString;
-
- begin
-
- Result := '';
-
- CASE Index of
- 1 : Result := ReturnElementsList;
-// 2 :
-// begin
-// if ControlMode = VOLTVAR then Result := VOLTVAR;
-// if ControlMode = VOLTWATT then Result := VOLTWATT;
-// if ControlMode = DRC then Result := DRC;
-// end;
-
- 4 : Result := Format ('%s',[Fvvc_curvename]);
- 5 : Result := Format('%-.6g', [Fvvc_curveOffset]);
- 6 :
- begin
- if(FVoltage_CurveX_ref = 0) then Result := 'rated'
- else if (FVoltage_CurveX_ref = 1) then Result := 'avg'
- else if (FVoltage_CurveX_ref = 2) then Result := 'avgrated'
- end;
-
- 7 : Result := Format('%d', [FRollAvgWindowLength,FRollAvgWindowLengthIntervalUnit]);
- 8 : Result := Format ('%s',[Fvoltwatt_curvename]);
- 9 : Result := Format('%.6g', [FDbVMin]);
- 10 : Result := Format('%.6g', [FDbVMax]);
- 11 : Result := Format('%.6g', [FArGraLowV]);
- 12 : Result := Format('%.6g', [FArGraHiV]);
- 13 : Result := Format('%d', [FDRCRollAvgWindowLength,FDRCRollAvgWindowLengthIntervalUnit]);
- 14 : Result := Format('%.6g', [FdeltaQ_factor]);
- 15 : Result := Format('%.6g', [FVoltageChangeTolerance]);
- 16 : Result := Format('%.6g', [FVarChangeTolerance]);
- 17 :
- begin
- if(FVoltwattYAxis = 0) then Result := 'PAVAILABLEPU';
- if(FVoltwattYAxis = 1) then Result := 'PMPPPU';
- if(FVoltwattYAxis = 2) then Result := 'PCTPMPPPU';
- if(FVoltwattYAxis = 3) then Result := 'KVARATINGPU';
- end;
-
- 18 :
- begin
- if RateofChangeMode = INACTIVE then Result := 'INACTIVE'
- else if RateofChangeMode = LPF then Result := 'LPF'
- else if RateofChangeMode = RISEFALL then Result := 'RISEFALL';
- end;
-
- 21 : Result := Format('%.6g', [FdeltaP_factor]);
- 23 : Result := FReacPower_ref;
- 24 : Result := Format('%.6g', [FActivePChangeTolerance]);
-
- 28 : Result := Format ('%s',[FvoltwattCH_curvename]);
-
- else // take the generic handler
- Result := Inherited GetPropertyValue(index);
- end;
- end;
-
-
-function TInvControl2Obj.ReturnElementsList: AnsiString;
- VAR
- i : Integer;
-
- begin
- if FListSize=0 then
- begin
- Result := '';
- Exit;
- end;
-
- Result := '['+ FDERNameList.Strings[0];
-
- for i := 1 to FListSize-1 do
- begin
- Result := Result + ', ' + FDERNameList.Strings[i]; // we need to pass the full name..
- end;
- Result := Result + ']'; // terminate the array
-
- end;
-
-procedure TInvControl2Obj.Set_Enabled(Value: Boolean);
- begin
- inherited;
-
- {Reset controlled PVSystem/Storages to original PF}
-
- end;
-
-procedure TInvControl2Obj.Set_PendingChange(Value: Integer;DevIndex: Integer);
- begin
+procedure TInvControl2Obj.Set_PendingChange(Value: Integer; DevIndex: Integer);
+begin
FPendingChange[DevIndex] := Value;
DblTraceParameter := Value;
- end;
-
-procedure TInvControl2Obj.UpdateInvControl(i:integer);
- Var
- j,k : Integer;
- solnvoltage : Double;
- tempVbuffer : pComplexArray;
- PVSys : TPVSystemObj;
- Storage : TStorageObj;
- BasekV : Double;
+end;
- begin
- tempVbuffer := Nil; // Initialize for Reallocmem
+procedure TInvControl2Obj.UpdateInvControl(i: Integer);
+var
+ j, k: Integer;
+ solnvoltage: Double;
+ tempVbuffer: pComplexArray;
+ PVSys: TPVSystemObj = NIL;
+ Storage: TStorageObj = NIL;
+ BasekV: Double;
+begin
+ tempVbuffer := NIL; // Initialize for Reallocmem
- for j := 1 to FDERPointerList.Count do
- begin
+ for j := 1 to FDERPointerList.Count do
+ begin
// only update solution idx one time through this routine
- if (j = 1) and (i = 1) then
- begin
+ if (j = 1) and (i = 1) then
+ begin
//update solution voltage in per-unit for hysteresis
- if FVpuSolutionIdx = 2 then FVpuSolutionIdx := 1
- else FVpuSolutionIdx := FVpuSolutionIdx+1;
- end;
+ if FVpuSolutionIdx = 2 then
+ FVpuSolutionIdx := 1
+ else
+ FVpuSolutionIdx := FVpuSolutionIdx + 1;
+ end;
+
+ if ControlledElement[j].DSSClassName = 'PVSystem' then
+ PVSys := ControlledElement[j] as TPVSystemObj
+ else
+ Storage := ControlledElement[j] as TStorageObj;
- if ControlledElement[j].DSSClassName = 'PVSystem' then PVSys := ControlledElement[j] as TPVSystemObj
- else Storage := ControlledElement[j] as TStorageObj;
+ BasekV := FVBase[i] / 1000.0;
- BasekV := FVBase[i] / 1000.0;
- // FPriorvars[j] := PVSys.Presentkvar;
- // FPriorWatts[j] := PVSys.PresentkW;
- FPriorPLimitOptionpu[j] := PLimitOptionpu[j];
- FPriorQDesireOptionpu[j] := QDesireOptionpu[j];
+ FPriorPLimitOptionpu[j] := PLimitOptionpu[j];
+ FPriorQDesireOptionpu[j] := QDesireOptionpu[j];
// Used to update the VW resquested kW
- if ControlledElement[j].DSSClassName = 'PVSystem' then PVSys.VWmode := FALSE
- else Storage.VWMode := FALSE;
+ if ControlledElement[j].DSSClassName = 'PVSystem' then
+ PVSys.VWmode := FALSE
+ else
+ Storage.VWMode := FALSE;
- if ControlledElement[j].DSSClassName = 'PVSystem' then PVSys.VVmode := FALSE
- else Storage.VVMode := FALSE;
+ if ControlledElement[j].DSSClassName = 'PVSystem' then
+ PVSys.VVmode := FALSE
+ else
+ Storage.VVMode := FALSE;
- if ControlledElement[j].DSSClassName = 'PVSystem' then PVSys.DRCmode := FALSE
- else Storage.DRCMode := FALSE;
+ if ControlledElement[j].DSSClassName = 'PVSystem' then
+ PVSys.DRCmode := FALSE
+ else
+ Storage.DRCMode := FALSE;
- FFlagVWOperates[j] := False;
+ FFlagVWOperates[j] := FALSE;
// Reset DQDV - We might not need it
- DQDV[j] := 0.0;
+ DQDV[j] := 0.0;
// Reset the operation flags for the new time step
- FVVOperation[j] := 0;
- FVWOperation[j] := 0;
- FDRCOperation[j] := 0;
- FVVDRCOperation[j]:= 0;
- FWPOperation[j] := 0;
- FWVOperation[j] := 0;
- FAVROperation[j] := 0;
+ FVVOperation[j] := 0;
+ FVWOperation[j] := 0;
+ FDRCOperation[j] := 0;
+ FVVDRCOperation[j] := 0;
+ FWPOperation[j] := 0;
+ FWVOperation[j] := 0;
+ FAVROperation[j] := 0;
// Reinitialize convergence arrays.
//FdeltaQFactor[j] := DELTAQDEFAULT;
- FdeltaPFactor[j] := DELTAPDEFAULT;
+ FdeltaPFactor[j] := DELTAPDEFAULT;
// allocated enough memory to buffer to hold voltages and initialize to cZERO
- Reallocmem(tempVbuffer, Sizeof(tempVbuffer^[1]) * ControlledElement[j].NConds);
- for k := 1 to ControlledElement[j].NConds do tempVbuffer[k] := cZERO;
+ Reallocmem(tempVbuffer, Sizeof(tempVbuffer^[1]) * ControlledElement[j].NConds);
+ for k := 1 to ControlledElement[j].NConds do
+ tempVbuffer[k] := cZERO;
- priorRollAvgWindow[j] := FRollAvgWindow[j].Get_AvgVal;
- priorDRCRollAvgWindow[j] := FDRCRollAvgWindow[j].Get_AvgVal;
+ priorRollAvgWindow[j] := FRollAvgWindow[j].AvgVal;
+ priorDRCRollAvgWindow[j] := FDRCRollAvgWindow[j].AvgVal;
// compute the present terminal voltage
- ControlledElement[j].ComputeVterminal();
- //PVSys.Set_Variable(5,FDRCRollAvgWindow[j].Get_AvgVal); // save rolling average voltage in monitor
+ ControlledElement[j].ComputeVterminal();
+ //PVSys.Set_Variable(5,FDRCRollAvgWindow[j].AvgVal); // save rolling average voltage in monitor
- solnvoltage := 0.0;
+ solnvoltage := 0.0;
- GetmonVoltage(solnvoltage, j, BasekV);
+ GetmonVoltage(solnvoltage, j, BasekV);
- //for k := 1 to localControlledElement.Yorder do tempVbuffer[k] := localControlledElement.Vterminal^[k];
+ // add present power flow solution voltage to the rolling average window
+ FRollAvgWindow[j].Add(solnvoltage, ActiveCircuit.Solution.DynaVars.h, FRollAvgWindowLength);
+ FDRCRollAvgWindow[j].Add(solnvoltage, ActiveCircuit.Solution.DynaVars.h, FDRCRollAvgWindowLength);
+ FVpuSolution[j, FVpuSolutionIdx] := solnvoltage / ((ActiveCircuit.Buses^[ControlledElement[j].terminals[0].busRef].kVBase) * 1000.0);
- //for k := 1 to localControlledElement.Nphases do solnvoltage := solnvoltage + Cabs(tempVbuffer[k]);
- //solnvoltage := solnvoltage / (localControlledElement.Nphases*1.0); // average of voltages if more than one phase
+ Reallocmem(tempVbuffer, 0); // Clean up memory
- // add present power flow solution voltage to the rolling average window
- FRollAvgWindow[j].Add(solnvoltage,ActiveCircuit.Solution.DynaVars.h,FVAvgWindowLengthSec);
- FDRCRollAvgWindow[j].Add(solnvoltage,ActiveCircuit.Solution.DynaVars.h,FDRCVAvgWindowLengthSec);
-
- FVpuSolution[j,FVpuSolutionIdx] := solnvoltage/((ActiveCircuit.Buses^[ControlledElement[j].terminals[0].busRef].kVBase)*1000.0);
-
- Reallocmem(tempVbuffer, 0); // Clean up memory
-
- end;
-
- end;
+ end;
+end;
-function TInvControl2Obj.Get_PendingChange(DevIndex: Integer):Integer;
- begin
+function TInvControl2Obj.Get_PendingChange(DevIndex: Integer): Integer;
+begin
Result := FPendingChange[DevIndex];
- end;
+end;
procedure TInvControl2Obj.CalcVoltWatt_watts(j: Integer);
- VAR
- DeltaPpu :Double;
+var
+ DeltaPpu: Double;
// PLimitEndpu[j] <= abs(kW_out_desiredpu[j] will always be true when we are in 'resquest' region of VW
// That's what we want. In this region, VW will work similarly to VV. So we need to move slowly towards the VW curve point.
- begin
- if ((PLimitEndpu[j] < 1.0) and (PLimitEndpu[j] <= abs(kW_out_desiredpu[j]))) or (FFlagVWOperates[j]) then
- begin
- if(ActiveCircuit.Solution.ControlIteration=1) then POldVWpu[j] := abs(kW_out_desiredpu[j]); // take abs(kW_out_desiredpu[j]) because might be in charging mode.
- FFlagVWOperates[j] := True;
+begin
+ if ((PLimitEndpu[j] < 1.0) and (PLimitEndpu[j] <= abs(kW_out_desiredpu[j]))) or (FFlagVWOperates[j]) then
+ begin
+ if (ActiveCircuit.Solution.ControlIteration = 1) then
+ POldVWpu[j] := abs(kW_out_desiredpu[j]); // take abs(kW_out_desiredpu[j]) because might be in charging mode.
+ FFlagVWOperates[j] := TRUE;
// PLimitEndpu might be negative here in 'requesting' region. Do we need to give POldVW a sign in this case?
// Yes, it will naturally evolve to a negative value with the process. It will always positive only in the 1st control iteration.
DeltaPpu := PLimitEndpu[j] - POldVWpu[j];
- if FdeltaP_factor = FLAGDELTAP then Change_deltaP_factor(j)
- else FdeltaPFactor[j] := FdeltaP_factor;
+ if FdeltaP_factor = FLAGDELTAP then
+ Change_deltaP_factor(j)
+ else
+ FdeltaPFactor[j] := FdeltaP_factor;
- PLimitVW[j] := (POldVWpu[j] + DeltaPpu * FdeltaPFactor[j]) * PBase[j];
- end
+ PLimitVW[j] := (POldVWpu[j] + DeltaPpu * FdeltaPFactor[j]) * PBase[j];
+ end
else
- begin
+ begin
PLimitVW[j] := PLimitEndpu[j] * PBase[j];
- end;
-
- end;
+ end;
+end;
procedure TInvControl2Obj.Check_Plimits(j: Integer; P: Double);
- VAR
- P_Ppriority :Double;
- pctDCkWRatedlimit :Double;
-
- begin
+var
+ P_Ppriority: Double;
+ pctDCkWRatedlimit: Double;
+begin
PLimitLimitedpu[j] := 1.0; // Not limited
// volt-watt states
- if P < 1.0 then FVWOperation[j] := 1.0;
+ if P < 1.0 then
+ FVWOperation[j] := 1.0;
pctDCkWRatedlimit := FpctDCkWRated[j] * FDCkWRated[j];
// PLimitEndpu should be less than the P avaliable under var priority (works for VV_VW)
- if FPPriority[j] = False then
- begin
- P_Ppriority := Sqrt(SQR(FkVARating[j]) - SQR(Fpresentkvar[j]));
+ if FPPriority[j] = FALSE then
+ begin
+ P_Ppriority := Sqrt(SQR(FkVARating[j]) - SQR(Fpresentkvar[j]));
if P_Ppriority < (abs(P) * PBase[j]) then // P might be negative in requesting region for storage
- begin
- PLimitLimitedpu[j] := P_Ppriority / PBase[j] * sign(P);
+ begin
+ PLimitLimitedpu[j] := P_Ppriority / PBase[j] * sign(P);
FVWOperation[j] := 0.0; // kVA exceeded under watt priority
- end;
- end;
+ end;
+ end;
// PLimitEndpu should be less than pctPmpp
- if (abs(P) * PBase[j]) > pctDCkWRatedlimit then
- begin
+ if (abs(P) * PBase[j]) > pctDCkWRatedlimit then
+ begin
FVWOperation[j] := 0.0; // pctPmpp exceeded under watt priority
- PLimitLimitedpu[j] := pctDCkWRatedlimit / PBase[j] * sign(P);
- end;
-
- end;
+ PLimitLimitedpu[j] := pctDCkWRatedlimit / PBase[j] * sign(P);
+ end;
+end;
procedure TInvControl2Obj.CalcVoltVar_vars(j: Integer);
- VAR
- DeltaQ :Double;
+var
+ DeltaQ: Double;
- begin
- if(FlagChangeCurve[j] = False) then
- begin
- if QDesireEndpu[j] >= 0.0 then DeltaQ := QDesireEndpu[j] * QHeadRoom[j] - QOldVV[j]
- else DeltaQ := QDesireEndpu[j] * QHeadRoomNeg[j] - QOldVV[j];
+begin
+ if (FlagChangeCurve[j] = FALSE) then
+ begin
+ if QDesireEndpu[j] >= 0.0 then
+ DeltaQ := QDesireEndpu[j] * QHeadRoom[j] - QOldVV[j]
+ else
+ DeltaQ := QDesireEndpu[j] * QHeadRoomNeg[j] - QOldVV[j];
- if FdeltaQ_factor = FLAGDELTAQ then Change_deltaQ_factor(j)
- else FdeltaQFactor[j] := FdeltaQ_factor;
+ if FdeltaQ_factor = FLAGDELTAQ then
+ Change_deltaQ_factor(j)
+ else
+ FdeltaQFactor[j] := FdeltaQ_factor;
QDesiredVV[j] := QOldVV[j] + DeltaQ * FdeltaQFactor[j];
- end
+ end
// else, stay at present var output level
else
- begin
+ begin
QDesiredVV[j] := Fpresentkvar[j]
- end;
- end;
+ end;
+end;
procedure TInvControl2Obj.CalcAVR_vars(j: Integer);
- VAR
- DeltaQ :Double;
+var
+ DeltaQ: Double;
- begin
- if QDesireEndpu[j] >= 0.0 then DeltaQ := QDesireEndpu[j] * QHeadRoom[j] - QOldAVR[j]
- else DeltaQ := QDesireEndpu[j] * QHeadRoomNeg[j] - QOldAVR[j];
+begin
+ if QDesireEndpu[j] >= 0.0 then
+ DeltaQ := QDesireEndpu[j] * QHeadRoom[j] - QOldAVR[j]
+ else
+ DeltaQ := QDesireEndpu[j] * QHeadRoomNeg[j] - QOldAVR[j];
- if FdeltaQ_factor = FLAGDELTAQ then Change_deltaQ_factor(j)
- else FdeltaQFactor[j] := FdeltaQ_factor;
+ if FdeltaQ_factor = FLAGDELTAQ then
+ Change_deltaQ_factor(j)
+ else
+ FdeltaQFactor[j] := FdeltaQ_factor;
QDesiredAVR[j] := QOldAVR[j] + 0.2 * DeltaQ;
// QDesiredAVR[j] := QDesireEndpu[j] * QHeadRoomNeg[j]
- end;
+end;
procedure TInvControl2Obj.CalcWATTPF_vars(j: Integer);
- begin
-
+begin
if QDesireEndpu[j] >= 0.0 then
- QDesiredWP[j] := QDesireEndpu[j] * QHeadRoom[j]
+ QDesiredWP[j] := QDesireEndpu[j] * QHeadRoom[j]
else
- QDesiredWP[j] := QDesireEndpu[j] * QHeadRoomNeg[j];
- end;
+ QDesiredWP[j] := QDesireEndpu[j] * QHeadRoomNeg[j];
+end;
procedure TInvControl2Obj.CalcWATTVAR_vars(j: Integer);
- begin
-
+begin
if QDesireEndpu[j] >= 0.0 then
- QDesiredWV[j] := QDesireEndpu[j] * QHeadRoom[j]
+ QDesiredWV[j] := QDesireEndpu[j] * QHeadRoom[j]
else
- QDesiredWV[j] := QDesireEndpu[j] * QHeadRoomNeg[j];
- end;
+ QDesiredWV[j] := QDesireEndpu[j] * QHeadRoomNeg[j];
+end;
procedure TInvControl2Obj.CalcDRC_vars(j: Integer);
- VAR
- DeltaQ :Double;
+var
+ DeltaQ: Double;
- begin
- if QDesireEndpu[j] >= 0.0 then DeltaQ := QDesireEndpu[j] * QHeadRoom[j] - QOldDRC[j]
- else DeltaQ := QDesireEndpu[j] * QHeadRoomNeg[j] - QOldDRC[j];
-
- if FdeltaQ_factor = FLAGDELTAQ then Change_deltaQ_factor(j)
- else FdeltaQFactor[j] := FdeltaQ_factor;
+begin
+ if QDesireEndpu[j] >= 0.0 then
+ DeltaQ := QDesireEndpu[j] * QHeadRoom[j] - QOldDRC[j]
+ else
+ DeltaQ := QDesireEndpu[j] * QHeadRoomNeg[j] - QOldDRC[j];
- QDesiredDRC[j] := QOldDRC[j] + DeltaQ * FdeltaQFactor[j];
+ if FdeltaQ_factor = FLAGDELTAQ then
+ Change_deltaQ_factor(j)
+ else
+ FdeltaQFactor[j] := FdeltaQ_factor;
- end;
+ QDesiredDRC[j] := QOldDRC[j] + DeltaQ * FdeltaQFactor[j];
+end;
procedure TInvControl2Obj.CalcVVDRC_vars(j: Integer);
- VAR
- DeltaQ :Double;
+var
+ DeltaQ: Double;
- begin
- if QDesireEndpu[j] >= 0.0 then DeltaQ := QDesireEndpu[j] * QHeadRoom[j] - QOldVVDRC[j]
- else DeltaQ := QDesireEndpu[j] * QHeadRoomNeg[j] - QOldVVDRC[j];
+begin
+ if QDesireEndpu[j] >= 0.0 then
+ DeltaQ := QDesireEndpu[j] * QHeadRoom[j] - QOldVVDRC[j]
+ else
+ DeltaQ := QDesireEndpu[j] * QHeadRoomNeg[j] - QOldVVDRC[j];
- if FdeltaQ_factor = FLAGDELTAQ then Change_deltaQ_factor(j)
- else FdeltaQFactor[j] := FdeltaQ_factor;
+ if FdeltaQ_factor = FLAGDELTAQ then
+ Change_deltaQ_factor(j)
+ else
+ FdeltaQFactor[j] := FdeltaQ_factor;
QDesiredVVDRC[j] := QOldVVDRC[j] + DeltaQ * FdeltaQFactor[j];
-
- end;
+end;
procedure TInvControl2Obj.Calc_PBase(j: Integer);
- Var
- DERelem :TPCElement;
-
- begin
+var
+ DERelem: TPCElement;
+begin
DERelem := ControlledElement[j];
if DERelem.DSSClassName = 'PVSystem' then
- begin
- if(FVoltwattYaxis = 0) then PBase[j] := FDCkW[j] * FEffFactor[j]
+ begin
+ if (FVoltwattYaxis = 0) then
+ PBase[j] := FDCkW[j] * FEffFactor[j]
- else if(FVoltwattYaxis = 1) then PBase[j] := FDCkWRated[j]
+ else
+ if (FVoltwattYaxis = 1) then
+ PBase[j] := FDCkWRated[j]
- else if(FVoltwattYaxis = 2) then PBase[j] := FDCkWRated[j] * FpctDCkWRated[j]
+ else
+ if (FVoltwattYaxis = 2) then
+ PBase[j] := FDCkWRated[j] * FpctDCkWRated[j]
- else if(FVoltwattYaxis = 3) then PBase[j] := FkVARating[j];
- end
+ else
+ if (FVoltwattYaxis = 3) then
+ PBase[j] := FkVARating[j];
+ end
else
- begin
- if(FVoltwattYaxis = 0) then PBase[j] := TStorageObj(DERelem).DCkW * FEffFactor[j]
-
- else if(FVoltwattYaxis = 1) then PBase[j] := FDCkWRated[j]
+ begin
+ if (FVoltwattYaxis = 0) then
+ PBase[j] := TStorageObj(DERelem).DCkW * FEffFactor[j]
- else if(FVoltwattYaxis = 2) then PBase[j] := FDCkWRated[j] * FpctDCkWRated[j]
+ else
+ if (FVoltwattYaxis = 1) then
+ PBase[j] := FDCkWRated[j]
- else if(FVoltwattYaxis = 3) then PBase[j] := FkVARating[j];
+ else
+ if (FVoltwattYaxis = 2) then
+ PBase[j] := FDCkWRated[j] * FpctDCkWRated[j]
- end;
+ else
+ if (FVoltwattYaxis = 3) then
+ PBase[j] := FkVARating[j];
- end;
+ end;
+end;
-procedure TInvControl2Obj.CalcLPF(m: Integer; powertype: AnsiString; LPF_desiredpu : Double);
- VAR
- alpha :Double;
+procedure TInvControl2Obj.CalcLPF(m: Integer; powertype: Ansistring; LPF_desiredpu: Double);
+var
+ alpha: Double;
// Applies the LPF:
// Return value is in kvar for VARS
// Return value is in puPmpp for WATTS
- begin
+begin
// Qoutput(t) = Qdesired(t) x {1- exp[-(t-t0)/tau]} + Qoutput(t-t0) x exp[-(t-t0)/tau]
// calculate the alpha constant: alpha = exp[-(t-t0)/tau]
- alpha := exp(-1.0 * ActiveCircuit.Solution.DynaVars.h/FLPFTau);
+ alpha := exp(-1.0 * ActiveCircuit.Solution.DynaVars.h / LPFTau);
- if powertype = 'VARS' then QDesireOptionpu[m] := LPF_desiredpu * (1-alpha) + FPriorQDesireOptionpu[m] * alpha;
+ if powertype = 'VARS' then
+ QDesireOptionpu[m] := LPF_desiredpu * (1 - alpha) + FPriorQDesireOptionpu[m] * alpha;
- if powertype = 'WATTS' then PLimitOptionpu[m] := LPF_desiredpu * (1-alpha) + FPriorPLimitOptionpu[m] * alpha
+ if powertype = 'WATTS' then
+ PLimitOptionpu[m] := LPF_desiredpu * (1 - alpha) + FPriorPLimitOptionpu[m] * alpha
- end;
+end;
-procedure TInvControl2Obj.CalcRF(m: Integer; powertype: AnsiString; RF_desiredpu: Double);
+procedure TInvControl2Obj.CalcRF(m: Integer; powertype: Ansistring; RF_desiredpu: Double);
- begin
+begin
// Applies the Rise/Fall limiting function:
- if powertype='VARS' then
- begin
+ if powertype = 'VARS' then
+ begin
// rate of change rise/fall limit
if (RF_desiredpu - FPriorQDesireOptionpu[m]) > (FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h) then
- QDesireOptionpu[m] := FPriorQDesireOptionpu[m] + FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h
- else if (RF_desiredpu - FPriorQDesireOptionpu[m]) < (-1 * FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h) then
- QDesireOptionpu[m] := FPriorQDesireOptionpu[m] - FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h
+ QDesireOptionpu[m] := FPriorQDesireOptionpu[m] + FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h
else
- QDesireOptionpu[m] := RF_desiredpu;
- end;
+ if (RF_desiredpu - FPriorQDesireOptionpu[m]) < (-1 * FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h) then
+ QDesireOptionpu[m] := FPriorQDesireOptionpu[m] - FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h
+ else
+ QDesireOptionpu[m] := RF_desiredpu;
+ end;
- if powertype='WATTS' then
- begin
+ if powertype = 'WATTS' then
+ begin
// rate of change rise/fall limit
if (RF_desiredpu - FPriorPLimitOptionpu[m]) > (FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h) then
- PLimitOptionpu[m] := FPriorPLimitOptionpu[m] + (FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h)
- else if (RF_desiredpu - FPriorPLimitOptionpu[m]) < (-1 * FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h) then
- PLimitOptionpu[m] := FPriorPLimitOptionpu[m] - (FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h)
+ PLimitOptionpu[m] := FPriorPLimitOptionpu[m] + (FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h)
else
- PLimitOptionpu[m] := RF_desiredpu;
- end;
-
- end;
+ if (RF_desiredpu - FPriorPLimitOptionpu[m]) < (-1 * FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h) then
+ PLimitOptionpu[m] := FPriorPLimitOptionpu[m] - (FRiseFallLimit * ActiveCircuit.Solution.DynaVars.h)
+ else
+ PLimitOptionpu[m] := RF_desiredpu;
+ end;
+end;
procedure TInvControl2Obj.CalcPVWcurve_limitpu(j: Integer);
- begin
-
- if ControlledElement[j].DSSClassName = 'PVSystem' then PLimitVWpu[j] := Fvoltwatt_curve.GetYValue(FPresentVpu[j])
- else
- begin
- if TStorageObj(ControlledElement[j]).StorageState = STORE_DISCHARGING then
- Begin
- if TStorageObj(ControlledElement[j]).FVWStateRequested then PLimitVWpu[j] := FvoltwattCH_curve.GetYValue(FPresentVpu[j])
- else PLimitVWpu[j] := Fvoltwatt_curve.GetYValue(FPresentVpu[j]);
-
- End
- else if (TStorageObj(ControlledElement[j]).StorageState = STORE_CHARGING) and (FvoltwattCH_curve <> Nil) then
- Begin
- if TStorageObj(ControlledElement[j]).FVWStateRequested then PLimitVWpu[j] := Fvoltwatt_curve.GetYValue(FPresentVpu[j])
- else PLimitVWpu[j] := FvoltwattCH_curve.GetYValue(FPresentVpu[j]) // try with positive PlimitVWpu
- End
+begin
+ if ControlledElement[j].DSSClassName = 'PVSystem' then
+ PLimitVWpu[j] := Fvoltwatt_curve.GetYValue(FPresentVpu[j])
+ else
+ begin
+ if TStorageObj(ControlledElement[j]).StorageState = STORE_DISCHARGING then
+ begin
+ if TStorageObj(ControlledElement[j]).FVWStateRequested then
+ PLimitVWpu[j] := FvoltwattCH_curve.GetYValue(FPresentVpu[j])
+ else
+ PLimitVWpu[j] := Fvoltwatt_curve.GetYValue(FPresentVpu[j]);
- else PLimitVWpu[j] := 1.0; // don't limit if in idling state
- end;
+ end
+ else
+ if (TStorageObj(ControlledElement[j]).StorageState = STORE_CHARGING) and (FvoltwattCH_curve <> NIL) then
+ begin
+ if TStorageObj(ControlledElement[j]).FVWStateRequested then
+ PLimitVWpu[j] := Fvoltwatt_curve.GetYValue(FPresentVpu[j])
+ else
+ PLimitVWpu[j] := FvoltwattCH_curve.GetYValue(FPresentVpu[j]) // try with positive PlimitVWpu
+ end
- end;
+ else
+ PLimitVWpu[j] := 1.0; // don't limit if in idling state
+ end;
+end;
procedure TInvControl2Obj.CalcQVVcurve_desiredpu(j: Integer);
- VAR
- voltagechangesolution :Double;
- QPresentpu :Double;
- VpuFromCurve :Double;
-
- begin
+var
+ voltagechangesolution: Double;
+ QPresentpu: Double;
+ VpuFromCurve: Double;
+begin
QDesireVVpu[j] := 0.0;
- if Fpresentkvar[j] >= 0.0 then QPresentpu := Fpresentkvar[j] / QHeadRoom[j]
- else QPresentpu := Fpresentkvar[j] / QHeadRoomNeg[j];
+ if Fpresentkvar[j] >= 0.0 then
+ QPresentpu := Fpresentkvar[j] / QHeadRoom[j]
+ else
+ QPresentpu := Fpresentkvar[j] / QHeadRoomNeg[j];
voltagechangesolution := 0.0;
// for first two seconds, keep voltagechangesolution equal to zero
// we don't have solutions from the time-series power flow, yet
- if ((ActiveCircuit.Solution.DynaVars.dblHour*3600.0 / ActiveCircuit.Solution.DynaVars.h)<3.0) then voltagechangesolution := 0.0
- else if(FVpuSolutionIdx = 1) then voltagechangesolution := FVpuSolution[j,1] - FVpuSolution[j,2]
- else if(FVpuSolutionIdx = 2) then voltagechangesolution := FVpuSolution[j,2] - FVpuSolution[j,1];
+ if ((ActiveCircuit.Solution.DynaVars.dblHour * 3600.0 / ActiveCircuit.Solution.DynaVars.h) < 3.0) then
+ voltagechangesolution := 0.0
+ else
+ if (FVpuSolutionIdx = 1) then
+ voltagechangesolution := FVpuSolution[j, 1] - FVpuSolution[j, 2]
+ else
+ if (FVpuSolutionIdx = 2) then
+ voltagechangesolution := FVpuSolution[j, 2] - FVpuSolution[j, 1];
// if no hysteresis (Fvvc_curveOffset == 0), then just look up the value
// from the volt-var curve
if Fvvc_curveOffset = 0.0 then
- begin // no hysteresis
+ begin // no hysteresis
QDesireVVpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j])
- end // end of logic for the no-hysteresis case
+ end // end of logic for the no-hysteresis case
// else if we're going in the positive direction and on curve 1, stay
// with curve 1
- else if (voltagechangesolution > 0) and (FActiveVVCurve[j] = 1) then
- begin
- if(FlagChangeCurve[j] = True) then
- begin
+ else
+ if (voltagechangesolution > 0) and (FActiveVVCurve[j] = 1) then
+ begin
+ if (FlagChangeCurve[j] = TRUE) then
+ begin
VpuFromCurve := Fvvc_curve.GetXValue(QPresentpu);
- if(Abs(FPresentVpu[j] - VpuFromCurve) < FVoltageChangeTolerance/2.0) then
- begin
- QDesireVVpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]); //Y value = in per-unit of headroom
- FlagChangeCurve[j] := False;
- end
- else
- begin
- QDesireVVpu[j] := QPresentpu; // (PR) look at here
- FlagChangeCurve[j] := False;
- end;
- end
+ if (Abs(FPresentVpu[j] - VpuFromCurve) < FVoltageChangeTolerance / 2.0) then
+ begin
+ QDesireVVpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]); //Y value = in per-unit of headroom
+ FlagChangeCurve[j] := FALSE;
+ end
+ else
+ begin
+ QDesireVVpu[j] := QPresentpu; // (PR) look at here
+ FlagChangeCurve[j] := FALSE;
+ end;
+ end
else
- begin
+ begin
QDesireVVpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]); //Y value = in per-unit of headroom
- end;
- end
+ end;
+ end
// with hysteresis if we're going in the positive direction on voltages
// from last two power flow solutions, and we're using curve 2, keep vars
// the same, and change to curve1 active
- else if (voltagechangesolution > 0) and (FActiveVVCurve[j] = 2) then
- begin
+ else
+ if (voltagechangesolution > 0) and (FActiveVVCurve[j] = 2) then
+ begin
QDesireVVpu[j] := QPresentpu;
FActiveVVCurve[j] := 1;
- FlagChangeCurve[j] := True;
- end
+ FlagChangeCurve[j] := TRUE;
+ end
// with hysteresis if we're going in the negative direction on voltages
// from last two power flow solutions, and we're using curve 2, either
// lookup the vars for the voltage we're at (with offset on curve1),
// or if we've not just changed curves, stay at the current p.u.
// var output
- else if (voltagechangesolution < 0) and (FActiveVVCurve[j] = 2) then
- begin
- if(FlagChangeCurve[j] = True) then
- begin
+ else
+ if (voltagechangesolution < 0) and (FActiveVVCurve[j] = 2) then
+ begin
+ if (FlagChangeCurve[j] = TRUE) then
+ begin
VpuFromCurve := Fvvc_curve.GetXValue(QPresentpu);
VpuFromCurve := VpuFromCurve - Fvvc_curveOffset;
- if(Abs(FPresentVpu[j] - VpuFromCurve) < FVoltageChangeTolerance/2.0) then
- begin
- QDesireVVpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]-Fvvc_curveOffset); //Y value = in per-unit of headroom
- FlagChangeCurve[j] := False;
- end
+ if (Abs(FPresentVpu[j] - VpuFromCurve) < FVoltageChangeTolerance / 2.0) then
+ begin
+ QDesireVVpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j] - Fvvc_curveOffset); //Y value = in per-unit of headroom
+ FlagChangeCurve[j] := FALSE;
+ end
else
- begin
+ begin
QDesireVVpu[j] := QPresentpu;
- FlagChangeCurve[j] := False;
- end;
- end
+ FlagChangeCurve[j] := FALSE;
+ end;
+ end
else
- begin
- QDesireVVpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]-Fvvc_curveOffset); //Y value = in per-unit of headroom
- end;
- end
+ begin
+ QDesireVVpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j] - Fvvc_curveOffset); //Y value = in per-unit of headroom
+ end;
+ end
// with hysteresis if we're going in the negative direction on voltages
// from last two power flow solutions, and we're using curve 1, then
// stay wjth present output vars and make curve2 active, set curve change
// flag
- else if (voltagechangesolution < 0) and (FActiveVVCurve[j] = 1) then
- begin
+ else
+ if (voltagechangesolution < 0) and (FActiveVVCurve[j] = 1) then
+ begin
QDesireVVpu[j] := QPresentpu;
FActiveVVCurve[j] := 2;
- FlagChangeCurve[j] := True;
- end
+ FlagChangeCurve[j] := TRUE;
+ end
// if no change in voltage from one powerflow to the next, then
// do one of the following
- else if (voltagechangesolution = 0) and (FActiveVVCurve[j] = 1) and (FlagChangeCurve[j] = False) then
- begin
+ else
+ if (voltagechangesolution = 0) and (FActiveVVCurve[j] = 1) and (FlagChangeCurve[j] = FALSE) then
+ begin
QDesireVVpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]);
- end
- else if (voltagechangesolution = 0) and (FlagChangeCurve[j] = True) then
- begin
+ end
+ else
+ if (voltagechangesolution = 0) and (FlagChangeCurve[j] = TRUE) then
+ begin
QDesireVVpu[j] := QPresentpu;
- end
-
- else if (voltagechangesolution = 0) and (FActiveVVCurve[j] = 2) and (FlagChangeCurve[j] = False) then
- begin
- QDesireVVpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j]-Fvvc_curveOffset);
- end;
+ end
- end;
+ else
+ if (voltagechangesolution = 0) and (FActiveVVCurve[j] = 2) and (FlagChangeCurve[j] = FALSE) then
+ begin
+ QDesireVVpu[j] := Fvvc_curve.GetYValue(FPresentVpu[j] - Fvvc_curveOffset);
+ end;
+end;
procedure TInvControl2Obj.CalcQWVcurve_desiredpu(j: Integer);
- VAR
+var
// voltagechangesolution :Double;
- Pbase :Double;
- begin
-
+ Pbase: Double;
+begin
QDesireWVpu[j] := 0.0;
// voltagechangesolution := 0.0;
@@ -4192,34 +3551,34 @@ procedure TInvControl2Obj.CalcQWVcurve_desiredpu(j: Integer);
// else if(FVpuSolutionIdx = 1) then voltagechangesolution := FVpuSolution[j,1] - FVpuSolution[j,2]
// else if(FVpuSolutionIdx = 2) then voltagechangesolution := FVpuSolution[j,2] - FVpuSolution[j,1];
- Pbase:= Min(FkVARating[j], FDCkWRated[j]); // Should include DC-to-AC and kW-to-KVA ratios to avoid to quick fix like this
+ Pbase := Min(FkVARating[j], FDCkWRated[j]); // Should include DC-to-AC and kW-to-KVA ratios to avoid to quick fix like this
QDesireWVpu[j] := Fwattvar_curve.GetYValue(FDCkW[j] * FEffFactor[j] * FpctDCkWRated[j] / Pbase);
-
- end;
+end;
procedure TInvControl2Obj.CalcQAVR_desiredpu(j: Integer);
- VAR
+var
// voltagechangesolution : Double;
- DQ : Double;
- QPresentpu : Double;
- DQmax : Double;
- DeltaV : Double;
- v : Double;
- begin
-
+ DQ: Double;
+ QPresentpu: Double;
+ DQmax: Double;
+ DeltaV: Double;
+ v: Double;
+begin
DQmax := 0.1 * Fkvarlimit[j] / QHeadRoomNeg[j];
QDesireAVRpu[j] := 0.0;
- if Fpresentkvar[j] >= 0.0 then QPresentpu := Fpresentkvar[j] / QHeadRoom[j]
- else QPresentpu := Fpresentkvar[j] / QHeadRoomNeg[j];
+ if Fpresentkvar[j] >= 0.0 then
+ QPresentpu := Fpresentkvar[j] / QHeadRoom[j]
+ else
+ QPresentpu := Fpresentkvar[j] / QHeadRoomNeg[j];
if (ActiveCircuit.Solution.ControlIteration = 3) then
begin
- v := FAvgpAVRVpuPrior[j];
- QPresentpu := 0.0;
- QOldAVR[j] := 0.0;
+ v := FAvgpAVRVpuPrior[j];
+ QPresentpu := 0.0;
+ QOldAVR[j] := 0.0;
end
else
v := FPresentVpu[j];
@@ -4234,34 +3593,43 @@ procedure TInvControl2Obj.CalcQAVR_desiredpu(j: Integer);
DeltaV := Abs(Fv_setpoint - FAvgpVpuPrior[j]);
- if (abs(DeltaV) < 0.005) and (FdeltaQFactor[j] > 0.2) then FdeltaQFactor[j] := FdeltaQFactor[j] + 0.1
- else if (abs(DeltaV) < 0.02) and (FdeltaQFactor[j] > 0.2) then FdeltaQFactor[j] := FdeltaQFactor[j] + 0.05
- else if (abs(DeltaV) > 0.02) and (FdeltaQFactor[j] < 0.9) then FdeltaQFactor[j] := FdeltaQFactor[j] - 0.05
- else if (abs(DeltaV) < 0.05) and (FdeltaQFactor[j] < 0.9) then FdeltaQFactor[j] := FdeltaQFactor[j] - 0.1;
+ if (abs(DeltaV) < 0.005) and (FdeltaQFactor[j] > 0.2) then
+ FdeltaQFactor[j] := FdeltaQFactor[j] + 0.1
+ else
+ if (abs(DeltaV) < 0.02) and (FdeltaQFactor[j] > 0.2) then
+ FdeltaQFactor[j] := FdeltaQFactor[j] + 0.05
+ else
+ if (abs(DeltaV) > 0.02) and (FdeltaQFactor[j] < 0.9) then
+ FdeltaQFactor[j] := FdeltaQFactor[j] - 0.05
+ else
+ if (abs(DeltaV) < 0.05) and (FdeltaQFactor[j] < 0.9) then
+ FdeltaQFactor[j] := FdeltaQFactor[j] - 0.1;
FdeltaQFactor[j] := 0.2;
DeltaV_old[j] := Abs(FPresentVpu[j] - FAvgpVpuPrior[j]);
- if (FPresentVpu[j] - FAvgpVpuPrior[j] = 0) then DQ := 0
+ if (FPresentVpu[j] - FAvgpVpuPrior[j] = 0) then
+ DQ := 0
else
- DQ := FdeltaQFactor[j] * DQDV[j] * (Fv_setpoint - v);
- If (Abs(DQ) > DQmax) Then IF (DQ < 0.0) Then DQ := -DQmax Else DQ := DQmax;
+ DQ := FdeltaQFactor[j] * DQDV[j] * (Fv_setpoint - v);
+ if (Abs(DQ) > DQmax) then
+ if (DQ < 0.0) then
+ DQ := -DQmax
+ else
+ DQ := DQmax;
QDesireAVRpu[j] := QPresentpu + DQ;
-
-
- end;
+end;
procedure TInvControl2Obj.CalcQWPcurve_desiredpu(j: Integer);
- VAR
+var
// voltagechangesolution :Double;
- p :Double;
- pf_priority :Boolean;
- QDesiredWP :Double;
-
- begin
+ p: Double;
+ pf_priority: Boolean = False;
+ QDesiredWP: Double;
+begin
QDesireWPpu[j] := 0.0;
// voltagechangesolution := 0.0;
@@ -4274,73 +3642,89 @@ procedure TInvControl2Obj.CalcQWPcurve_desiredpu(j: Integer);
pf_wp_nominal := Fwattpf_curve.GetYValue(FDCkW[j] * FEffFactor[j] * FpctDCkWRated[j] / FDCkWRated[j]);
- if ControlledElement[j].DSSClassName = 'PVSystem' then pf_priority := TPVSystemObj(ControlledElement[j]).PVSystemVars.PF_Priority
- else if ControlledElement[j].DSSClassName = 'Storage' then pf_priority := TStorageObj(ControlledElement[j]).StorageVars.PF_Priority;
-
- if (FPPriority[j] = FALSE) and (pf_priority = FALSE) then p := FDCkW[j] * FEffFactor[j] * FpctDCkWRated[j]
- else p := kW_out_desired[j];
+ if ControlledElement[j].DSSClassName = 'PVSystem' then
+ pf_priority := TPVSystemObj(ControlledElement[j]).PVSystemVars.PF_Priority
+ else
+ if ControlledElement[j].DSSClassName = 'Storage' then
+ pf_priority := TStorageObj(ControlledElement[j]).StorageVars.PF_Priority;
- QDesiredWP := p * sqrt(1 / (pf_wp_nominal * pf_wp_nominal) -1) * sign(pf_wp_nominal);
+ if (FPPriority[j] = FALSE) and (pf_priority = FALSE) then
+ p := FDCkW[j] * FEffFactor[j] * FpctDCkWRated[j]
+ else
+ p := kW_out_desired[j];
+ QDesiredWP := p * sqrt(1 / (pf_wp_nominal * pf_wp_nominal) - 1) * sign(pf_wp_nominal);
- if QDesiredWP >= 0.0 then QDesireWPpu[j] := QDesiredWP / QHeadRoom[j]
- else QDesireWPpu[j] := QDesiredWP / QHeadRoomNeg[j];
- end;
+ if QDesiredWP >= 0.0 then
+ QDesireWPpu[j] := QDesiredWP / QHeadRoom[j]
+ else
+ QDesireWPpu[j] := QDesiredWP / QHeadRoomNeg[j];
+end;
procedure TInvControl2Obj.CalcQDRC_desiredpu(j: Integer);
- VAR
- basekV :Double;
-
- begin
+var
+ basekV: Double;
+begin
QDesireDRCpu[j] := 0.0;
- basekV := FVBase[j] / 1000.0; // It's a line-to-ground voltage
+ basekV := FVBase[j] / 1000.0; // It's a line-to-ground voltage
// calculate deltaV quantity in per-unit from subtracting the rolling average
// value (in p.u.) from the present p.u. terminal voltage (average of line-ground)
// if more than one phase
- if(FDRCRollAvgWindow[j].Get_AvgVal/(basekV*1000.0)) = 0.0 then deltaVDynReac[j]:=0
- else deltaVDynReac[j] := FPresentDRCVpu[j] - FDRCRollAvgWindow[j].Get_AvgVal/(basekV*1000.0);
+ if (FDRCRollAvgWindow[j].AvgVal / (basekV * 1000.0)) = 0.0 then
+ deltaVDynReac[j] := 0
+ else
+ deltaVDynReac[j] := FPresentDRCVpu[j] - FDRCRollAvgWindow[j].AvgVal / (basekV * 1000.0);
// if below the lower deadband and deltaV quantity is non-zero then
// calculate desired pu var output. In per-unit of kva rating (also
// ampere rating), per report specifications.
- if (deltaVDynReac[j] <>0) and (FPresentDRCVpu[j] < FDbVMin) then QDesireDRCpu[j] := -deltaVDynReac[j]*FArGraLowV
+ if (deltaVDynReac[j] <> 0) and (FPresentDRCVpu[j] < FDbVMin) then
+ QDesireDRCpu[j] := -deltaVDynReac[j] * FArGraLowV
// if above the upper deadband and deltaV quantity is non-zero then
// calculate desired pu var output. In per-unit of kva rating (also
// ampere rating), per report specifications.
- else if (deltaVDynReac[j] <>0) and (FPresentDRCVpu[j] > FDbVMax) then QDesireDRCpu[j] := -deltaVDynReac[j]*FArGraHiV
-
- else if deltaVDynReac[j] = 0.0 then QDesireDRCpu[j] := 0.0;
+ else
+ if (deltaVDynReac[j] <> 0) and (FPresentDRCVpu[j] > FDbVMax) then
+ QDesireDRCpu[j] := -deltaVDynReac[j] * FArGraHiV
- if (ActiveCircuit.Solution.Dynavars.t=1) then QDesireDRCpu[j] := 0.0;
+ else
+ if deltaVDynReac[j] = 0.0 then
+ QDesireDRCpu[j] := 0.0;
- end;
+ if (ActiveCircuit.Solution.Dynavars.t = 1) then
+ QDesireDRCpu[j] := 0.0;
+end;
procedure TInvControl2Obj.Check_Qlimits_WV(j: Integer; Q: Double);
- VAR
+var
// Q_Ppriority :Double;
- currentkvarlimitpu :Double;
- currentkvarlimitnegpu :Double;
- FOperation :Double;
- error :Double;
-
- begin
+ currentkvarlimitpu: Double;
+ currentkvarlimitnegpu: Double;
+ FOperation: Double;
+ error: Double;
+begin
// Will organize this part into functions later
// states
error := 0;
- if (ControlMode = WATTVAR) then error := 0.005;
+ if (ControlMode = WATTVAR) then
+ error := 0.005;
- if Q < -error then FOperation := -1.0
- else if Q > error then FOperation := 1.0
- else FOperation := 0.0;
+ if Q < -error then
+ FOperation := -1.0
+ else
+ if Q > error then
+ FOperation := 1.0
+ else
+ FOperation := 0.0;
QDesireLimitedpu[j] := 1.0; // Not limited
@@ -4348,64 +3732,67 @@ procedure TInvControl2Obj.Check_Qlimits_WV(j: Integer; Q: Double);
currentkvarlimitpu := FCurrentkvarLimit[j] / QHeadRoom[j];
currentkvarlimitnegpu := FCurrentkvarLimitNeg[j] / QHeadRoomNeg[j];
- if currentkvarlimitpu > QDesireLimitedpu[j] then currentkvarlimitpu := QDesireLimitedpu[j];
- if currentkvarlimitnegpu > QDesireLimitedpu[j] then currentkvarlimitnegpu := QDesireLimitedpu[j];
+ if currentkvarlimitpu > QDesireLimitedpu[j] then
+ currentkvarlimitpu := QDesireLimitedpu[j];
+ if currentkvarlimitnegpu > QDesireLimitedpu[j] then
+ currentkvarlimitnegpu := QDesireLimitedpu[j];
// Q curve desiredpu should be less than currentkvarlimit(neg)
if (Q > 0.0) and (abs(Q) >= abs(currentkvarlimitpu)) then
- begin
+ begin
FOperation := 0.2 * sign(Q); // When kvarlimit is exceeded
QDesireLimitedpu[j] := currentkvarlimitpu * sign(Q);
- end
- else if (Q < 0.0) and (abs(Q) >= abs(currentkvarlimitnegpu)) then
- begin
+ end
+ else
+ if (Q < 0.0) and (abs(Q) >= abs(currentkvarlimitnegpu)) then
+ begin
FOperation := 0.2 * sign(Q); // When kvarlimitneg is exceeded
QDesireLimitedpu[j] := currentkvarlimitnegpu * sign(Q);
- end;
+ end;
// States Flags
- if (ControlMode = WATTVAR) then FWVOperation[j] := FOperation;
-
- end;
-
+ if (ControlMode = WATTVAR) then
+ FWVOperation[j] := FOperation;
+end;
procedure TInvControl2Obj.Calc_PQ_WV(j: Integer);
-VAR
- coeff: TCoeff;
+var
+ coeff: TCoeff;
// QPratio,
// pre_S,
- var_limit_operation_value,
- Pbase,
- Qbase,
+ var_limit_operation_value,
+ Pbase,
+ Qbase,
// Qbasesign,
- A,
- B,
- C,
- a_line,
- b_line: Double;
+ A,
+ B,
+ C,
+ a_line,
+ b_line: Double;
begin
+ Pbase := Min(FkVARating[j], FDCkWRated[j]);
- Pbase := Min(FkVARating[j], FDCkWRated[j]);
-
- if QDesiredWV[j] >= 0.0 then
+ if QDesiredWV[j] >= 0.0 then
begin
- Qbase := QHeadroom[j];
+ Qbase := QHeadroom[j];
// Qbasesign := 1.0;
end
- else
+ else
begin
- Qbase := QHeadroomNeg[j];
+ Qbase := QHeadroomNeg[j];
// Qbasesign := -1.0;
end;
- var_limit_operation_value := 0.2;
- if (abs(FWVOperation[j]) = var_limit_operation_value) then PLimitEndpu[j] := Fwattvar_curve.GetXValue(QDesireEndpu[j])
- else PLimitEndpu[j] := 1.0;
+ var_limit_operation_value := 0.2;
+ if (abs(FWVOperation[j]) = var_limit_operation_value) then
+ PLimitEndpu[j] := Fwattvar_curve.GetXValue(QDesireEndpu[j])
+ else
+ PLimitEndpu[j] := 1.0;
- CalcWATTVAR_vars(j);
+ CalcWATTVAR_vars(j);
// Qdesiredpu should be less than the Q avaliable under watt priority (works just for varmax)
- if (Sqrt(Sqr(FDCkW[j] * FEffFactor[j] * FpctDCkWRated[j] * PLimitEndpu[j]) + Sqr(QDesiredWV[j])) > FkVARating[j]) then
+ if (Sqrt(Sqr(FDCkW[j] * FEffFactor[j] * FpctDCkWRated[j] * PLimitEndpu[j]) + Sqr(QDesiredWV[j])) > FkVARating[j]) then
begin
coeff := Fwattvar_curve.GetCoefficients(FDCkW[j] * FEffFactor[j] * FpctDCkWRated[j] / Pbase);
@@ -4421,32 +3808,42 @@ procedure TInvControl2Obj.Calc_PQ_WV(j: Integer);
QDesireEndpu[j] := Fwattvar_curve.GetYValue(PLimitEndpu[j]);
end;
- CalcWATTVAR_vars(j)
+ CalcWATTVAR_vars(j)
end;
procedure TInvControl2Obj.Check_Qlimits(j: Integer; Q: Double);
- VAR
- Q_Ppriority :Double;
- currentkvarlimitpu :Double;
- currentkvarlimitnegpu :Double;
- FOperation :Double;
- error :Double;
-
- begin
+var
+ Q_Ppriority: Double;
+ currentkvarlimitpu: Double;
+ currentkvarlimitnegpu: Double;
+ FOperation: Double;
+ error: Double;
+begin
// states
error := 0;
- if (ControlMode = VOLTVAR) then error := 0.005;
- if (ControlMode = WATTPF) then error := 0.005;
- if (ControlMode = WATTVAR) then error := 0.005;
- if (ControlMode = DRC) then error := 0.0005;
- if (ControlMode = AVR) then error := 0.005;
- if (CombiControlMode = VV_DRC) then error := 0.005;
- if (CombiControlMode = VV_VW) then error := 0.005;
-
- if Q < -error then FOperation := -1.0
- else if Q > error then FOperation := 1.0
- else FOperation := 0.0;
+ if (ControlMode = VOLTVAR) then
+ error := 0.005;
+ if (ControlMode = WATTPF) then
+ error := 0.005;
+ if (ControlMode = WATTVAR) then
+ error := 0.005;
+ if (ControlMode = DRC) then
+ error := 0.0005;
+ if (ControlMode = AVR) then
+ error := 0.005;
+ if (CombiMode = VV_DRC) then
+ error := 0.005;
+ if (CombiMode = VV_VW) then
+ error := 0.005;
+
+ if Q < -error then
+ FOperation := -1.0
+ else
+ if Q > error then
+ FOperation := 1.0
+ else
+ FOperation := 0.0;
QDesireLimitedpu[j] := 1.0; // Not limited
@@ -4455,247 +3852,148 @@ procedure TInvControl2Obj.Check_Qlimits(j: Integer; Q: Double);
currentkvarlimitnegpu := FCurrentkvarLimitNeg[j] / QHeadRoomNeg[j];
if currentkvarlimitpu > QDesireLimitedpu[j] then
- currentkvarlimitpu := QDesireLimitedpu[j];
- if currentkvarlimitnegpu > QDesireLimitedpu[j] then
- currentkvarlimitnegpu := QDesireLimitedpu[j];
+ currentkvarlimitpu := QDesireLimitedpu[j];
+ if currentkvarlimitnegpu > QDesireLimitedpu[j] then
+ currentkvarlimitnegpu := QDesireLimitedpu[j];
// Q curve desiredpu should be less than currentkvarlimit(neg)
if (Q > 0.0) and (abs(Q) >= abs(currentkvarlimitpu)) then
- begin
+ begin
FOperation := 0.2 * sign(Q); // When kvarlimit is exceeded
QDesireLimitedpu[j] := currentkvarlimitpu * sign(Q);
- end
- else if (Q < 0.0) and (abs(Q) >= abs(currentkvarlimitnegpu)) then
- begin
+ end
+ else
+ if (Q < 0.0) and (abs(Q) >= abs(currentkvarlimitnegpu)) then
+ begin
FOperation := 0.2 * sign(Q); // When kvarlimitneg is exceeded
QDesireLimitedpu[j] := currentkvarlimitnegpu * sign(Q);
- end;
+ end;
// Qdesiredpu should be less than the Q avaliable under watt priority (works just for varmax)
- if FPPriority[j] and ((FReacPower_ref = 'VARMAX') or (ControlMode = WATTPF)) then
- begin
- if Q >= 0.0 then Q_Ppriority := Sqrt(SQR(FkVARating[j]) - SQR(FpresentkW[j]))/QHeadRoom[j]
- else Q_Ppriority := Sqrt(SQR(FkVARating[j]) - SQR(FpresentkW[j]))/QHeadRoomNeg[j];
+ if FPPriority[j] and ((FReacPower_ref = ReacPower_VARMAX) or (ControlMode = WATTPF)) then
+ begin
+ if Q >= 0.0 then
+ Q_Ppriority := Sqrt(SQR(FkVARating[j]) - SQR(FpresentkW[j])) / QHeadRoom[j]
+ else
+ Q_Ppriority := Sqrt(SQR(FkVARating[j]) - SQR(FpresentkW[j])) / QHeadRoomNeg[j];
if (abs(Q_Ppriority) < abs(QDesireLimitedpu[j])) and (abs(Q_Ppriority) < abs(Q)) then
- begin
+ begin
FOperation := 0.6 * sign(Q); // kVA exceeded under watt priority is considered above
- if (abs(Q) < (0.01 / 100)) or (abs(Q_Ppriority) < epsilon) then FOperation := 0.0;
+ if (abs(Q) < (0.01 / 100)) or (abs(Q_Ppriority) < epsilon) then
+ FOperation := 0.0;
QDesireLimitedpu[j] := Q_Ppriority * sign(Q);
- end;
- end;
+ end;
+ end;
// States Flags
- if (ControlMode = VOLTVAR) then FVVOperation[j] := FOperation;
- if (ControlMode = WATTPF) then FWPOperation[j] := FOperation;
- if (ControlMode = WATTVAR) then FWVOperation[j] := FOperation;
- if (ControlMode = DRC) then FDRCOperation[j] := FOperation;
- if (ControlMode = AVR) then FAVROperation[j] := FOperation;
- if (CombiControlMode = VV_DRC) then FVVDRCOperation[j] := FOperation;
- if (CombiControlMode = VV_VW) then FVVOperation[j] := FOperation;
-
- end;
+ if (ControlMode = VOLTVAR) then
+ FVVOperation[j] := FOperation;
+ if (ControlMode = WATTPF) then
+ FWPOperation[j] := FOperation;
+ if (ControlMode = WATTVAR) then
+ FWVOperation[j] := FOperation;
+ if (ControlMode = DRC) then
+ FDRCOperation[j] := FOperation;
+ if (ControlMode = AVR) then
+ FAVROperation[j] := FOperation;
+ if (CombiMode = VV_DRC) then
+ FVVDRCOperation[j] := FOperation;
+ if (CombiMode = VV_VW) then
+ FVVOperation[j] := FOperation;
+end;
procedure TInvControl2Obj.Calc_QHeadRoom(j: Integer);
- begin
-
- if FReacPower_ref = 'VARAVAL' then
- begin
- if(abs(FpresentkW[j]) < FkVARating[j]) then
- QHeadRoom[j] := SQRT(Sqr(FkVARating[j])-Sqr(FpresentkW[j]))
+begin
+ if FReacPower_ref = ReacPower_VARAVAL then
+ begin
+ if (abs(FpresentkW[j]) < FkVARating[j]) then
+ QHeadRoom[j] := SQRT(Sqr(FkVARating[j]) - Sqr(FpresentkW[j]))
else
- QHeadRoom[j] := 0.0;
+ QHeadRoom[j] := 0.0;
QHeadRoomNeg[j] := QHeadRoom[j];
- end;
+ end;
- if (FReacPower_ref = 'VARMAX') or (ControlMode = WATTPF) then
- begin
+ if (FReacPower_ref = ReacPower_VARMAX) or (ControlMode = WATTPF) then
+ begin
QHeadRoom[j] := FkvarLimit[j];
QHeadRoomNeg[j] := FkvarLimitNeg[j];
- end;
-
- if(QHeadRoom[j] = 0.0) then QHeadRoom[j] := FkvarLimit[j];
- if(QHeadRoomNeg[j] = 0.0) then QHeadRoomNeg[j] := FkvarLimitNeg[j];
+ end;
- end;
+ if (QHeadRoom[j] = 0.0) then
+ QHeadRoom[j] := FkvarLimit[j];
+ if (QHeadRoomNeg[j] = 0.0) then
+ QHeadRoomNeg[j] := FkvarLimitNeg[j];
+end;
procedure TInvControl2Obj.Change_deltaQ_factor(j: Integer);
- VAR
- DeltaV :Double;
+var
+ DeltaV: Double;
- begin
+begin
DeltaV := Abs(FPresentVpu[j] - FAvgpVpuPrior[j]);
if (DeltaV_old[j] >= 0.0) then
- begin
- if (abs(DeltaV) > 0.8 * DeltaV_old[j]) and (FdeltaQFactor[j] > 0.2) then FdeltaQFactor[j] := FdeltaQFactor[j] - 0.1
- else if (abs(DeltaV) > 0.6 * DeltaV_old[j]) and (FdeltaQFactor[j] > 0.2) then FdeltaQFactor[j] := FdeltaQFactor[j] - 0.05
- else if (abs(DeltaV) < 0.2 * DeltaV_old[j]) and (FdeltaQFactor[j] < 0.9) then FdeltaQFactor[j] := FdeltaQFactor[j] + 0.1
- else if (abs(DeltaV) < 0.4 * DeltaV_old[j]) and (FdeltaQFactor[j] < 0.9) then FdeltaQFactor[j] := FdeltaQFactor[j] + 0.05;
- end;
+ begin
+ if (abs(DeltaV) > 0.8 * DeltaV_old[j]) and (FdeltaQFactor[j] > 0.2) then
+ FdeltaQFactor[j] := FdeltaQFactor[j] - 0.1
+ else
+ if (abs(DeltaV) > 0.6 * DeltaV_old[j]) and (FdeltaQFactor[j] > 0.2) then
+ FdeltaQFactor[j] := FdeltaQFactor[j] - 0.05
+ else
+ if (abs(DeltaV) < 0.2 * DeltaV_old[j]) and (FdeltaQFactor[j] < 0.9) then
+ FdeltaQFactor[j] := FdeltaQFactor[j] + 0.1
+ else
+ if (abs(DeltaV) < 0.4 * DeltaV_old[j]) and (FdeltaQFactor[j] < 0.9) then
+ FdeltaQFactor[j] := FdeltaQFactor[j] + 0.05;
+ end;
DeltaV_old[j] := Abs(FPresentVpu[j] - FAvgpVpuPrior[j]);
- end;
+end;
procedure TInvControl2Obj.Change_deltaP_factor(j: Integer);
- VAR
- DeltaV :Double;
+var
+ DeltaV: Double;
- begin
+begin
DeltaV := Abs(FPresentVpu[j] - FAvgpVpuPrior[j]);
if DeltaV_old[j] >= 0.0 then
- begin
- if (abs(DeltaV) > 0.9 * DeltaV_old[j]) and (FdeltaPFactor[j] > 0.2) then FdeltaPFactor[j] := FdeltaPFactor[j] - 0.1
- else if (abs(DeltaV) > 0.8 * DeltaV_old[j]) and (FdeltaPFactor[j] > 0.1) then FdeltaPFactor[j] := FdeltaPFactor[j] - 0.05
- else if (abs(DeltaV) < 0.2 * DeltaV_old[j]) and (FdeltaPFactor[j] < 0.9) then FdeltaPFactor[j] := FdeltaPFactor[j] + 0.05
- else if (abs(DeltaV) < 0.1 * DeltaV_old[j]) and (FdeltaPFactor[j] < 0.9) then FdeltaPFactor[j] := FdeltaPFactor[j] + 0.1;
- end;
+ begin
+ if (abs(DeltaV) > 0.9 * DeltaV_old[j]) and (FdeltaPFactor[j] > 0.2) then
+ FdeltaPFactor[j] := FdeltaPFactor[j] - 0.1
+ else
+ if (abs(DeltaV) > 0.8 * DeltaV_old[j]) and (FdeltaPFactor[j] > 0.1) then
+ FdeltaPFactor[j] := FdeltaPFactor[j] - 0.05
+ else
+ if (abs(DeltaV) < 0.2 * DeltaV_old[j]) and (FdeltaPFactor[j] < 0.9) then
+ FdeltaPFactor[j] := FdeltaPFactor[j] + 0.05
+ else
+ if (abs(DeltaV) < 0.1 * DeltaV_old[j]) and (FdeltaPFactor[j] < 0.9) then
+ FdeltaPFactor[j] := FdeltaPFactor[j] + 0.1;
+ end;
DeltaV_old[j] := Abs(FPresentVpu[j] - FAvgpVpuPrior[j]);
- end;
-
+end;
//Called at end of main power flow solution loop
procedure TInvControl2.UpdateAll();
- VAR
- i : Integer;
-
- begin
-
- for i := 1 to ElementList.Count do
- with TInvControl2Obj(ElementList.Get(i)) do
- if Enabled then UpdateInvControl(i);
-
- end;
-
-
-
-procedure TRollAvgWindow.Add(IncomingSampleValue: Double;IncomingSampleTime: Double;VAvgWindowLengthSec:Double);
- begin
- {$IFNDEF FPC}
- if(sample.Count > 0) and (bufferfull) then
- begin
- runningsumsample := runningsumsample - sample.Dequeue;
- if(bufferlength = 0) then
- begin
- IncomingSampleValue := 0.0;
- end;
- sample.Enqueue(IncomingSampleValue);
- runningsumsample := runningsumsample + IncomingSampleValue;
- runningsumsampletime := runningsumsampletime - sampletime.Dequeue;
- sampletime.Enqueue(IncomingSampleTime);
- runningsumsampletime := runningsumsampletime +IncomingSampleTime;
- end
-
- else
- begin
- if(bufferlength = 0) then
- begin
- IncomingSampleValue := 0.0;
- end;
- sample.Enqueue(IncomingSampleValue);
- runningsumsample := runningsumsample + IncomingSampleValue;
- sampletime.Enqueue(IncomingSampleTime);
- runningsumsampletime := runningsumsampletime + IncomingSampleTime;
- if (runningsumsampletime > VAvgWindowLengthSec)
- then bufferfull := True;
- if (sample.Count = bufferlength)
- then bufferfull := True;
- end;
-
- {$else}
- if(sample.size > 0) and (bufferfull) then
- begin
- runningsumsample := runningsumsample - sample.front; sample.pop;
- if(bufferlength = 0) then
- begin
- IncomingSampleValue := 0.0;
- end;
- sample.push(IncomingSampleValue);
- runningsumsample := runningsumsample + IncomingSampleValue;
- runningsumsampletime := runningsumsampletime - sampletime.front; sampletime.pop;
- sampletime.push(IncomingSampleTime);
- runningsumsampletime := runningsumsampletime +IncomingSampleTime;
- end
- else
- begin
- if(bufferlength = 0) then
- begin
- IncomingSampleValue := 0.0;
- end;
- sample.push(IncomingSampleValue);
- runningsumsample := runningsumsample + IncomingSampleValue;
- sampletime.push(IncomingSampleTime);
- runningsumsampletime := runningsumsampletime + IncomingSampleTime;
- if (runningsumsampletime > VAvgWindowLengthSec)
- then bufferfull := True;
- if (sample.size = bufferlength)
- then bufferfull := True;
- end;
- {$ENDIF}
- end;
-
-constructor TRollAvgWindow.Create();
- begin
- sample := TQueue.Create();
- sampletime := TQueue.Create();
-
- runningsumsample := 0.0;
- runningsumsampletime := 0.0;
- bufferlength := 0;
- bufferfull := False;
- end;
-
-destructor TRollAvgWindow.Destroy;
- begin
- sample := nil;
- sampletime := nil;
-
- inherited;
- end;
-
-procedure TRollAvgWindow.Set_BuffLength(const Value: Integer);
- begin
- bufferlength := Value;
- end;
-
-function TRollAvgWindow.Get_AvgVal: Double;
- begin
- {$IFNDEF FPC}
- if(sample.Count = 0) then
- Result:= 0.0
- else Result:= runningsumsample / sample.Count;
- {$else}
- if(sample.size = 0) then
- Result:= 0.0
- else Result:= runningsumsample / sample.size;
- {$ENDIF}
- end;
-
-function TRollAvgWindow.Get_AccumSec: Double;
- begin
- {$IFNDEF FPC}
- if(sample.Count = 0) then
- Result:= 0.0
- else Result:= runningsumsampletime;
- {$else}
- if(sample.size = 0) then
- Result:= 0.0
- else Result:= runningsumsampletime;
- {$ENDIF}
- end;
-
-
-INITIALIZATION
-
-
-
-Finalization
-
+var
+ i: Integer;
+begin
+ for i := 1 to ElementList.Count do
+ with TInvControl2Obj(ElementList.Get(i)) do
+ if Enabled then
+ UpdateInvControl(i);
+end;
+finalization ModeEnum.Free;
+ CombiModeEnum.Free;
+ VoltageCurveXRefEnum.Free;
+ VoltWattYAxisEnum.Free;
+ RoCEnum.Free;
+ RefQEnum.Free;
end.
-
diff --git a/src/Controls/Recloser.pas b/src/Controls/Recloser.pas
index e3489dd67..ffa677cd6 100644
--- a/src/Controls/Recloser.pas
+++ b/src/Controls/Recloser.pas
@@ -6,26 +6,6 @@
All rights reserved.
----------------------------------------------------------
}
-
-{
- Created 11-1-00 from Relay Control
-
-
-}
-{
- A Recloser is a control element that is connected to a terminal of a
- circuit element and controls the switches in the same or another terminal.
-
- The control is usually placed in the
- terminal of a line or transformer, but it could be any element
-
- CktElement to be controlled must already exist.
-
- 7-18-2002 Fixed typos in help
- 5-1-2006 Added Time Delays to be compatible with relays
-
-}
-
interface
uses
@@ -36,39 +16,61 @@ interface
CktElement,
DSSClass,
Arraydef,
- ucomplex,
+ UComplex, DSSUcomplex,
utilities,
TCC_Curve,
Math;
type
+{$SCOPEDENUMS ON}
+ TRecloserProp = (
+ INVALID = 0,
+ MonitoredObj = 1,
+ MonitoredTerm = 2,
+ SwitchedObj = 3,
+ SwitchedTerm = 4,
+ NumFast = 5,
+ PhaseFast = 6,
+ PhaseDelayed = 7,
+ GroundFast = 8,
+ GroundDelayed = 9,
+ PhaseTrip = 10,
+ GroundTrip = 11,
+ PhaseInst = 12,
+ GroundInst = 13,
+ Reset = 14,
+ Shots = 15,
+ RecloseIntervals = 16,
+ Delay = 17,
+ Action = 18,
+ TDPhFast = 19,
+ TDGrFast = 20,
+ TDPhDelayed = 21,
+ TDGrDelayed = 22,
+ Normal = 23,
+ State = 24
+ );
+{$SCOPEDENUMS OFF}
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TRecloser = class(TControlClass)
- PRIVATE
- function GetTccCurve(const CurveName: String): TTCC_CurveObj;
PROTECTED
- procedure DefineProperties;
- function MakeLike(const RecloserName: String): Integer; OVERRIDE;
+ TCC_CurveClass: TDSSClass;
+
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TRecloserObj = class(TControlElem)
PRIVATE
-
PhaseDelayed,
GroundDelayed,
PhaseFast,
GroundFast: TTCC_CurveObj;
-
ResetTime,
DelayTime,
TDGrDelayed,
@@ -76,7 +78,7 @@ TRecloserObj = class(TControlElem)
TDGrFast,
TDPhFast: Double;
- FPresentState, FNormalState: EControlAction;
+ FPresentState: EControlAction;
OperationCount: Integer;
@@ -84,59 +86,44 @@ TRecloserObj = class(TControlElem)
ArmedForClose,
ArmedForOpen,
GroundTarget,
- PhaseTarget,
- NormalStateSet: Boolean;
+ PhaseTarget: Boolean;
CondOffset: Integer; // Offset for monitored terminal
cBuffer: pComplexArray; // Complexarray buffer
- procedure InterpretRecloserState(const Action: String; const PropertyName: String);
function get_PresentState: EControlAction; //TODO: check why this function even exists (also in Relay)
procedure set_PresentState(const Value: EControlAction);
- procedure set_NormalState(const Value: EControlAction);
-
- function GetTccCurve(const CurveName: String): TTCC_CurveObj;
PUBLIC
-
- RecloseIntervals: pdoubleArray;
+ NormalState: EControlAction;
+ NormalStateSet: Boolean;
+ RecloseIntervals: pdoubleArray; //TODO: make a fixed-size array
NumFast,
NumReclose: Integer;
- MonitoredElementName: String;
MonitoredElementTerminal: Integer;
PhaseTrip,
GroundTrip,
PhaseInst,
GroundInst: Double;
-
constructor Create(ParClass: TDSSClass; const RecloserName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
procedure RecalcElementData; OVERRIDE;
- procedure CalcYPrim; OVERRIDE; // Always Zero for a Recloser
procedure Sample; OVERRIDE; // Sample control quantities and set action times in Control Queue
procedure DoPendingAction(const Code, ProxyHdl: Integer); OVERRIDE; // Do the action that is pending from last sample
procedure Reset; OVERRIDE; // Reset to initial defined state
-
- procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present value of terminal Curr
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
property PresentState: EControlAction read get_PresentState write set_PresentState;
- property NormalState: EControlAction read FNormalState write set_NormalState;
-
end;
-{--------------------------------------------------------------------------}
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Circuit,
@@ -147,349 +134,216 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TRecloserObj;
+ TProp = TRecloserProp;
const
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
+ ActionEnum, StateEnum: TDSSEnum;
- NumPropsThisClass = 24;
-
- CURRENT = 0; {Default}
- VOLTAGE = 1;
- REVPOWER = 3;
-
-function TRecloser.GetTccCurve(const CurveName: String): TTCC_CurveObj;
-begin
- Result := DSS.TCC_CurveClass.Find(CurveName);
- if Result = NIL then
- DoSimpleMsg('TCC Curve object: "' + CurveName + '" not found.', 388);
-end;
-
-function TRecloserObj.GetTccCurve(const CurveName: String): TTCC_CurveObj;
-begin
- Result := DSS.TCC_CurveClass.Find(CurveName);
- if Result = NIL then
- DoSimpleMsg('TCC Curve object: "' + CurveName + '" not found.', 388);
-end;
-
-
-{--------------------------------------------------------------------------}
-constructor TRecloser.Create(dssContext: TDSSContext); // Creates superstructure for all Recloser objects
+constructor TRecloser.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
-
- Class_name := 'Recloser';
- DSSClassType := DSSClassType + RECLOSER_CONTROL;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ ActionEnum := TDSSEnum.Create('Recloser: Action', False, 1, 1,
+ ['close', 'open', 'trip'],
+ [ord(CTRL_CLOSE), ord(CTRL_OPEN), ord(CTRL_OPEN)]);
+ StateEnum := TDSSEnum.Create('Recloser: State', False, 1, 1,
+ ['closed', 'open', 'trip'],
+ [ord(CTRL_CLOSE), ord(CTRL_OPEN), ord(CTRL_OPEN)]);
+ end;
- DefineProperties;
+ TCC_CurveClass := GetDSSClassPtr(dssContext, 'TCC_Curve');
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, RECLOSER_CONTROL, 'Recloser');
end;
-{--------------------------------------------------------------------------}
destructor TRecloser.Destroy;
-
begin
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TRecloser.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
-
- PropertyName[1] := 'MonitoredObj';
- PropertyName[2] := 'MonitoredTerm';
- PropertyName[3] := 'SwitchedObj';
- PropertyName[4] := 'SwitchedTerm';
- PropertyName[5] := 'NumFast';
- PropertyName[6] := 'PhaseFast';
- PropertyName[7] := 'PhaseDelayed';
- PropertyName[8] := 'GroundFast';
- PropertyName[9] := 'GroundDelayed';
- PropertyName[10] := 'PhaseTrip';
- PropertyName[11] := 'GroundTrip';
- PropertyName[12] := 'PhaseInst';
- PropertyName[13] := 'GroundInst';
- PropertyName[14] := 'Reset';
- PropertyName[15] := 'Shots';
- PropertyName[16] := 'RecloseIntervals';
- PropertyName[17] := 'Delay';
- PropertyName[18] := 'Action';
- PropertyName[19] := 'TDPhFast';
- PropertyName[20] := 'TDGrFast';
- PropertyName[21] := 'TDPhDelayed';
- PropertyName[22] := 'TDGrDelayed';
- PropertyName[23] := 'Normal';
- PropertyName[24] := 'State';
-
- PropertyHelp[1] := 'Full object name of the circuit element, typically a line, transformer, load, or generator, ' +
- 'to which the Recloser''s PT and/or CT are connected.' +
- ' This is the "monitored" element. ' +
- 'There is no default; must be specified.';
- PropertyHelp[2] := 'Number of the terminal of the circuit element to which the Recloser is connected. ' +
- '1 or 2, typically. Default is 1.';
- PropertyHelp[3] := 'Name of circuit element switch that the Recloser controls. ' +
- 'Specify the full object name.' +
- 'Defaults to the same as the Monitored element. ' +
- 'This is the "controlled" element.';
- PropertyHelp[4] := 'Number of the terminal of the controlled element in which the switch is controlled by the Recloser. ' +
- '1 or 2, typically. Default is 1.';
- PropertyHelp[5] := 'Number of Fast (fuse saving) operations. Default is 1. (See "Shots")';
- PropertyHelp[6] := 'Name of the TCC Curve object that determines the Phase Fast trip. Must have been previously defined as a TCC_Curve object.' +
- ' Default is "A". ' +
- 'Multiplying the current values in the curve by the "phasetrip" value gives the actual current.';
- PropertyHelp[7] := 'Name of the TCC Curve object that determines the Phase Delayed trip. Must have been previously defined as a TCC_Curve object.' +
- ' Default is "D".' +
- 'Multiplying the current values in the curve by the "phasetrip" value gives the actual current.';
- PropertyHelp[8] := 'Name of the TCC Curve object that determines the Ground Fast trip. Must have been previously defined as a TCC_Curve object.' +
- ' Default is none (ignored). ' +
- 'Multiplying the current values in the curve by the "groundtrip" value gives the actual current.';
- PropertyHelp[9] := 'Name of the TCC Curve object that determines the Ground Delayed trip. Must have been previously defined as a TCC_Curve object.' +
- ' Default is none (ignored).' +
- 'Multiplying the current values in the curve by the "groundtrip" value gives the actual current.';
- PropertyHelp[10] := 'Multiplier or actual phase amps for the phase TCC curve. Defaults to 1.0.';
- PropertyHelp[11] := 'Multiplier or actual ground amps (3I0) for the ground TCC curve. Defaults to 1.0.';
- PropertyHelp[12] := 'Actual amps for instantaneous phase trip which is assumed to happen in 0.01 sec + Delay Time. Default is 0.0, which signifies no inst trip. ';
- PropertyHelp[13] := 'Actual amps for instantaneous ground trip which is assumed to happen in 0.01 sec + Delay Time.Default is 0.0, which signifies no inst trip.';
- PropertyHelp[14] := 'Reset time in sec for Recloser. Default is 15. ';
- PropertyHelp[15] := 'Total Number of fast and delayed shots to lockout. Default is 4. This is one more than the number of reclose intervals.';
- PropertyHelp[16] := 'Array of reclose intervals. Default for Recloser is (0.5, 2.0, 2.0) seconds. ' +
- 'A locked out Recloser must be closed manually (action=close).';
- PropertyHelp[17] := 'Fixed delay time (sec) added to Recloser trip time. Default is 0.0. Used to represent breaker time or any other delay.';
- PropertyHelp[18] := 'DEPRECATED. See "State" property';
- PropertyHelp[19] := 'Time dial for Phase Fast trip curve. Multiplier on time axis of specified curve. Default=1.0.';
- PropertyHelp[20] := 'Time dial for Ground Fast trip curve. Multiplier on time axis of specified curve. Default=1.0.';
- PropertyHelp[21] := 'Time dial for Phase Delayed trip curve. Multiplier on time axis of specified curve. Default=1.0.';
- PropertyHelp[22] := 'Time dial for Ground Delayed trip curve. Multiplier on time axis of specified curve. Default=1.0.';
- PropertyHelp[23] := '{Open | Closed} Normal state of the recloser. The recloser reverts to this state for reset, change of mode, etc. ' +
- 'Defaults to "State" if not specificallt declared.';
- PropertyHelp[24] := '{Open | Closed} Actual state of the recloser. Upon setting, immediately forces state of the recloser, overriding the Recloser control. ' +
- 'Simulates manual control on recloser. Defaults to Closed. "Open" causes the controlled element to open and lock out. "Closed" causes the ' +
- 'controlled element to close and the recloser to reset to its first operation.';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enums
+ PropertyType[ord(TProp.Action)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.Action)] := ptruint(@obj.FPresentState);
+ PropertyOffset2[ord(TProp.Action)] := PtrInt(ActionEnum);
+ PropertyFlags[ord(TProp.Action)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.Action)] := ord(TProp.State);
+
+ PropertyType[ord(TProp.State)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.State)] := ptruint(@obj.FPresentState);
+ PropertyOffset2[ord(TProp.State)] := PtrInt(StateEnum);
+
+ PropertyType[ord(TProp.Normal)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.Normal)] := ptruint(@obj.NormalState);
+ PropertyOffset2[ord(TProp.Normal)] := PtrInt(StateEnum);
+
+ // double arrays/vectors
+ PropertyType[ord(TProp.RecloseIntervals)] := TPropertyType.DoubleVArrayProperty;
+ PropertyOffset[ord(TProp.RecloseIntervals)] := ptruint(@obj.RecloseIntervals);
+ PropertyOffset2[ord(TProp.RecloseIntervals)] := ptruint(@obj.NumReclose);
+ PropertyOffset3[ord(TProp.RecloseIntervals)] := 4;
+ PropertyFlags[ord(TProp.RecloseIntervals)] := [TPropertyFlag.ArrayMaxSize];
+
+ // object properties
+ PropertyType[ord(TProp.PhaseFast)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.PhaseFast)] := ptruint(@obj.PhaseFast);
+ PropertyOffset2[ord(TProp.PhaseFast)] := ptruint(TCC_CurveClass);
+
+ PropertyType[ord(TProp.PhaseDelayed)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.PhaseDelayed)] := ptruint(@obj.PhaseDelayed);
+ PropertyOffset2[ord(TProp.PhaseDelayed)] := ptruint(TCC_CurveClass);
+
+ PropertyType[ord(TProp.GroundFast)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.GroundFast)] := ptruint(@obj.GroundFast);
+ PropertyOffset2[ord(TProp.GroundFast)] := ptruint(TCC_CurveClass);
+
+ PropertyType[ord(TProp.GroundDelayed)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.GroundDelayed)] := ptruint(@obj.GroundDelayed);
+ PropertyOffset2[ord(TProp.GroundDelayed)] := ptruint(TCC_CurveClass);
+
+ PropertyType[ord(TProp.MonitoredObj)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.MonitoredObj)] := ptruint(@obj.FMonitoredElement);
+ PropertyOffset2[ord(TProp.MonitoredObj)] := 0;
+ PropertyWriteFunction[ord(TProp.MonitoredObj)] := @SetMonitoredElement;
+ PropertyFlags[ord(TProp.MonitoredObj)] := [TPropertyFlag.WriteByFunction];
+ //[TPropertyFlag.CheckForVar]; // not required for general cktelements
+
+ PropertyType[ord(TProp.SwitchedObj)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.SwitchedObj)] := ptruint(@obj.FControlledElement);
+ PropertyOffset2[ord(TProp.SwitchedObj)] := 0;
+ PropertyWriteFunction[ord(TProp.SwitchedObj)] := @SetControlledElement;
+ PropertyFlags[ord(TProp.SwitchedObj)] := [TPropertyFlag.WriteByFunction];
+ //[TPropertyFlag.CheckForVar]; // not required for general cktelements
+
+ // integer properties
+ PropertyType[ord(TProp.SwitchedTerm)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.MonitoredTerm)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.NumFast)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.SwitchedTerm)] := ptruint(@obj.ElementTerminal);
+ PropertyOffset[ord(TProp.MonitoredTerm)] := ptruint(@obj.MonitoredElementTerminal);
+ PropertyOffset[ord(TProp.NumFast)] := ptruint(@obj.NumFast);
+
+ PropertyType[ord(TProp.Shots)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Shots)] := ptruint(@obj.NumReclose);
+ PropertyFlags[ord(TProp.Shots)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero, TPropertyFlag.ValueOffset];
+ PropertyValueOffset[ord(TProp.Shots)] := -1;
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.PhaseTrip)] := ptruint(@obj.PhaseTrip);
+ PropertyOffset[ord(TProp.GroundTrip)] := ptruint(@obj.GroundTrip);
+ PropertyOffset[ord(TProp.PhaseInst)] := ptruint(@obj.PhaseInst);
+ PropertyOffset[ord(TProp.GroundInst)] := ptruint(@obj.GroundInst);
+ PropertyOffset[ord(TProp.Reset)] := ptruint(@obj.Resettime);
+ PropertyOffset[ord(TProp.Delay)] := ptruint(@obj.DelayTime);
+ PropertyOffset[ord(TProp.TDPhFast)] := ptruint(@obj.TDPhFast);
+ PropertyOffset[ord(TProp.TDGrFast)] := ptruint(@obj.TDGrFast);
+ PropertyOffset[ord(TProp.TDPhDelayed)] := ptruint(@obj.TDPhDelayed);
+ PropertyOffset[ord(TProp.TDGrDelayed)] := ptruint(@obj.TDGrDelayed);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ inherited DefineProperties;
end;
-{--------------------------------------------------------------------------}
-function TRecloser.NewObject(const ObjName: String): Integer;
+function TRecloser.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new Recloser and add it to Recloser class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TRecloserObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-{--------------------------------------------------------------------------}
-
-
-{--------------------------------------------------------------------------}
-function TRecloser.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
+procedure TRecloserObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
-
- // continue parsing WITH contents of Parser
- DSS.ActiveRecloserObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveRecloserObj;
-
- Result := 0;
-
- with DSS.ActiveRecloserObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 390);
- 1:
- MonitoredElementName := lowercase(param);
- 2:
- MonitoredElementTerminal := Parser.IntValue;
- 3:
- ElementName := lowercase(param);
- 4:
- ElementTerminal := Parser.IntValue;
- 5:
- NumFast := Parser.Intvalue;
- 6:
- PhaseFast := GetTccCurve(Param);
- 7:
- PhaseDelayed := GetTCCCurve(Param);
- 8:
- GroundFast := GetTccCurve(Param);
- 9:
- GroundDelayed := GetTCCCurve(Param);
- 10:
- PhaseTrip := Parser.Dblvalue;
- 11:
- GroundTrip := Parser.Dblvalue;
- 12:
- PhaseInst := Parser.Dblvalue;
- 13:
- GroundInst := Parser.Dblvalue;
- 14:
- Resettime := Parser.Dblvalue;
- 15:
- NumReclose := Parser.Intvalue - 1; // one less than number of shots
- 16:
- NumReclose := Parser.ParseAsVector(4, RecloseIntervals); // max of 4 allowed
- 17:
- DelayTime := Parser.DblValue;
- 19:
- TDPhFast := Parser.DblValue;
- 20:
- TDGrFast := Parser.DblValue;
- 21:
- TDPhDelayed := Parser.DblValue;
- 22:
- TDGrDelayed := Parser.DblValue;
- 23:
- begin
- InterpretRecloserState(Param, ParamName); // Set normal state
- NormalStateSet := true;
- end;
- 18, 24:
- InterpretRecloserState(Param, ParamName); // Set current state
-
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveRecloserObj, ParamPointer - NumPropsthisClass)
- end;
-
- case ParamPointer of
- {Default the controlled element to the monitored element}
- 1:
- ElementName := MonitoredElementName;
- 2:
- ElementTerminal := MonitoredElementTerminal;
- 18, 24:
- if not NormalStateSet then
- begin
- NormalStateSet := true;
- FNormalState := FPresentState;
- end;
+ case Idx of
+ ord(TProp.MonitoredObj):
+ // Default the controlled element to the monitored element
+ ControlledElement := MonitoredElement; //TODO: This can cause unexpected behavior
+ ord(TProp.MonitoredTerm):
+ ElementTerminal := MonitoredElementTerminal; //TODO: This can cause unexpected behavior
+ ord(TProp.Normal):
+ NormalStateSet := true;
+ ord(TProp.Action), ord(TProp.State):
+ if not NormalStateSet then
+ begin
+ NormalStateSet := true;
+ NormalState := FPresentState;
end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
- RecalcElementData;
end;
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-
-{--------------------------------------------------------------------------}
-function TRecloser.MakeLike(const RecloserName: String): Integer;
+procedure TRecloserObj.MakeLike(OtherPtr: Pointer);
var
- OtherRecloser: TRecloserObj;
+ Other: TObj;
i: Integer;
begin
- Result := 0;
- {See if we can find this Recloser name in the present collection}
- OtherRecloser := Find(RecloserName);
- if OtherRecloser <> NIL then
- with DSS.ActiveRecloserObj do
- begin
-
- NPhases := OtherRecloser.Fnphases;
- NConds := OtherRecloser.Fnconds; // Force Reallocation of terminal stuff
-
- ElementName := OtherRecloser.ElementName;
- ElementTerminal := OtherRecloser.ElementTerminal;
- ControlledElement := OtherRecloser.ControlledElement; // Pointer to target circuit element
-
- MonitoredElement := OtherRecloser.MonitoredElement; // Pointer to target circuit element
- MonitoredElementName := OtherRecloser.MonitoredElementName; // Pointer to target circuit element
- MonitoredElementTerminal := OtherRecloser.MonitoredElementTerminal; // Pointer to target circuit element
-
- PhaseDelayed := OtherRecloser.PhaseDelayed;
- GroundDelayed := OtherRecloser.GroundDelayed;
- PhaseFast := OtherRecloser.PhaseFast;
- GroundFast := OtherRecloser.GroundFast;
- PhaseTrip := OtherRecloser.PhaseTrip;
- GroundTrip := OtherRecloser.GroundTrip;
- PhaseInst := OtherRecloser.PhaseInst;
- GroundInst := OtherRecloser.GroundInst;
- Resettime := OtherRecloser.Resettime;
- NumReclose := OtherRecloser.NumReclose;
- NumFast := OtherRecloser.NumFast;
-
- Reallocmem(RecloseIntervals, SizeOf(RecloseIntervals^[1]) * 4); // Always make a max of 4
- for i := 1 to NumReclose do
- RecloseIntervals^[i] := OtherRecloser.RecloseIntervals^[i];
-
- LockedOut := OtherRecloser.LockedOut;
-
- FPresentState := OtherRecloser.FPresentState;
- FNormalState := OtherRecloser.FNormalState;
- NormalStateSet := OtherRecloser.NormalStateSet;
- CondOffset := OtherRecloser.CondOffset;
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherRecloser.PropertyValue[i];
-
- end
- else
- DoSimpleMsg('Error in Recloser MakeLike: "' + RecloserName + '" Not Found.', 391);
-
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
+
+ ElementTerminal := Other.ElementTerminal;
+ ControlledElement := Other.ControlledElement; // Pointer to target circuit element
+
+ MonitoredElement := Other.MonitoredElement; // Pointer to target circuit element
+ MonitoredElementTerminal := Other.MonitoredElementTerminal; // Pointer to target circuit element
+
+ PhaseDelayed := Other.PhaseDelayed;
+ GroundDelayed := Other.GroundDelayed;
+ PhaseFast := Other.PhaseFast;
+ GroundFast := Other.GroundFast;
+ PhaseTrip := Other.PhaseTrip;
+ GroundTrip := Other.GroundTrip;
+ PhaseInst := Other.PhaseInst;
+ GroundInst := Other.GroundInst;
+ Resettime := Other.Resettime;
+ NumReclose := Other.NumReclose;
+ NumFast := Other.NumFast;
+
+ Reallocmem(RecloseIntervals, SizeOf(RecloseIntervals^[1]) * 4); // Always make a max of 4
+ for i := 1 to NumReclose do
+ RecloseIntervals^[i] := Other.RecloseIntervals^[i];
+
+ LockedOut := Other.LockedOut;
+ FPresentState := Other.FPresentState;
+ NormalState := Other.NormalState;
+ NormalStateSet := Other.NormalStateSet;
+ CondOffset := Other.CondOffset;
end;
-
-{==========================================================================}
-{ TRecloserObj }
-{==========================================================================}
-
-
-{--------------------------------------------------------------------------}
constructor TRecloserObj.Create(ParClass: TDSSClass; const RecloserName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(RecloserName);
+ Name := AnsiLowerCase(RecloserName);
DSSObjType := ParClass.DSSClassType;
- NPhases := 3; // Directly set conds and phases
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
- Nterms := 1; // this forces allocation of terminals and conductors
- // in base class
-
+ Nterms := 1; // this forces allocation of terminals and conductors in base class
- ElementName := '';
ControlledElement := NIL;
ElementTerminal := 1;
- MonitoredElementName := '';
MonitoredElementTerminal := 1;
MonitoredElement := NIL;
- PhaseFast := GetTccCurve('a');
- PhaseDelayed := GetTccCurve('d');
+ PhaseFast := TRecloser(ParClass).TCC_CurveClass.Find('a'); //TODO: is an error message ever required for this?
+ PhaseDelayed := TRecloser(ParClass).TCC_CurveClass.Find('d'); //TODO: is an error message ever required for this?
GroundFast := NIL;
GroundDelayed := NIL;
@@ -513,12 +367,10 @@ constructor TRecloserObj.Create(ParClass: TDSSClass; const RecloserName: String)
RecloseIntervals^[2] := 2.0;
RecloseIntervals^[3] := 2.0;
-
FPresentState := CTRL_CLOSE;
- FNormalState := CTRL_CLOSE;
+ NormalState := CTRL_CLOSE;
NormalStateSet := FALSE;
-
Operationcount := 1;
LockedOut := FALSE;
ArmedForOpen := FALSE;
@@ -526,45 +378,32 @@ constructor TRecloserObj.Create(ParClass: TDSSClass; const RecloserName: String)
GroundTarget := FALSE;
PhaseTarget := FALSE;
-
cBuffer := NIL; // Complex buffer
DSSObjType := ParClass.DSSClassType; //cap_CONTROL;
- InitPropertyValues(0);
-
-
// RecalcElementData;
-
end;
destructor TRecloserObj.Destroy;
begin
- MonitoredElementName := '';
ReallocMem(RecloseIntervals, 0);
ReallocMem(cBuffer, 0);
inherited Destroy;
end;
-{--------------------------------------------------------------------------}
procedure TRecloserObj.RecalcElementData;
-
-var
- DevIndex: Integer;
-
begin
+ //TODO: still need to warn/error if elements are NIL?
- Devindex := GetCktElementIndex(MonitoredElementName); // Global function
- if DevIndex > 0 then
+ if MonitoredElement <> NIL then
begin
-
- MonitoredElement := ActiveCircuit.CktElements.Get(DevIndex);
- Nphases := MonitoredElement.NPhases; // Force number of phases to be same
+ FNphases := MonitoredElement.NPhases; // Force number of phases to be same
if MonitoredElementTerminal > MonitoredElement.Nterms then
begin
- DoErrorMsg('Recloser: "' + Name + '"',
- 'Terminal no. "' + '" does not exist.',
- 'Re-specify terminal no.', 392);
+ DoErrorMsg(Format(_('Recloser: "%s"'), [Name]),
+ Format(_('Terminal no. "%d" does not exist.'), [MonitoredElementTerminal]),
+ _('Re-specify terminal no.'), 392);
end
else
begin
@@ -576,27 +415,22 @@ procedure TRecloserObj.RecalcElementData;
end;
end;
-{Check for existence of Controlled Element}
+ // Check for existence of Controlled Element
- // If previously assigned, reset HasOCPDevice flag in case this is a move
- if Assigned(ControlledElement) then
+ // If previously assigned, reset HasOCPDevice flag in case this is a move
+ if ControlledElement <> NIL then
begin
- ControlledElement.HasOCPDevice := FALSE;
- ControlledElement.HasAutoOCPDevice := FALSE;
- end;
-
- Devindex := GetCktElementIndex(ElementName); // Global function
- if DevIndex > 0 then
- begin // Both CktElement and monitored element must already exist
-
- ControlledElement := ActiveCircuit.CktElements.Get(DevIndex);
+ Exclude(ControlledElement.Flags, Flg.HasOCPDevice);
+ Exclude(ControlledElement.Flags, Flg.HasAutoOCPDevice);
+
+ // Both CktElement and monitored element must already exist
ControlledElement.ActiveTerminalIdx := ElementTerminal; // Make the 1 st terminal active
- // If the recloser becomes disabled, leave at False
+ // If the recloser becomes disabled, leave at False
if Enabled then
- begin
- ControlledElement.HasOCPDevice := TRUE; // For Reliability calcs
- ControlledElement.HasAutoOCPDevice := TRUE; // For Reliability calcs
+ begin
+ Include(ControlledElement.Flags, Flg.HasOCPDevice); // For Reliability calcs
+ Include(ControlledElement.Flags, Flg.HasAutoOCPDevice); // For Reliability calcs
end;
// Open/Close State of controlled element based on state assigned to the control
@@ -614,52 +448,26 @@ procedure TRecloserObj.RecalcElementData;
OperationCount := NumReclose + 1;
ArmedForClose := FALSE;
end;
- end
- else
- begin
- ControlledElement := NIL; // element not found
- DoErrorMsg('Recloser: "' + Self.Name + '"', 'CktElement Element "' + ElementName + '" Not Found.',
- ' Element must be defined previously.', 393);
end;
end;
-procedure TRecloserObj.MakePosSequence;
+procedure TRecloserObj.MakePosSequence();
begin
if MonitoredElement <> NIL then
begin
- Nphases := MonitoredElement.NPhases;
+ FNphases := MonitoredElement.NPhases;
Nconds := FNphases;
Setbus(1, MonitoredElement.GetBus(ElementTerminal));
- // Allocate a buffer bigenough to hold everything from the monitored element
+ // Allocate a buffer bigenough to hold everything from the monitored element
ReAllocMem(cBuffer, SizeOF(cbuffer^[1]) * MonitoredElement.Yorder);
CondOffset := (ElementTerminal - 1) * MonitoredElement.NConds; // for speedy sampling
end;
inherited;
end;
-{--------------------------------------------------------------------------}
-procedure TRecloserObj.CalcYPrim;
-begin
- // leave YPrims as nil and they will be ignored
- // Yprim is zeroed when created. Leave it as is.
- // IF YPrim=nil THEN YPrim := TcMatrix.CreateMatrix(Yorder);
-end;
-
-{--------------------------------------------------------------------------}
-procedure TRecloserObj.GetCurrents(Curr: pComplexArray);
-var
- i: Integer;
-begin
-
- for i := 1 to Fnconds do
- Curr^[i] := CZERO;
-
-end;
-
-{--------------------------------------------------------------------------}
procedure TRecloserObj.DoPendingAction(const Code, ProxyHdl: Integer);
begin
- with ControlledElement do
+ with ControlledElement do
begin
ControlledElement.ActiveTerminalIdx := ElementTerminal; // Set active terminal of CktElement to terminal 1
case Code of
@@ -687,63 +495,35 @@ procedure TRecloserObj.DoPendingAction(const Code, ProxyHdl: Integer);
AppendtoEventLog(' ', 'Ground Target');
ArmedForOpen := FALSE;
end;
- else {nada}
+ else // Nada
end;
Integer(CTRL_CLOSE):
case FPresentState of
CTRL_OPEN:
if ArmedForClose and not LockedOut then
begin
- ControlledElement.Closed[0] := TRUE; // Close all phases of active terminal
+ ControlledElement.Closed[0] := TRUE; // Close all phases of active terminal
Inc(OperationCount);
AppendtoEventLog('Recloser.' + Self.Name, 'Closed');
ArmedForClose := FALSE;
end;
- else {Nada}
+ else // Nada
end;
Integer(CTRL_RESET):
case FPresentState of
CTRL_CLOSE:
if not ArmedForOpen then
- OperationCount := 1; // Don't reset if we just rearmed
- else {Nada}
+ OperationCount := 1; // Don't reset if we just rearmed
+ else // Nada
end;
else
- {Do Nothing }
+ // Do Nothing
end;
end;
end;
-{--------------------------------------------------------------------------}
-
-
-procedure TRecloserObj.InterpretRecloserState(const Action: String; const PropertyName: String);
-begin
- if (LowerCase(PropertyName[1]) = 's') or (LowerCase(PropertyName[1]) = 'a') then // state or action (deprecated)
- begin
- case LowerCase(Action[1]) of
- 'o','t':
- FPresentState := CTRL_OPEN;
- 'c':
- FPresentState := CTRL_CLOSE;
- end;
- end
- else
- begin
- // normal state
- case LowerCase(Action[1]) of
- 'o','t':
- FNormalState := CTRL_OPEN;
- 'c':
- FNormalState := CTRL_CLOSE;
- end;
- end;
-end;
-
-{--------------------------------------------------------------------------}
procedure TRecloserObj.Sample;
-
var
i: Integer;
cmag: Double;
@@ -752,22 +532,24 @@ procedure TRecloserObj.Sample;
GroundCurve, PhaseCurve: TTCC_CurveObj;
Groundtime, PhaseTime, TripTime, TimeTest: Double;
TDPhase, TDGround: Double;
-
begin
-
-
ControlledElement.ActiveTerminalIdx := ElementTerminal;
- if ControlledElement.Closed[0] // Check state of phases of active terminal
+ if ControlledElement.Closed[0] // Check state of phases of active terminal
then
FPresentState := CTRL_CLOSE
else
FPresentState := CTRL_OPEN;
-
- with MonitoredElement do
+ if MonitoredElement = NIL then
begin
+ DoSimpleMsg('Required property MonitoredObj is not defined for "%s".', [FullName], 9894);
+ DSS.SolutionAbort := True;
+ Exit;
+ end;
+ with MonitoredElement do
+ begin
if OperationCount > NumFast then
begin
GroundCurve := GroundDelayed;
@@ -789,15 +571,15 @@ procedure TRecloserObj.Sample;
GroundTime := -1.0;
PhaseTime := -1.0; {No trip}
- // Check largest Current of all phases of monitored element
+ // Check largest Current of all phases of monitored element
MonitoredElement.GetCurrents(cBuffer);
- {Check Ground Trip, if any}
+ // Check Ground Trip, if any
if GroundCurve <> NIL then
begin
Csum := CZERO;
for i := (1 + CondOffset) to (Fnphases + CondOffset) do
- caccum(Csum, cBuffer^[i]);
+ Csum += cBuffer^[i];
Cmag := Cabs(Csum);
if (GroundInst > 0.0) and (Cmag >= GroundInst) and (OperationCount = 1) then
GroundTime := 0.01 + DelayTime // Inst trip on first operation
@@ -811,9 +593,9 @@ procedure TRecloserObj.Sample;
GroundTarget := TRUE;
end;
- // If GroundTime > 0 then we have a ground trip
+ // If GroundTime > 0 then we have a ground trip
- {Check Phase Trip, if any}
+ // Check Phase Trip, if any
if PhaseCurve <> NIL then
begin
@@ -876,71 +658,13 @@ procedure TRecloserObj.Sample;
PhaseTarget := FALSE;
end;
end;
- end; {IF PresentState=CLOSE}
- end; {With}
-end;
-
-
-{--------------------------------------------------------------------------}
-procedure TRecloserObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
- if Complete then
- begin
- FSWriteln(F);
- end;
-
+ end; // IF PresentState=CLOSE
+ end; // With
end;
-function TRecloserObj.GetPropertyValue(Index: Integer): String;
-var
- i: Integer;
-begin
- Result := '';
- case Index of
- 15:
- Result := Format('%d', [NumReclose + 1]);
- 16:
- begin
- Result := '(';
- for i := 1 to NumReclose do
- Result := Result + Format('%-g, ', [RecloseIntervals^[i]]);
- Result := Result + ')';
- end;
- 23:
- begin
- if FNormalState = CTRL_OPEN then
- Result := 'open'
- else
- Result := 'closed';
- end;
- 18, 24:
- begin
- if FPresentState = CTRL_OPEN then
- Result := 'open'
- else
- Result := 'closed';
- end;
- else
- Result := inherited GetPropertyValue(index);
- end;
-end;
-
-
procedure TRecloserObj.Reset;
begin
- FPresentState := FNormalState;
+ FPresentState := NormalState;
ArmedForOpen := FALSE;
ArmedForClose := FALSE;
GroundTarget := FALSE;
@@ -949,9 +673,9 @@ procedure TRecloserObj.Reset;
if ControlledElement = NIL then
Exit;
- ControlledElement.ActiveTerminalIdx := ElementTerminal; // Set active terminal
+ ControlledElement.ActiveTerminalIdx := ElementTerminal; // Set active terminal
- if FNormalState = CTRL_OPEN then
+ if NormalState = CTRL_OPEN then
begin
ControlledElement.Closed[0] := FALSE; // Open all phases of active terminal
LockedOut := TRUE;
@@ -965,7 +689,7 @@ procedure TRecloserObj.Reset;
end;
end;
-function TRecloserObj.get_PresentState: EControlAction;
+function TRecloserObj.get_PresentState: EControlAction; //TODO: why GetPropertyValue doesn't use this one?
begin
if ControlledElement <> NIL then
begin
@@ -1007,43 +731,9 @@ function TRecloserObj.get_PresentState: EControlAction;
end;
end;
-procedure TRecloserObj.set_NormalState(const Value: EControlAction);
-begin
- FNormalState := Value;
- NormalStateSet := TRUE;
-end;
-
-procedure TRecloserObj.InitPropertyValues(ArrayOffset: Integer);
-begin
- PropertyValue[1] := ''; //'element';
- PropertyValue[2] := '1'; //'terminal';
- PropertyValue[3] := '';
- PropertyValue[4] := '1'; //'terminal';
- PropertyValue[5] := IntToStr(NumFast);
- PropertyValue[6] := '';
- PropertyValue[7] := '';
- PropertyValue[8] := '';
- PropertyValue[9] := '';
- PropertyValue[10] := '1.0';
- PropertyValue[11] := '1.0';
- PropertyValue[12] := '0';
- PropertyValue[13] := '0';
- PropertyValue[14] := '15';
- PropertyValue[15] := '4';
- PropertyValue[16] := '(0.5, 2.0, 2.0)';
- PropertyValue[17] := '0.0';
- PropertyValue[18] := 'closed';
- PropertyValue[19] := '1.0';
- PropertyValue[20] := '1.0';
- PropertyValue[21] := '1.0';
- PropertyValue[22] := '1.0';
- PropertyValue[23] := 'closed';
- PropertyValue[24] := 'closed';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
initialization
-
+ PropInfo := NIL;
+finalization
+ ActionEnum.Free;
+ StateEnum.Free;
end.
diff --git a/src/Controls/RegControl.pas b/src/Controls/RegControl.pas
index 717e80fad..26aac85c5 100644
--- a/src/Controls/RegControl.pas
+++ b/src/Controls/RegControl.pas
@@ -7,26 +7,16 @@
----------------------------------------------------------
}
-{
- Change Log
- 1-28-00 Created
- 4-29-00 fixed problem with NumPhases = # phases of controlled element
- 12/17/01 Added LDC logic
- 12/18/01 Added MaxTapChange property and logic
- 6/18/11 Updated Rev Power logic
- 12/4/2018 Added autotransformer control
-}
-
-{
- A RegControl is a control element that is connected to a terminal of another
- circuit element that must be a transformer.
+// 12/4/2018 Added autotransformer control
- A RegControl is defined by a New command:
-
- New RegControl.Name=myname Transformer = name Terminal=[1,2,...] Controlledbus=name etc...
-
- Transformer to be controlled must already exist.
-}
+// A RegControl is a control element that is connected to a terminal of another
+// circuit element that must be a transformer.
+//
+// A RegControl is defined by a New command:
+//
+// New RegControl.Name=myname Transformer = name Terminal=[1,2,...] Controlledbus=name etc...
+//
+// Transformer to be controlled must already exist.
interface
@@ -37,85 +27,94 @@ interface
ControlElem,
DSSClass,
Arraydef,
- ucomplex,
+ UComplex, DSSUcomplex,
Transformer,
AutoTrans,
utilities;
type
+{$SCOPEDENUMS ON}
+ TRegControlProp = (
+ INVALID = 0,
+ transformer = 1,
+ winding = 2,
+ vreg = 3,
+ band = 4,
+ ptratio = 5,
+ CTprim = 6,
+ R = 7,
+ X = 8,
+ bus = 9,
+ delay = 10,
+ reversible = 11,
+ revvreg = 12,
+ revband = 13,
+ revR = 14,
+ revX = 15,
+ tapdelay = 16,
+ debugtrace = 17,
+ maxtapchange = 18,
+ inversetime = 19,
+ tapwinding = 20,
+ vlimit = 21,
+ PTphase = 22,
+ revThreshold = 23,
+ revDelay = 24,
+ revNeutral = 25,
+ EventLog = 26,
+ RemotePTRatio = 27,
+ TapNum = 28,
+ Reset = 29,
+ LDC_Z = 30,
+ rev_Z = 31,
+ Cogen = 32
+ );
+{$SCOPEDENUMS OFF}
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TRegControl = class(TControlClass)
- PRIVATE
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const RegControlName: String): Integer; OVERRIDE;
+ Transf_Or_AutoTrans_ProxyClass: TProxyClass;
+
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TRegControlObj = class(TControlElem)
-{$IFDEF DSS_CAPI}
PUBLIC
-{$ELSE}
- PROTECTED
-{$ENDIF}
- procedure Set_Enabled(Value: Boolean); OVERRIDE;
-
- PRIVATE
+ LastChange: Integer;
- Vreg,
- Bandwidth,
- PTRatio,
RemotePTRatio,
- CTRating,
- R,
- X,
LDC_Z: Double;
- {Reverse Power Variables}
- revVreg,
- revBandwidth,
+ // Reverse Power Variables
RevPowerThreshold, // W
kWRevPowerThreshold,
revDelay,
- revR,
- revX,
revLDC_Z: Double;
- IsReversible: Boolean;
InReverseMode: Boolean;
ReversePending: Boolean;
- ReverseNeutral: Boolean;
- CogenEnabled: Boolean;
+ ReverseNeutral: LongBool;
+ CogenEnabled: LongBool;
InCogenMode: Boolean;
RevHandle: Integer;
RevBackHandle: Integer;
- LDCActive: Boolean;
UsingRegulatedBus: Boolean;
RegulatedBus: String;
- FPendingTapChange, // amount of tap change pending
- TapDelay: Double; // delay between taps
+ FPendingTapChange: Double; // amount of tap change pending
- DebugTrace: Boolean;
+ DebugTrace: LongBool;
Armed: Boolean;
TraceFile: TFileStream;
- TapLimitPerChange: Integer;
- TapWinding: Integer; // Added 7-19-07
- FInversetime: Boolean;
- Vlimit: Double;
- VLimitActive: Boolean;
+ TapWinding: Integer;
FPTphase: Integer;
ControlledPhase: Integer;
@@ -124,14 +123,16 @@ TRegControlObj = class(TControlElem)
VBuffer, CBuffer: pComplexArray;
+ procedure Set_Enabled(Value: Boolean); OVERRIDE;
+
function Get_Transformer: TTransfObj;
function Get_Winding: Integer;
- // CIM accessors
function Get_MinTap: Double;
function Get_MaxTap: Double;
function Get_TapIncrement: Double;
function Get_NumTaps: Integer;
function Get_TapNum: Integer;
+ procedure Set_TapNum(const Value: Integer);
procedure RegWriteTraceRecord(TapChangeMade: Double);
procedure RegWriteDebugRecord(S: String);
@@ -139,64 +140,53 @@ TRegControlObj = class(TControlElem)
function AtLeastOneTap(const ProposedChange: Double; Increment: Double): Double;
function ComputeTimeDelay(Vavg: Double): Double;
function GetControlVoltage(VBuffer: pComplexArray; Nphs: Integer; PTRatio: Double): Complex;
- procedure Set_TapNum(const Value: Integer);
-
PUBLIC
+ TapLimitPerChange: Integer; // MaxTapChange
+
+ InverseTime: LongBool; // IsInverseTime
+ LDCActive: Boolean; // UseLineDrop
+ IsReversible: LongBool; // UseReverseDrop
+
+ TapDelay, // delay between taps // SubsequentDelay
+ Vlimit, // VoltageLimit
+ CTRating, // CT
+ PTRatio, // PT
+ Vreg, // TargetVoltage
+ revVreg, // RevTargetVoltage
+ Bandwidth, // BandVoltage
+ revBandwidth, // RevBandVoltage
+ R, // LineDropR
+ X, // LineDropX
+ revR, // RevLineDropR
+ revX: Double; // RevLineDropX
constructor Create(ParClass: TDSSClass; const RegControlName: String);
destructor Destroy; OVERRIDE;
-
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
- procedure CalcYPrim; OVERRIDE; // Always Zero for a RegControl
-
procedure Sample; OVERRIDE; // Sample control quantities and set action times in Control Queue
procedure DoPendingAction(const Code, ProxyHdl: Integer); OVERRIDE; // Do the action that is pending from last sample
procedure Reset; OVERRIDE; // Reset to initial defined state
-
-
- procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present value of terminal Curr
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- procedure SaveWrite(F: TFileStream); OVERRIDE;
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
property Transformer: TTransfObj READ Get_Transformer; // Pointer to controlled Transformer
property TrWinding: Integer READ Get_Winding; // Report Tapped winding
-
property PendingTapChange: Double READ FPendingTapChange WRITE set_PendingTapChange;
-
- // CIM XML accessors
- property TargetVoltage: Double READ Vreg;
- property BandVoltage: Double READ BandWidth;
- property CT: Double READ CTRating;
- property PT: Double READ PTRatio;
- property LineDropR: Double READ R;
- property LineDropX: Double READ X;
- property RevLineDropR: Double READ revR;
- property RevLineDropX: Double READ revX;
- property RevTargetVoltage: Double READ revVreg;
- property RevBandVoltage: Double READ revBandWidth;
- property UseLineDrop: Boolean READ LDCActive;
- property UseReverseDrop: Boolean READ IsReversible;
- property UseLimit: Boolean READ VLimitActive;
- property VoltageLimit: Double READ VLimit;
- property InitialDelay: Double READ TimeDelay;
- property SubsequentDelay: Double READ TapDelay;
+ function VLimitActive: Boolean;
+ // property InitialDelay: Double READ TimeDelay;
property MinTap: Double READ Get_MinTap;
property MaxTap: Double READ Get_MaxTap;
property TapIncrement: Double READ Get_TapIncrement;
property NumTaps: Integer READ Get_NumTaps;
- property MaxTapChange: Integer READ TapLimitPerChange;
- property IsInverseTime: Boolean READ FInverseTime;
property TapNum: Integer READ Get_TapNum WRITE Set_TapNum;
end;
-{--------------------------------------------------------------------------}
implementation
uses
- ParserDel,
+ BufStream,
DSSClassDefs,
DSSGlobals,
Circuit,
@@ -209,406 +199,245 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TRegControlObj;
+ TProp = TRegControlProp;
+
const
- AVGPHASES = -1;
+ NumPropsThisClass = Ord(High(TProp));
+
+ // AVGPHASES = -1;
MAXPHASE = -2;
MINPHASE = -3;
ACTION_TAPCHANGE = 0;
ACTION_REVERSE = 1;
-
- NumPropsThisClass = 32;
-
var
- LastChange: Integer;
+ PropInfo: Pointer = NIL;
+ PhaseEnum: TDSSEnum;
-{--------------------------------------------------------------------------}
-constructor TRegControl.Create(dssContext: TDSSContext); // Creates superstructure for all RegControl objects
+constructor TRegControl.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ PhaseEnum := TDSSEnum.Create('RegControl: Phase Selection', True, 2, 2,
+ ['min', 'max'], [-3, -2]);
+ PhaseEnum.Hybrid := True;
+ end;
- Class_name := 'RegControl';
- DSSClassType := DSSClassType + REG_CONTROL;
+ Transf_Or_AutoTrans_ProxyClass := TProxyClass.Create(dssContext, ['Transformer', 'AutoTrans']);
- DefineProperties;
+ inherited Create(dssContext, REG_CONTROL, 'RegControl');
+end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+destructor TRegControl.Destroy;
+begin
+ Transf_Or_AutoTrans_ProxyClass.Free;
+ inherited Destroy;
+end;
- LastChange := 0;
+procedure SetTapNum(obj: TObj; Value: Integer);
+begin
+ obj.set_TapNum(Value);
end;
-{--------------------------------------------------------------------------}
-destructor TRegControl.Destroy;
+function GetTapNum(obj: TObj): Integer;
+begin
+ Result := obj.get_TapNum();
+end;
+procedure DoReset(Obj: TObj);
begin
- inherited Destroy;
+ // force a reset
+ Obj.Reset;
+ //PropertyValue[29] := 'n'; // so it gets reported properly
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TRegControl.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
-
- PropertyName[1] := 'transformer';
- PropertyName[2] := 'winding';
- PropertyName[3] := 'vreg';
- PropertyName[4] := 'band';
- PropertyName[5] := 'ptratio';
- PropertyName[6] := 'CTprim';
- PropertyName[7] := 'R';
- PropertyName[8] := 'X';
- PropertyName[9] := 'bus';
- PropertyName[10] := 'delay';
- PropertyName[11] := 'reversible';
- PropertyName[12] := 'revvreg';
- PropertyName[13] := 'revband';
- PropertyName[14] := 'revR';
- PropertyName[15] := 'revX';
- PropertyName[16] := 'tapdelay';
- PropertyName[17] := 'debugtrace';
- PropertyName[18] := 'maxtapchange';
- PropertyName[19] := 'inversetime';
- PropertyName[20] := 'tapwinding';
- PropertyName[21] := 'vlimit';
- PropertyName[22] := 'PTphase';
- PropertyName[23] := 'revThreshold';
- PropertyName[24] := 'revDelay';
- PropertyName[25] := 'revNeutral';
- PropertyName[26] := 'EventLog';
- PropertyName[27] := 'RemotePTRatio';
- PropertyName[28] := 'TapNum';
- PropertyName[29] := 'Reset';
- PropertyName[30] := 'LDC_Z';
- PropertyName[31] := 'rev_Z';
- PropertyName[32] := 'Cogen';
-
- PropertyHelp[1] := 'Name of Transformer or AutoTrans element to which the RegControl is connected. ' +
- 'Do not specify the full object name; "Transformer" or "AutoTrans" is assumed for ' +
- 'the object class. Example:' + CRLF + CRLF +
- 'Transformer=Xfmr1';
- PropertyHelp[2] := 'Number of the winding of the transformer element that the RegControl is monitoring. ' +
- '1 or 2, typically. Side Effect: Sets TAPWINDING property to the same winding.';
- PropertyHelp[3] := 'Voltage regulator setting, in VOLTS, for the winding being controlled. Multiplying this ' +
- 'value times the ptratio should yield the voltage across the WINDING of the controlled transformer.' +
- ' Default is 120.0';
- PropertyHelp[4] := 'Bandwidth in VOLTS for the controlled bus (see help for ptratio property). Default is 3.0';
- PropertyHelp[5] := 'Ratio of the PT that converts the controlled winding voltage to the regulator control voltage. ' +
- 'Default is 60. If the winding is Wye, the line-to-neutral voltage is used. Else, the line-to-line ' +
- 'voltage is used. SIDE EFFECT: Also sets RemotePTRatio property.';
- PropertyHelp[6] := 'Rating, in Amperes, of the primary CT rating for which the line amps convert to control rated amps.' +
- 'The typical default secondary ampere rating is 0.2 Amps (check with manufacturer specs). ' +
- 'Current at which the LDC voltages match the R and X settings.';
- PropertyHelp[7] := 'R setting on the line drop compensator in the regulator, expressed in VOLTS.';
- PropertyHelp[8] := 'X setting on the line drop compensator in the regulator, expressed in VOLTS.';
- PropertyHelp[9] := 'Name of a bus (busname.nodename) in the system to use as the controlled bus instead of the bus to which the ' +
- 'transformer winding is connected or the R and X line drop compensator settings. Do not specify this ' +
- 'value if you wish to use the line drop compensator settings. Default is null string. Assumes the base voltage for this ' +
- 'bus is the same as the transformer winding base specified above. ' +
- 'Note: This bus (1-phase) WILL BE CREATED by the regulator control upon SOLVE if not defined by some other device. ' +
- 'You can specify the node of the bus you wish to sample (defaults to 1). ' +
- 'If specified, the RegControl is redefined as a 1-phase device since only one voltage is used.';
- PropertyHelp[10] := 'Time delay, in seconds, from when the voltage goes out of band to when the tap changing begins. ' +
- 'This is used to determine which regulator control will act first. Default is 15. You may specify any ' +
- 'floating point number to achieve a model of whatever condition is necessary.';
- PropertyHelp[11] := '{Yes |No*} Indicates whether or not the regulator can be switched to regulate in the reverse direction. Default is No.' +
- 'Typically applies only to line regulators and not to LTC on a substation transformer.';
- PropertyHelp[12] := 'Voltage setting in volts for operation in the reverse direction.';
- PropertyHelp[13] := 'Bandwidth for operating in the reverse direction.';
- PropertyHelp[14] := 'R line drop compensator setting for reverse direction.';
- PropertyHelp[15] := 'X line drop compensator setting for reverse direction.';
- PropertyHelp[16] := 'Delay in sec between tap changes. Default is 2. This is how long it takes between changes ' +
- 'after the first change.';
- PropertyHelp[17] := '{Yes | No* } Default is no. Turn this on to capture the progress of the regulator model ' +
- 'for each control iteration. Creates a separate file for each RegControl named "REG_name.CSV".';
- PropertyHelp[18] := 'Maximum allowable tap change per control iteration in STATIC control mode. Default is 16. ' + CRLF + CRLF +
- 'Set this to 1 to better approximate actual control action. ' + CRLF + CRLF +
- 'Set this to 0 to fix the tap in the current position.';
- PropertyHelp[19] := '{Yes | No* } Default is no. The time delay is adjusted inversely proportional to the amount the voltage is outside the band down to 10%.';
- PropertyHelp[20] := 'Winding containing the actual taps, if different than the WINDING property. Defaults to the same winding as specified by the WINDING property.';
- PropertyHelp[21] := 'Voltage Limit for bus to which regulated winding is connected (e.g. first customer). Default is 0.0. ' +
- 'Set to a value greater then zero to activate this function.';
- PropertyHelp[22] := 'For multi-phase transformers, the number of the phase being monitored or one of { MAX | MIN} for all phases. Default=1. ' +
- 'Must be less than or equal to the number of phases. Ignored for regulated bus.';
- PropertyHelp[23] := 'kW reverse power threshold for reversing the direction of the regulator. Default is 100.0 kw.';
- PropertyHelp[24] := 'Time Delay in seconds (s) for executing the reversing action once the threshold for reversing has been exceeded. Default is 60 s.';
- PropertyHelp[25] := '{Yes | No*} Default is no. Set this to Yes if you want the regulator to go to neutral in the reverse direction or in cogen operation.';
- PropertyHelp[26] := '{Yes/True* | No/False} Default is YES for regulator control. Log control actions to Eventlog.';
- PropertyHelp[27] := 'When regulating a bus (the Bus= property is set), the PT ratio required to convert actual voltage at the remote bus to control voltage. ' +
- 'Is initialized to PTratio property. Set this property after setting PTratio.';
- PropertyHelp[28] := 'An integer number indicating the tap position that the controlled transformer winding tap position is currently at, or is being set to. If being set, and the value is outside the range of the transformer min or max tap,' +
- ' then set to the min or max tap position as appropriate. Default is 0';
- PropertyHelp[29] := '{Yes | No} If Yes, forces Reset of this RegControl.';
- PropertyHelp[30] := 'Z value for Beckwith LDC_Z control option. Volts adjustment at rated control current.';
- PropertyHelp[31] := 'Reverse Z value for Beckwith LDC_Z control option.';
- PropertyHelp[32] := '{Yes|No*} Default is No. The Cogen feature is activated. Continues looking forward if power ' +
- 'reverses, but switches to reverse-mode LDC, vreg and band values.';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enum properties
+ PropertyType[ord(TProp.PTphase)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.PTphase)] := ptruint(@obj.FPTPhase);
+ PropertyOffset2[ord(TProp.PTphase)] := PtrInt(PhaseEnum);
+
+ // object reference
+ PropertyType[ord(TProp.transformer)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.transformer)] := ptruint(@obj.FControlledElement);
+ PropertyOffset2[ord(TProp.transformer)] := ptruint(Transf_Or_AutoTrans_ProxyClass);
+ PropertyWriteFunction[ord(TProp.transformer)] := @SetControlledElement;
+ PropertyFlags[ord(TProp.transformer)] := [TPropertyFlag.WriteByFunction, TPropertyFlag.CheckForVar];
+
+ // double property, as Pascal property
+ PropertyType[ord(TProp.TapNum)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.TapNum)] := 1;
+ PropertyWriteFunction[ord(TProp.TapNum)] := @SetTapNum;
+ PropertyReadFunction[ord(TProp.TapNum)] := @GetTapNum;
+ PropertyFlags[ord(TProp.TapNum)] := [TPropertyFlag.WriteByFunction, TPropertyFlag.ReadByFunction];
+
+ // boolean properties
+ PropertyType[ord(TProp.reversible)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.debugtrace)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.inversetime)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.revNeutral)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.EventLog)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.Cogen)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.reversible)] := ptruint(@obj.IsReversible);
+ PropertyOffset[ord(TProp.debugtrace)] := ptruint(@obj.DebugTrace);
+ PropertyOffset[ord(TProp.inversetime)] := ptruint(@obj.Inversetime);
+ PropertyOffset[ord(TProp.revNeutral)] := ptruint(@obj.ReverseNeutral);
+ PropertyOffset[ord(TProp.EventLog)] := ptruint(@obj.ShowEventLog);
+ PropertyOffset[ord(TProp.Cogen)] := ptruint(@obj.CogenEnabled);
+
+ // integer properties
+ PropertyType[ord(TProp.winding)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.tapwinding)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.maxtapchange)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.winding)] := ptruint(@obj.ElementTerminal);
+ PropertyOffset[ord(TProp.tapwinding)] := ptruint(@obj.TapWinding);
+ PropertyOffset[ord(TProp.maxtapchange)] := ptruint(@obj.TapLimitPerChange);
+
+ // string properties
+ PropertyType[ord(TProp.bus)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.bus)] := ptruint(@obj.RegulatedBus);
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.vreg)] := ptruint(@obj.Vreg);
+ PropertyOffset[ord(TProp.band)] := ptruint(@obj.Bandwidth);
+ PropertyOffset[ord(TProp.ptratio)] := ptruint(@obj.PTRatio);
+ PropertyOffset[ord(TProp.ctprim)] := ptruint(@obj.CTRating);
+ PropertyOffset[ord(TProp.R)] := ptruint(@obj.R);
+ PropertyOffset[ord(TProp.X)] := ptruint(@obj.X);
+ PropertyOffset[ord(TProp.delay)] := ptruint(@obj.TimeDelay);
+ PropertyOffset[ord(TProp.revvreg)] := ptruint(@obj.revVreg);
+ PropertyOffset[ord(TProp.revband)] := ptruint(@obj.revBandwidth);
+ PropertyOffset[ord(TProp.revR)] := ptruint(@obj.revR);
+ PropertyOffset[ord(TProp.revX)] := ptruint(@obj.revX);
+ PropertyOffset[ord(TProp.tapdelay)] := ptruint(@obj.TapDelay);
+ PropertyOffset[ord(TProp.RemotePTRatio)] := ptruint(@obj.RemotePTRatio);
+ PropertyOffset[ord(TProp.LDC_Z)] := ptruint(@obj.LDC_Z);
+ PropertyOffset[ord(TProp.rev_Z)] := ptruint(@obj.revLDC_Z);
+ PropertyOffset[ord(TProp.revThreshold)] := ptruint(@obj.kWRevPowerThreshold);
+ PropertyOffset[ord(TProp.revDelay)] := ptruint(@obj.RevDelay);
+ PropertyOffset[ord(TProp.Vlimit)] := ptruint(@obj.Vlimit);
+
+ // boolean action
+ PropertyType[ord(TProp.Reset)] := TPropertyType.BooleanActionProperty;
+ PropertyOffset[ord(TProp.Reset)] := ptruint(@DoReset);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ inherited DefineProperties;
end;
-{--------------------------------------------------------------------------}
-function TRegControl.NewObject(const ObjName: String): Integer;
+function TRegControl.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new RegControl and add it to RegControl class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TRegControlObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-{--------------------------------------------------------------------------}
-function TRegControl.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
- function Max(a, b: Integer): Integer;
- begin
- if a >= b then
- Result := a
- else
- Result := b;
- end;
-
+procedure TRegControlObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
-
- // continue parsing WITH contents of Parser
- DSS.ActiveRegControlObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveRegControlObj;
-
- Result := 0;
-
- with DSS.ActiveRegControlObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
+ case Idx of
+ ord(TProp.Transformer):
begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 120);
- 1:
- ElementName := 'Transformer.' + lowercase(param); // Initialize to Transformer
- 2:
- ElementTerminal := Parser.IntValue;
- 3:
- Vreg := Parser.DblValue;
- 4:
- Bandwidth := Parser.DblValue;
- 5:
- PTRatio := Parser.DblValue;
- 6:
- CTRating := Parser.DblValue;
- 7:
- R := Parser.DblValue;
- 8:
- X := Parser.DblValue;
- 9:
- RegulatedBus := Param; // Buaname.node
- 10:
- TimeDelay := Parser.DblValue;
- 11:
- IsReversible := InterpretYesNo(Param);
- 12:
- revVreg := Parser.DblValue;
- 13:
- revBandwidth := Parser.DblValue;
- 14:
- revR := Parser.DblValue;
- 15:
- revX := Parser.DblValue;
- 16:
- TapDelay := Parser.DblValue;
- 17:
- DebugTrace := InterpretYesNo(Param);
- 18:
- TapLimitPerChange := max(0, Parser.IntValue);
- 19:
- FInversetime := InterpretYesNo(Param);
- 20:
- TapWinding := Parser.intValue;
- 21:
- begin
- Vlimit := Parser.DblValue;
- if VLimit > 0.0 then
- VLimitActive := TRUE
- else
- VLimitActive := FALSE;
- end;
- 22:
- if CompareTextShortest(param, 'max') = 0 then
- FPTPhase := MAXPHASE
- else
- if CompareTextShortest(param, 'min') = 0 then
- FPTPhase := MINPHASE
- else
- FPTPhase := max(1, Parser.IntValue);
- 23:
- kWRevPowerThreshold := Parser.DblValue;
- 24:
- RevDelay := Parser.DblValue;
- 25:
- ReverseNeutral := InterpretYesNo(Param);
- 26:
- ShowEventLog := InterpretYesNo(param);
- 27:
- RemotePTRatio := Parser.DblValue;
- 28:
- TapNum := Parser.IntValue;
- 29:
- if InterpretYesNo(Param) then
- begin // force a reset
- Reset;
- PropertyValue[29] := 'n'; // so it gets reported properly
- end;
- 30:
- LDC_Z := Parser.DblValue;
- 31:
- revLDC_Z := Parser.DblValue;
- 32:
- CogenEnabled := InterpretYesNo(Param);
-
+ MonitoredElement := ControlledElement; // same for this controller
+ PrpSequence[Idx] := -10; // make sure Transformer prop is first
+ end;
+ ord(TProp.winding):
+ begin
+ Tapwinding := ElementTerminal; // Resets if property re-assigned
+ // SetAsNextSeq(ord(TProp.tapwinding)); -- not really required
+ end;
+ ord(TProp.ptratio):
+ RemotePTRatio := PTRatio; // re-initialise RemotePTRatio whenever PTRatio is set
+ ord(TProp.debugtrace):
+ if DebugTrace then
+ begin
+ FreeAndNil(TraceFile);
+ TraceFile := TBufferedFileStream.Create(DSS.OutputDirectory + 'REG_' + Name + '.csv', fmCreate);
+ FSWriteln(TraceFile, 'Hour, Sec, ControlIteration, Iterations, LoadMultiplier, Present Tap, Pending Change, Actual Change, Increment, Min Tap, Max Tap');
+ FSFlush(Tracefile);
+ end
else
- // Inherited parameters
- ClassEdit(DSS.ActiveRegControlObj, ParamPointer - NumPropsthisClass)
- end;
-
- case ParamPointer of
- 2:
- begin
- Tapwinding := ElementTerminal; // Resets if property re-assigned
- PropertyValue[20] := Param;
- end;
- 5:
- RemotePTRatio := PTRatio; // re-initialise RemotePTRatio whenever PTRatio is set
- 17:
- if DebugTrace then
- begin
- FreeAndNil(TraceFile);
- TraceFile := TFileStream.Create(DSS.OutputDirectory + 'REG_' + Name + '.CSV', fmCreate);
- FSWriteln(TraceFile, 'Hour, Sec, ControlIteration, Iterations, LoadMultiplier, Present Tap, Pending Change, Actual Change, Increment, Min Tap, Max Tap');
- FSFlush(Tracefile);
- end
- else
- begin
- FreeAndNil(TraceFile);
- end;
- 23:
- RevPowerThreshold := kWRevPowerThreshold * 1000.0;
+ begin
+ FreeAndNil(TraceFile);
end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
- RecalcElementData;
- end; {With}
-
+ ord(TProp.maxtapchange):
+ TapLimitPerChange := max(0, TapLimitPerChange);
+ ord(TProp.revThreshold):
+ RevPowerThreshold := kWRevPowerThreshold * 1000.0;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-
-{--------------------------------------------------------------------------}
-function TRegControl.MakeLike(const RegControlName: String): Integer;
+procedure TRegControlObj.MakeLike(OtherPtr: Pointer);
var
- OtherRegControl: TRegControlObj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this RegControl name in the present collection}
- OtherRegControl := Find(RegControlName);
- if OtherRegControl <> NIL then
- with DSS.ActiveRegControlObj do
- begin
-
- Nphases := OtherRegControl.Fnphases;
- NConds := OtherRegControl.Fnconds; // Force Reallocation of terminal stuff
-
- ElementName := OtherRegControl.ElementName;
- ControlledElement := OtherRegControl.ControlledElement; // Pointer to target circuit element
- ElementTerminal := OtherRegControl.ElementTerminal;
-
- Vreg := OtherRegControl.Vreg;
- Bandwidth := OtherRegControl.Bandwidth;
- PTRatio := OtherRegControl.PTRatio;
- RemotePTRatio := OtherRegControl.RemotePTRatio;
- CTRating := OtherRegControl.CTRating;
- R := OtherRegControl.R;
- X := OtherRegControl.X;
- RegulatedBus := OtherRegControl.RegulatedBus;
- TimeDelay := OtherRegControl.TimeDelay;
- IsReversible := OtherRegControl.IsReversible;
- revVreg := OtherRegControl.revVreg;
- revBandwidth := OtherRegControl.revBandwidth;
- revR := OtherRegControl.revR;
- revX := OtherRegControl.revX;
- TapDelay := OtherRegControl.TapDelay;
- TapWinding := OtherRegControl.TapWinding;
- FInversetime := OtherRegControl.FInversetime;
-
- TapLimitPerChange := OtherRegControl.TapLimitPerChange;
- kWRevPowerThreshold := OtherRegControl.kWRevPowerThreshold;
- RevPowerThreshold := OtherRegControl.RevPowerThreshold;
- RevDelay := OtherRegControl.RevDelay;
- ReverseNeutral := OtherRegControl.ReverseNeutral;
- ShowEventLog := OtherRegControl.ShowEventLog;
- // DebugTrace := OtherRegControl.DebugTrace; Always default to NO
-
- FPTphase := OtherRegControl.FPTphase;
- TapNum := OtherRegControl.TapNum;
- CogenEnabled := OtherRegControl.CogenEnabled;
- LDC_Z := OtherRegControl.LDC_Z;
- RevLDC_Z := OtherRegControl.revLDC_Z;
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherRegControl.PropertyValue[i];
- end
-
- else
- DoSimpleMsg('Error in RegControl MakeLike: "' + RegControlName + '" Not Found.', 121);
-
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNphases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
+
+ ControlledElement := Other.ControlledElement; // Pointer to target circuit element
+ ElementTerminal := Other.ElementTerminal;
+
+ Vreg := Other.Vreg;
+ Bandwidth := Other.Bandwidth;
+ PTRatio := Other.PTRatio;
+ RemotePTRatio := Other.RemotePTRatio;
+ CTRating := Other.CTRating;
+ R := Other.R;
+ X := Other.X;
+ RegulatedBus := Other.RegulatedBus;
+ TimeDelay := Other.TimeDelay;
+ IsReversible := Other.IsReversible;
+ revVreg := Other.revVreg;
+ revBandwidth := Other.revBandwidth;
+ revR := Other.revR;
+ revX := Other.revX;
+ TapDelay := Other.TapDelay;
+ TapWinding := Other.TapWinding;
+ Inversetime := Other.Inversetime;
+
+ TapLimitPerChange := Other.TapLimitPerChange;
+ kWRevPowerThreshold := Other.kWRevPowerThreshold;
+ RevPowerThreshold := Other.RevPowerThreshold;
+ RevDelay := Other.RevDelay;
+ ReverseNeutral := Other.ReverseNeutral;
+ ShowEventLog := Other.ShowEventLog;
+ // DebugTrace := Other.DebugTrace; Always default to NO
+
+ FPTphase := Other.FPTphase;
+ TapNum := Other.TapNum;
+ CogenEnabled := Other.CogenEnabled;
+ LDC_Z := Other.LDC_Z;
+ RevLDC_Z := Other.revLDC_Z;
end;
-
-{==========================================================================}
-{ TRegControlObj }
-{==========================================================================}
-
-
-{--------------------------------------------------------------------------}
constructor TRegControlObj.Create(ParClass: TDSSClass; const RegControlName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(RegControlName);
+ Name := AnsiLowerCase(RegControlName);
DSSObjType := ParClass.DSSClassType;
TraceFile := nil;
- NPhases := 3; // Directly set conds and phases
- Fnconds := 3;
- Nterms := 1; // this forces allocation of terminals and conductors
- // in base class
+ LastChange := 0;
+ FNPhases := 3; // Directly set conds and phases
+ Fnconds := 3;
+ Nterms := 1; // this forces allocation of terminals and conductors in base class
Vreg := 120.0;
Bandwidth := 3.0;
@@ -622,7 +451,6 @@ constructor TRegControlObj.Create(ParClass: TDSSClass; const RegControlName: Str
FPTphase := 1;
-
LDCActive := FALSE;
TapDelay := 2.0;
TapLimitPerChange := 16;
@@ -649,7 +477,6 @@ constructor TRegControlObj.Create(ParClass: TDSSClass; const RegControlName: Str
RevHandle := 0;
RevBackHandle := 0;
- ElementName := '';
ControlledElement := NIL;
ElementTerminal := 1;
TapWinding := ElementTerminal;
@@ -659,20 +486,17 @@ constructor TRegControlObj.Create(ParClass: TDSSClass; const RegControlName: Str
DSSObjType := ParClass.DSSClassType; //REG_CONTROL;
- InitPropertyValues(0);
- FInversetime := FALSE;
+ Inversetime := FALSE;
RegulatedBus := '';
Vlimit := 0.0;
ControlActionHandle := 0;
// RecalcElementData;
-
end;
destructor TRegControlObj.Destroy;
begin
- ElementName := '';
if Assigned(VBuffer) then
ReallocMem(VBuffer, 0);
if Assigned(CBuffer) then
@@ -683,13 +507,9 @@ destructor TRegControlObj.Destroy;
inherited Destroy;
end;
-{--------------------------------------------------------------------------}
procedure TRegControlObj.RecalcElementData;
-
var
- DevIndex: Integer;
- TransName, NewElementName: String;
-
+ ename: String;
begin
if (R <> 0.0) or (X <> 0.0) or (LDC_Z > 0.0) then
LDCActive := TRUE
@@ -700,100 +520,78 @@ procedure TRegControlObj.RecalcElementData;
else
UsingRegulatedBus := TRUE;
- Devindex := GetCktElementIndex(ElementName); // Global FUNCTION
- if DevIndex = 0 then
- begin // Try 'AutoTrans' instead of Transformer
- TransName := StripClassName(ElementName);
- NewElementName := 'autotrans.' + TransName;
- Devindex := GetCktElementIndex(NewElementName);
- if Devindex > 0 then
- ElementName := NewElementName;
+ if ControlledElement = NIL then
+ begin
+ // element not found or not set
+ DoErrorMsg(
+ Format(_('RegControl: "%s"'), [Self.Name]),
+ _('Transformer Element is not set.'),
+ _('Element must be defined previously.'), 124);
+ Exit;
end;
- if DevIndex > 0 then
- begin // RegControled element must already exist
- ControlledElement := ActiveCircuit.CktElements.Get(DevIndex);
- Monitoredelement := ControlledElement; // same for this controller
-
- if UsingRegulatedBus then
- begin
- Nphases := 1; // Only need one phase
- Nconds := 2;
- end
- else
+ if UsingRegulatedBus then
+ begin
+ FNphases := 1; // Only need one phase
+ Nconds := 2;
+ end
+ else
+ begin
+ FNphases := ControlledElement.NPhases;
+ Nconds := FNphases;
+ if FPTphase > FNphases then
begin
- Nphases := ControlledElement.NPhases;
- Nconds := FNphases;
- if FPTphase > FNphases then
- begin
- FPTphase := 1;
- PropertyValue[22] := '1';
- end;
+ FPTphase := 1;
+ SetAsNextSeq(ord(TProp.PTphase));
end;
+ end;
- if (Comparetext(ControlledElement.DSSClassName, 'transformer') = 0) or // either should work
- (Comparetext(ControlledElement.DSSClassName, 'autotrans') = 0) then
+ if (Comparetext(ControlledElement.DSSClassName, 'transformer') = 0) or // either should work
+ (Comparetext(ControlledElement.DSSClassName, 'autotrans') = 0) then
+ begin
+ if ElementTerminal > ControlledElement.Nterms then
begin
- if ElementTerminal > ControlledElement.Nterms then
- begin
- DoErrorMsg('RegControl: "' + Name + '"', 'Winding no. "' + '" does not exist.',
- 'Respecify Monitored Winding no.', 122);
- end
- else
- begin
- // Sets name of i-th terminal's connected bus in RegControl's buslist
- // This value will be used to set the NodeRef array (see Sample function)
- if UsingRegulatedBus then
- Setbus(1, RegulatedBus) // hopefully this will actually exist
- else
- Setbus(1, ControlledElement.GetBus(ElementTerminal));
- ReAllocMem(VBuffer, SizeOF(Vbuffer^[1]) * ControlledElement.NPhases); // buffer to hold regulator voltages
- ReAllocMem(CBuffer, SizeOF(CBuffer^[1]) * ControlledElement.Yorder);
- end;
+ DoErrorMsg(
+ Format(_('RegControl: "%s"'), [Name]),
+ Format(_('Winding no. "%d" does not exist.'), [ElementTerminal]),
+ _('Respecify Monitored Winding no.'), 122);
end
else
begin
- ControlledElement := NIL; // we get here if element not found
- DoErrorMsg('RegControl: "' + Self.Name + '"', 'Controlled Regulator Element "' + ElementName + '" Is not a transformer.',
- ' Element must be defined previously.', 123);
+ // Sets name of i-th terminal's connected bus in RegControl's buslist
+ // This value will be used to set the NodeRef array (see Sample function)
+ if UsingRegulatedBus then
+ Setbus(1, RegulatedBus) // hopefully this will actually exist
+ else
+ Setbus(1, ControlledElement.GetBus(ElementTerminal));
+ ReAllocMem(VBuffer, SizeOF(Vbuffer^[1]) * ControlledElement.NPhases); // buffer to hold regulator voltages
+ ReAllocMem(CBuffer, SizeOF(CBuffer^[1]) * ControlledElement.Yorder);
end;
end
else
begin
- ControlledElement := NIL; // element not found
- DoErrorMsg('RegControl: "' + Self.Name + '"', 'Transformer Element "' + ElementName + '" Not Found.',
- ' Element must be defined previously.', 124);
+ ename := ControlledElement.Name;
+ ControlledElement := NIL;
+ DoErrorMsg(
+ Format(_('RegControl: "%s"'), [Self.Name]),
+ Format(_('Controlled Regulator Element "%s" is not a transformer.'), [ename]),
+ _('Element must be defined previously.'), 123);
end;
end;
-{--------------------------------------------------------------------------}
-procedure TRegControlObj.CalcYPrim;
-begin
- // leave YPrim as nil and it will be ignored ... zero current source
- // Yprim is zeroed when created. Leave it as is.
- // IF YPrim=nil THEN YPrim := TcMatrix.CreateMatrix(Yorder);
-end;
-
-
-{--------------------------------------------------------------------------}
function TRegControlObj.GetControlVoltage(VBuffer: pComplexArray; Nphs: Integer; PTRatio: Double): Complex;
-
var
i: Integer;
V: Double;
-
begin
-
-
case FPTphase of
-{
- AVGPHASES: Begin
- Result := CZERO;
- FOR i := 1 to Nphs Do Result := Result + Cabs(VBuffer^[i]);
- Result := CdivReal(Result, (Nphs*PTRatio));
- End;
-
-} MAXPHASE:
+// AVGPHASES: Begin
+// Result := CZERO;
+// FOR i := 1 to Nphs Do Result := Result + Cabs(VBuffer^[i]);
+// Result := Result / (Nphs*PTRatio);
+// End;
+//
+ MAXPHASE:
begin
ControlledPhase := 1;
V := Cabs(VBuffer^[ControlledPhase]);
@@ -803,7 +601,7 @@ function TRegControlObj.GetControlVoltage(VBuffer: pComplexArray; Nphs: Integer;
V := Cabs(VBuffer^[i]);
ControlledPhase := i;
end;
- Result := CDivReal(VBuffer^[ControlledPhase], PTRatio);
+ Result := VBuffer^[ControlledPhase] / PTRatio;
end;
MINPHASE:
begin
@@ -815,45 +613,18 @@ function TRegControlObj.GetControlVoltage(VBuffer: pComplexArray; Nphs: Integer;
V := Cabs(VBuffer^[i]);
ControlledPhase := i;
end;
- Result := CDivReal(VBuffer^[ControlledPhase], PTRatio);
+ Result := VBuffer^[ControlledPhase] / PTRatio;
end;
else
{Just use one phase because that's what most controls do.}
- Result := CDivReal(VBuffer^[FPTPhase], PTRatio);
+ Result := VBuffer^[FPTPhase] / PTRatio;
ControlledPhase := FPTPhase;
end;
-
-
end;
-procedure TRegControlObj.GetCurrents(Curr: pComplexArray);
+procedure TRegControlObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i: Integer;
-begin
-
- for i := 1 to Fnconds do
- Curr^[i] := CZERO;
-
-end;
-
-{--------------------------------------------------------------------}
-
-function TRegControlObj.GetPropertyValue(Index: Integer): String;
-begin
- case Index of
- 28:
- Result := Format('%d', [Tapnum]);
- else
- Result := inherited GetPropertyValue(index);
- end;
-end;
-
-{--------------------------------------------------------------------------}
-procedure TRegControlObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
begin
inherited DumpProperties(F, Complete);
@@ -863,26 +634,19 @@ procedure TRegControlObj.DumpProperties(F: TFileStream; Complete: Boolean);
FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
end;
- // Note: The PropertyValue access function calls GetPropertyValue routine.
-
if Complete then
begin
FSWriteln(F, '! Bus =' + GetBus(1));
FSWriteln(F);
end;
-
end;
-{--------------------------------------------------------------------------}
function TRegControlObj.AtLeastOneTap(const ProposedChange: Double; Increment: Double): Double;
-
// Called in STATIC mode
// Changes 70% of the way but at least one tap, subject to maximum allowable tap change
var
NumTaps: Integer;
-
begin
-
NumTaps := Trunc(0.7 * Abs(ProposedChange) / Increment);
if NumTaps = 0 then
@@ -901,45 +665,35 @@ function TRegControlObj.AtLeastOneTap(const ProposedChange: Double; Increment: D
Result := -NumTaps * Increment;
LastChange := -NumTaps;
end;
-
end;
-
-{--------------------------------------------------------------------------}
-function OneInDirectionOf(var ProposedChange: Double; Increment: Double): Double;
-
+function OneInDirectionOf(Obj: TObj; var ProposedChange: Double; Increment: Double): Double;
// Computes the amount of one tap change in the direction of the pending tapchange
// Automatically decrements the proposed change by that amount
-
begin
- LastChange := 0;
+ Obj.LastChange := 0;
if ProposedChange > 0.0 then
begin
Result := Increment;
- LastChange := 1;
+ Obj.LastChange := 1;
ProposedChange := ProposedChange - Increment;
end
else
begin
Result := -Increment;
- LastChange := -1;
+ Obj.LastChange := -1;
ProposedChange := ProposedChange + Increment;
end;
if Abs(ProposedChange) < 0.9 * Increment then
ProposedChange := 0.0;
-
end;
-{--------------------------------------------------------------------------}
procedure TRegControlObj.DoPendingAction(const Code, ProxyHdl: Integer);
-
// 2-23-00 Modified to change one tap at a time
var
TapChangeToMake: Double;
-
begin
-
case Code of
ACTION_TAPCHANGE:
begin
@@ -948,14 +702,11 @@ procedure TRegControlObj.DoPendingAction(const Code, ProxyHdl: Integer);
RegWriteDebugRecord(Format('+++ %.6g s: Handling TapChange = %.8g', [Solution.DynaVars.t, PendingTapChange]));
if PendingTapChange = 0.0 then {Check to make sure control has not reset}
-
Armed := FALSE
-
else
- with TTransfObj(ControlledElement) do
+ with TTransfObj(ControlledElement) do
begin
-
- // Transformer PresentTap property automatically limits tap
+ // Transformer PresentTap property automatically limits tap
with ActiveCircuit, ActiveCircuit.Solution do
begin
case ControlMode of
@@ -973,7 +724,7 @@ procedure TRegControlObj.DoPendingAction(const Code, ProxyHdl: Integer);
EVENTDRIVEN:
begin
- TapChangeToMake := OneInDirectionOf(FPendingTapChange, TapIncrement[TapWinding]);
+ TapChangeToMake := OneInDirectionOf(Self, FPendingTapChange, TapIncrement[TapWinding]);
if (DebugTrace) then
RegWriteTraceRecord(TapChangeToMake);
PresentTap[TapWinding] := PresentTap[TapWinding] + TapChangeToMake;
@@ -985,7 +736,7 @@ procedure TRegControlObj.DoPendingAction(const Code, ProxyHdl: Integer);
TIMEDRIVEN:
begin
- TapChangeToMake := OneInDirectionOf(FPendingTapChange, TapIncrement[TapWinding]);
+ TapChangeToMake := OneInDirectionOf(Self, FPendingTapChange, TapIncrement[TapWinding]);
if (DebugTrace) then
RegWriteTraceRecord(TapChangeToMake);
PresentTap[TapWinding] := PresentTap[TapWinding] + TapChangeToMake;
@@ -1002,7 +753,7 @@ procedure TRegControlObj.DoPendingAction(const Code, ProxyHdl: Integer);
MULTIRATE:
begin
- TapChangeToMake := OneInDirectionOf(FPendingTapChange, TapIncrement[TapWinding]);
+ TapChangeToMake := OneInDirectionOf(Self, FPendingTapChange, TapIncrement[TapWinding]);
if (DebugTrace) then
RegWriteTraceRecord(TapChangeToMake);
PresentTap[TapWinding] := PresentTap[TapWinding] + TapChangeToMake;
@@ -1049,11 +800,8 @@ procedure TRegControlObj.DoPendingAction(const Code, ProxyHdl: Integer);
end;
procedure TRegControlObj.Sample;
-
{This is where it all happens ...}
-
var
-
BoostNeeded,
Increment,
Vactual,
@@ -1070,7 +818,6 @@ procedure TRegControlObj.Sample;
i, ii: Integer;
ControlledTransformer: TTransfObj;
TransformerConnection: Integer;
-
begin
ControlledTransformer := TTransfObj(ControlledElement);
@@ -1095,7 +842,6 @@ procedure TRegControlObj.Sample;
if IsReversible or CogenEnabled then
begin
-
if LookingForward and (not InCogenMode) then // If looking forward, check to see if we should reverse
begin
FwdPower := -ControlledTransformer.Power[ElementTerminal].re; // watts
@@ -1203,7 +949,7 @@ procedure TRegControlObj.Sample;
1:
begin // Delta
ii := ControlledTransformer.RotatePhases(i); // Get next phase in sequence using Transformer Obj rotate
- VBuffer^[i] := CSub(Vterminal^[i], Vterminal^[ii]);
+ VBuffer^[i] := Vterminal^[i] - Vterminal^[ii];
end
end;
end;
@@ -1221,7 +967,7 @@ procedure TRegControlObj.Sample;
if UsingRegulatedBus then
begin
ControlledTransformer.GetWindingVoltages(ElementTerminal, VBuffer);
- Vlocalbus := Cabs(CDivReal(VBuffer^[1], PTRatio));
+ Vlocalbus := Cabs(VBuffer^[1] / PTRatio);
end
else
begin
@@ -1236,14 +982,14 @@ procedure TRegControlObj.Sample;
begin
ControlledElement.GetCurrents(Cbuffer);
// Convert current to control current by CTRating
- ILDC := CDivReal(CBuffer^[ControlledElement.Nconds * (ElementTerminal - 1) + ControlledPhase], CTRating);
+ ILDC := (CBuffer^[ControlledElement.Nconds * (ElementTerminal - 1) + ControlledPhase]) / CTRating;
if LDC_Z = 0.0 then // Standard R, X LDC
begin
if InReverseMode or InCogenMode then
- VLDC := Cmul(Cmplx(revR, revX), ILDC)
+ VLDC := Cmplx(revR, revX) * ILDC
else
- VLDC := Cmul(Cmplx(R, X), ILDC);
- Vcontrol := Cadd(Vcontrol, VLDC); // Direction on ILDC is INTO terminal, so this is equivalent to Vterm - (R+jX)*ILDC
+ VLDC := Cmplx(R, X) * ILDC;
+ Vcontrol := Vcontrol + VLDC; // Direction on ILDC is INTO terminal, so this is equivalent to Vterm - (R+jX)*ILDC
end
else // Beckwith LDC_Z control mode
begin
@@ -1337,14 +1083,11 @@ procedure TRegControlObj.Sample;
ControlActionHandle := 0;
end;
end;
-
end;
-
end;
function TRegControlObj.Get_Transformer: TTransfObj;
begin
-
Result := TTransfObj(ControlledElement);
end;
@@ -1357,16 +1100,13 @@ function TRegControlObj.Get_TapNum: Integer;
var
ctrldTransformer: TTransfObj;
ictrldWinding: Integer;
-
begin
if ControlledElement <> NIL then
begin
-
ctrldTransformer := Get_Transformer;
ictrldWinding := TRWinding;
with ctrldTransformer do
Result := round((PresentTap[ictrldWinding] - (MaxTap[ictrldWinding] + MinTap[ictrldWinding]) / 2.0) / TapIncrement[ictrldWinding]);
-
end
else
Result := 0;
@@ -1405,9 +1145,7 @@ procedure TRegControlObj.RegWriteDebugRecord(S: String);
On E: Exception do
begin
end;
-
end;
-
end;
procedure TRegControlObj.RegWriteTraceRecord(TapChangeMade: Double);
@@ -1415,7 +1153,6 @@ procedure TRegControlObj.RegWriteTraceRecord(TapChangeMade: Double);
Separator: String;
sout: String;
begin
-
try
if (not DSS.InShowResults) then
begin
@@ -1443,7 +1180,6 @@ procedure TRegControlObj.RegWriteTraceRecord(TapChangeMade: Double);
On E: Exception do
begin {Do Nothing}
end;
-
end;
end;
@@ -1453,72 +1189,6 @@ procedure TRegControlObj.Reset;
ARMED := FALSE;
end;
-procedure TRegcontrolObj.SaveWrite(F: TFileStream);
-{Override standard SaveWrite}
-{Regcontrol structure not conducive to standard means of saving}
-var
- iprop: Integer;
-begin
- {Write only properties that were explicitly set in the
- final order they were actually set}
-
- // Write Transformer name out first so that it is set for later operations
- iProp := 1;
- if Length(PropertyValue[iProp]) > 0 then
- with ParentClass do
- FSWrite(F, Format(' %s=%s', [PropertyName^[RevPropertyIdxMap[iProp]], CheckForBlanks(PropertyValue[iProp])]));
-
- iProp := GetNextPropertySet(0); // Works on ActiveDSSObject
- while iProp > 0 do
- with ParentClass do
- begin
- if iProp <> 1 then // Don't repeat Transformer property
- if Length(PropertyValue[iProp]) > 0 then
- FSWrite(F, Format(' %s=%s', [PropertyName^[RevPropertyIdxMap[iProp]], CheckForBlanks(PropertyValue[iProp])]));
- iProp := GetNextPropertySet(iProp);
- end;
-end;
-
-procedure TRegcontrolObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := ''; //'element';
- PropertyValue[2] := '1'; //'terminal';
- PropertyValue[3] := '120';
- PropertyValue[4] := '3';
- PropertyValue[5] := '60';
- PropertyValue[6] := '300';
- PropertyValue[7] := '0';
- PropertyValue[8] := '0';
- PropertyValue[9] := '';
- PropertyValue[10] := '15';
- PropertyValue[11] := 'no';
- PropertyValue[12] := '120';
- PropertyValue[13] := '3';
- PropertyValue[14] := '0';
- PropertyValue[15] := '0';
- PropertyValue[16] := '2';
- PropertyValue[17] := 'no';
- PropertyValue[18] := '16';
- PropertyValue[19] := 'no';
- PropertyValue[20] := '1';
- PropertyValue[21] := '0.0';
- PropertyValue[22] := '1';
- PropertyValue[23] := '100';
- PropertyValue[24] := '60';
- PropertyValue[25] := 'No';
- PropertyValue[26] := 'YES';
- PropertyValue[27] := '60';
- PropertyValue[28] := '0';
- PropertyValue[29] := 'NO';
- PropertyValue[30] := '0';
- PropertyValue[31] := '0';
- PropertyValue[32] := 'No';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
procedure TRegControlObj.set_PendingTapChange(const Value: Double);
begin
FPendingTapChange := Value;
@@ -1541,22 +1211,21 @@ procedure TRegControlObj.Set_TapNum(const Value: Integer);
with ctrldTransformer do
PresentTap[ictrldWinding] := Value * TapIncrement[ictrldWinding] + ((MaxTap[ictrldWinding] + MinTap[ictrldWinding]) / 2.0);
-// Tap range checking is done in PresentTap
-// You can attempt to set the tap at an illegal value but it won't do anything
-
+ // Tap range checking is done in PresentTap
+ // You can attempt to set the tap at an illegal value but it won't do anything
end;
end;
-procedure TRegControlObj.MakePosSequence;
+procedure TRegControlObj.MakePosSequence();
begin
if ControlledElement <> NIL then
begin
Enabled := ControlledElement.Enabled;
if UsingRegulatedBus then
- Nphases := 1
+ FNphases := 1
else
- Nphases := ControlledElement.NPhases;
+ FNphases := ControlledElement.NPhases;
Nconds := FNphases;
if (Comparetext(ControlledElement.DSSClassName, 'transformer') = 0) or // either should work
(Comparetext(ControlledElement.DSSClassName, 'autotrans') = 0) then
@@ -1576,8 +1245,7 @@ procedure TRegControlObj.MakePosSequence;
function TRegControlObj.ComputeTimeDelay(Vavg: Double): Double;
begin
-
- if Finversetime then
+ if inversetime then
Result := TimeDelay / Min(10.0, (2.0 * Abs(Vreg - Vavg) / Bandwidth))
else
Result := TimeDelay;
@@ -1590,7 +1258,11 @@ procedure TRegControlObj.Set_Enabled(Value: Boolean);
FEnabled := Value;
end;
-initialization
-
+function TRegControlObj.VLimitActive: Boolean;
+begin
+ Result := VLimit > 0.0;
+end;
+finalization
+ PhaseEnum.Free;
end.
diff --git a/src/Controls/Relay.pas b/src/Controls/Relay.pas
index 4410ab20f..d5e0989a5 100644
--- a/src/Controls/Relay.pas
+++ b/src/Controls/Relay.pas
@@ -7,45 +7,31 @@
----------------------------------------------------------
}
-{
- Created 8-24-00 from CktElement Control
- 9-20-00 Implemented Voltage relay and updated arming logic for all relays
- 10-31-00 Added Event Logging
- 11-1-00 Added shots=
- 3-7-03 Added new property definition process
- Added Neg seq relays and Generic relay
- Added capability to monitor PC Element variable
- 2-16-04 Fixed address bug in symmetrical component transformation in 46 relay
- 5-1-06 Added Time Dial to Phase and ground
-}
-{
- A Relay is a control element that is connected to a terminal of a
- circuit element and controls the switches in the same or another terminal.
-
- The control is usually placed in the
- terminal of a line or transformer, but it could be any element
-
- A Relay is defined by a New command:
-
- New Relay.Name=myname Element=devclass.name terminal=[ 1|2|...] Switch = devclass.name terminal=[ 1|2|...]
- Type = [current | voltage]
- Phase = TCCCurve
- Ground = TCCCurve
- OverVolt = TCCcurve
- UnderVolt = TCCCurve
- PhaseTrip = Multipliers times curve
- GroundTrip =
- PhaseInst =
- GroundInst =
- RecloseIntervals= (array of times, sec);
- ResetTime =
-
- CktElement to be controlled must already exist.
-
- Voltage relay is a definite time relay that operates after the voltage stays out of bounds
- for a fixed time interval. It will then reclose a set time after the voltage comes back in the normal range.
-
-}
+// A Relay is a control element that is connected to a terminal of a
+// circuit element and controls the switches in the same or another terminal.
+//
+// The control is usually placed in the
+// terminal of a line or transformer, but it could be any element
+//
+// A Relay is defined by a New command:
+//
+// New Relay.Name=myname Element=devclass.name terminal=[ 1|2|...] Switch = devclass.name terminal=[ 1|2|...]
+// Type = [current | voltage]
+// Phase = TCCCurve
+// Ground = TCCCurve
+// OverVolt = TCCcurve
+// UnderVolt = TCCCurve
+// PhaseTrip = Multipliers times curve
+// GroundTrip =
+// PhaseInst =
+// GroundInst =
+// RecloseIntervals= (array of times, sec);
+// ResetTime =
+//
+// CktElement to be controlled must already exist.
+//
+// Voltage relay is a definite time relay that operates after the voltage stays out of bounds
+// for a fixed time interval. It will then reclose a set time after the voltage comes back in the normal range.
interface
@@ -57,40 +43,85 @@ interface
CktElement,
DSSClass,
Arraydef,
- ucomplex,
+ UComplex, DSSUcomplex,
utilities,
TCC_Curve,
Math;
type
+{$SCOPEDENUMS ON}
+ TRelayProp = (
+ INVALID = 0,
+ MonitoredObj = 1,
+ MonitoredTerm = 2,
+ SwitchedObj = 3,
+ SwitchedTerm = 4,
+ typ = 5,
+ Phasecurve = 6,
+ Groundcurve = 7,
+ PhaseTrip = 8,
+ GroundTrip = 9,
+ TDPhase = 10, // 28
+ TDGround = 11, // 29
+ PhaseInst = 12, // 10
+ GroundInst = 13, // 11
+ Reset = 14, // 12
+ Shots = 15, // 13
+ RecloseIntervals = 16, // 14
+ Delay = 17, // 24
+ Overvoltcurve = 18, // 15
+ Undervoltcurve = 19, // 16
+ kvbase = 20, // 17
+ __47pctPickup = 21, // 25
+ __46BaseAmps = 22, // 23
+ __46pctPickup = 23, // 21
+ __46isqt = 24, // 22
+ Variable = 25, // 20
+ overtrip = 26,
+ undertrip = 27,
+ Breakertime = 28, // 18
+ action = 29, // 19
+ Z1mag = 30,
+ Z1ang = 31,
+ Z0mag = 32,
+ Z0ang = 33,
+ Mphase = 34,
+ Mground = 35,
+ EventLog = 36,
+ DebugTrace = 37,
+ DistReverse = 38,
+ Normal = 39,
+ State = 40,
+ DOC_TiltAngleLow = 41,
+ DOC_TiltAngleHigh = 42,
+ DOC_TripSettingLow = 43,
+ DOC_TripSettingHigh = 44,
+ DOC_TripSettingMag = 45,
+ DOC_DelayInner = 46,
+ DOC_PhaseCurveInner = 47,
+ DOC_PhaseTripInner = 48,
+ DOC_TDPhaseInner = 49
+ );
+{$SCOPEDENUMS OFF}
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TRelay = class(TControlClass)
- PRIVATE
- TCC_CurveClass: TDSSClass;
PROTECTED
- procedure DefineProperties;
- function MakeLike(const RelayName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
- function GetTccCurve(const CurveName: String): TTCC_CurveObj;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TRelayObj = class(TControlElem)
PRIVATE
ControlType: Integer;
-
- {OverCurrent Relay}
+ // OverCurrent Relay
PhaseCurve,
GroundCurve: TTCC_CurveObj;
-
PhaseTrip,
GroundTrip,
PhaseInst,
@@ -106,25 +137,24 @@ TRelayObj = class(TControlElem)
RelayTarget: String;
-
- {over/Under Voltage Relay}
+ // over/Under Voltage Relay
OVcurve, // Curves assumed in per unit of base voltage
UVCurve: TTCC_CurveObj;
Vbase, // line-neut volts base
kVBase: Double;
- {46 Relay Neg Seq Current}
+ // 46 Relay Neg Seq Current
PickupAmps46,
PctPickup46,
BaseAmps46,
Isqt46: Double;
- {47 Relay}
+ // 47 Relay
PickupVolts47,
PctPickup47: Double;
- {Distance Relay}
+ // Distance Relay
Z1Mag,
Z1Ang,
Z0Mag,
@@ -134,9 +164,9 @@ TRelayObj = class(TControlElem)
Dist_Z1,
Dist_Z0,
Dist_K0: Complex;
- Dist_Reverse: Boolean;
+ Dist_Reverse: LongBool;
- {TD21 Relay}
+ // TD21 Relay
td21_i, // present ring buffer index into td21_h
td21_next, // index to one cycle back, and next write location
td21_pt: Integer; // number of time samples in td21_h
@@ -147,22 +177,29 @@ TRelayObj = class(TControlElem)
td21_dV: pComplexArray; // incremental voltages
td21_dI: pComplexArray; // incremental currents
- {Generic Relay}
+ // Directional Overcurrent Relay
+ DOC_TiltAngleLow, // Tilt angle for lower current magnitude
+ DOC_TiltAngleHigh, // Tilt angle for higher current magnitude
+ DOC_TripSetLow, // Trip setting for lower current magnitude
+ DOC_TripSetHigh, // Trip setting for higher current magnitude
+ DOC_TripSetMag, // Current magnitude trip setting (define a circle for the relay characteristics)
+ DOC_DelayInner, // Delay for trip in inner zone of the DOC characteristic
+ DOC_PhaseTripInner, // Multiplier for TCC Curve for tripping in inner zone of the DOC characteristic
+ DOC_TDPhaseInner: Double; // Time Dial for DOC_PhaseTripInner
+ DOC_PhaseCurveInner: TTCC_CurveObj; // TCC Curve for tripping in inner zone of the DOC characteristic
+
+ // Generic Relay
OverTrip,
UnderTrip: Double;
- FPresentState,
- FNormalState: EControlAction;
-
OperationCount: Integer;
LockedOut,
ArmedForClose,
ArmedForOpen,
ArmedForReset,
- PhaseTarget,
- GroundTarget,
- NormalStateSet: Boolean;
+ PhaseTarget,
+ GroundTarget: Boolean;
NextTriptime: Double;
LastEventHandle: Integer;
@@ -172,14 +209,11 @@ TRelayObj = class(TControlElem)
cBuffer: pComplexArray; // Complexarray buffer for an operating quantity
cvBuffer: pComplexArray; // for distance and td21 voltages, using cBuffer for the currents
- DebugTrace: Boolean;
+ DebugTrace: LongBool;
+ PreviousControlledElement: TDSSCktElement;
- procedure InterpretRelayState(const Action: String; const PropertyName: String);
function get_PresentState: EControlAction;
procedure set_PresentState(const Value: EControlAction);
- procedure set_NormalState(const Value: EControlAction);
-
- procedure InterpretRelayType(const S: String);
procedure OvercurrentLogic;
procedure VoltageLogic;
@@ -189,38 +223,31 @@ TRelayObj = class(TControlElem)
procedure GenericLogic;
procedure DistanceLogic;
procedure TD21Logic;
-
+ procedure DirectionalOvercurrentLogic;
PUBLIC
-
- MonitoredElementName: String;
MonitoredElementTerminal: Integer;
+ FPresentState,
+ NormalState: EControlAction;
+ NormalStateSet: Boolean;
constructor Create(ParClass: TDSSClass; const RelayName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
procedure RecalcElementData; OVERRIDE;
- procedure CalcYPrim; OVERRIDE; // Always Zero for a Relay
procedure Sample; OVERRIDE; // Sample control quantities and set action times in Control Queue
procedure DoPendingAction(const Code, ProxyHdl: Integer); OVERRIDE; // Do the action that is pending from last sample
procedure Reset; OVERRIDE; // Reset to initial defined state
-
- procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present value of terminal Curr
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
- property PresentState:EControlAction Read get_PresentState write set_PresentState;
- property NormalState: EControlAction read FNormalState write set_NormalState;
+ property PresentState: EControlAction Read get_PresentState write set_PresentState;
end;
-{--------------------------------------------------------------------------}
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Circuit,
@@ -232,9 +259,11 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TRelayObj;
+ TProp = TRelayProp;
const
-
- NumPropsThisClass = 40;
+ NumPropsThisClass = Ord(High(TProp));
CURRENT = 0; {Default}
VOLTAGE = 1;
@@ -244,435 +273,353 @@ implementation
GENERIC = 6; {Use this for frequency, etc. Generic over/under relay}
DISTANCE = 7;
TD21 = 8;
+ DOC = 9;
-{--------------------------------------------------------------------------}
-constructor TRelay.Create(dssContext: TDSSContext); // Creates superstructure for all Relay objects
-begin
- inherited Create(dssContext);
-
- Class_name := 'Relay';
- DSSClassType := DSSClassType + RELAY_CONTROL;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ MIN_DISTANCE_REACTANCE = -1.0e-8; // allow near-bolted faults to be detected
+var
+ PropInfo: Pointer = NIL;
+ ActionEnum, StateEnum: TDSSEnum;
+ RelayTypeEnum : TDSSEnum;
- TCC_CurveClass := GetDSSClassPtr(DSS, 'TCC_Curve');
+constructor TRelay.Create(dssContext: TDSSContext);
+begin
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ RelayTypeEnum := TDSSEnum.Create('Relay: Type', False, 1, 2,
+ ['Current', 'Voltage', 'ReversePower', '46', '47', 'Generic', 'Distance', 'TD21', 'DOC'],
+ [0, 1, 3, 4, 5, 6, 7, 8, 9]
+ );
+ RelayTypeEnum.DefaultValue := 0;
+ ActionEnum := TDSSEnum.Create('Relay: Action', False, 1, 1,
+ ['close', 'open', 'trip'],
+ [ord(CTRL_CLOSE), ord(CTRL_OPEN), ord(CTRL_OPEN)]);
+ StateEnum := TDSSEnum.Create('Relay: State', False, 1, 1,
+ ['closed', 'open', 'trip'],
+ [ord(CTRL_CLOSE), ord(CTRL_OPEN), ord(CTRL_OPEN)]);
+ end;
+ inherited Create(dssContext, RELAY_CONTROL, 'Relay');
end;
-{--------------------------------------------------------------------------}
destructor TRelay.Destroy;
-
begin
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TRelay.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
+ TCC_CurveClass: TDSSClass;
begin
+ TCC_CurveClass := GetDSSClassPtr(DSS, 'TCC_Curve');
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
-
- AllocatePropertyArrays; {see DSSClass}
-
- // Define Property names
- // Addproperty (property name, internal property index (see Edit), Help string);
-
- AddProperty('MonitoredObj', 1,
- 'Full object name of the circuit element, typically a line, transformer, load, or generator, ' +
- 'to which the relay''s PT and/or CT are connected.' +
- ' This is the "monitored" element. ' +
- 'There is no default; must be specified.');
- AddProperty('MonitoredTerm', 2,
- 'Number of the terminal of the circuit element to which the Relay is connected. ' +
- '1 or 2, typically. Default is 1.');
- AddProperty('SwitchedObj', 3,
- 'Name of circuit element switch that the Relay controls. ' +
- 'Specify the full object name.' +
- 'Defaults to the same as the Monitored element. ' +
- 'This is the "controlled" element.');
- AddProperty('SwitchedTerm', 4,
- 'Number of the terminal of the controlled element in which the switch is controlled by the Relay. ' +
- '1 or 2, typically. Default is 1.');
- AddProperty('type', 5, 'One of a legal relay type:' + CRLF +
- ' Current' + CRLF +
- ' Voltage' + CRLF +
- ' Reversepower' + CRLF +
- ' 46 (neg seq current)' + CRLF +
- ' 47 (neg seq voltage)' + CRLF +
- ' Generic (generic over/under relay)' + CRLF +
- ' Distance' + CRLF +
- ' TD21' + CRLF + CRLF +
- 'Default is overcurrent relay (Current) ' +
- 'Specify the curve and pickup settings appropriate for each type. '+
- 'Generic relays monitor PC Element Control variables and trip on out of over/under range in definite time.');
- AddProperty('Phasecurve', 6, 'Name of the TCC Curve object that determines the phase trip. ' +
- 'Must have been previously defined as a TCC_Curve object.' +
- ' Default is none (ignored). ' +
- 'For overcurrent relay, multiplying the current values in the curve by the "phasetrip" value gives the actual current.');
- AddProperty('Groundcurve', 7, 'Name of the TCC Curve object that determines the ground trip. Must have been previously defined as a TCC_Curve object.' +
- ' Default is none (ignored).' +
- 'For overcurrent relay, multiplying the current values in the curve by the "groundtrip" valuw gives the actual current.');
- AddProperty('PhaseTrip', 8, 'Multiplier or actual phase amps for the phase TCC curve. Defaults to 1.0.');
- AddProperty('GroundTrip', 9, 'Multiplier or actual ground amps (3I0) for the ground TCC curve. Defaults to 1.0.');
- AddProperty('TDPhase', 28, 'Time dial for Phase trip curve. Multiplier on time axis of specified curve. Default=1.0.');
- AddProperty('TDGround', 29, 'Time dial for Ground trip curve. Multiplier on time axis of specified curve. Default=1.0.');
- AddProperty('PhaseInst', 10, 'Actual amps (Current relay) or kW (reverse power relay) for instantaneous phase trip which is assumed to happen in 0.01 sec + Delay Time. Default is 0.0, which signifies no inst trip. ' +
- 'Use this value for specifying the Reverse Power threshold (kW) for reverse power relays.');
- AddProperty('GroundInst', 11, 'Actual amps for instantaneous ground trip which is assumed to happen in 0.01 sec + Delay Time.Default is 0.0, which signifies no inst trip.');
- AddProperty( 'Reset',12, 'Reset time in sec for relay. Default is 15. If this much time passes between the last pickup event, and the relay has not locked out, the operation counter resets.');
- AddProperty('Shots', 13, 'Number of shots to lockout. Default is 4. This is one more than the number of reclose intervals.');
- AddProperty('RecloseIntervals', 14, 'Array of reclose intervals. If none, specify "NONE". Default for overcurrent relay is (0.5, 2.0, 2.0) seconds. ' +
- 'Default for a voltage relay is (5.0). In a voltage relay, this is seconds after restoration of ' +
- 'voltage that the reclose occurs. ' +
- 'Reverse power relay is one shot to lockout, ' +
- 'so this is ignored. A locked out relay must be closed manually (set action=close).');
- AddProperty('Delay', 24, 'Trip time delay (sec) for DEFINITE TIME relays. Default is 0.0 for current and voltage relays. If >0 then this value is used instead of curves. ' +
- ' Used by Generic, RevPower, 46 and 47 relays. Defaults to 0.1 s for these relays.');
- AddProperty('Overvoltcurve', 15, 'TCC Curve object to use for overvoltage relay. Curve is assumed to be defined with per unit voltage values. ' +
- 'Voltage base should be defined for the relay. Default is none (ignored).');
- AddProperty('Undervoltcurve', 16, 'TCC Curve object to use for undervoltage relay. Curve is assumed to be defined with per unit voltage values. ' +
- 'Voltage base should be defined for the relay. Default is none (ignored).');
- AddProperty('kvbase', 17, 'Voltage base (kV) for the relay. Specify line-line for 3 phase devices); line-neutral for 1-phase devices. Relay assumes ' +
- 'the number of phases of the monitored element. Default is 0.0, which results in assuming the voltage ' +
- 'values in the "TCC" curve are specified in actual line-to-neutral volts.');
- AddProperty('47%Pickup', 25, 'Percent voltage pickup for 47 relay (Neg seq voltage). Default is 2. Specify also base voltage (kvbase) and delay time value. ');
- AddProperty('46BaseAmps', 23, 'Base current, Amps, for 46 relay (neg seq current).' +
- ' Used for establishing pickup and per unit I-squared-t.');
- AddProperty('46%Pickup', 21, 'Percent pickup current for 46 relay (neg seq current). Default is 20.0. ' +
- ' When current exceeds this value * BaseAmps, I-squared-t calc starts.');
- AddProperty('46isqt', 22, 'Negative Sequence I-squared-t trip value for 46 relay (neg seq current).' +
- ' Default is 1 (trips in 1 sec for 1 per unit neg seq current). Should be 1 to 99.');
- AddProperty('Variable', 20, 'Name of variable in PC Elements being monitored. Only applies to Generic relay.');
- AddProperty('overtrip', 26, 'Trip setting (high value) for Generic relay variable. Relay trips in definite time if value of variable exceeds this value.');
- AddProperty('undertrip', 27, 'Trip setting (low value) for Generic relay variable. Relay trips in definite time if value of variable is less than this value.');
- AddProperty('Breakertime', 18, 'Fixed delay time (sec) added to relay time. Default is 0.0. Designed to represent breaker time or some other delay after a trip decision is made.' +
- 'Use Delay property for setting a fixed trip time delay.' +
- 'Added to trip time of current and voltage relays. Could use in combination with inst trip value to obtain a definite time overcurrent relay.');
- AddProperty('action', 19, 'DEPRECATED. See "State" property');
- AddProperty('Z1mag', 30, 'Positive sequence reach impedance in primary ohms for Distance and TD21 functions. Default=0.7');
- AddProperty('Z1ang', 31, 'Positive sequence reach impedance angle in degrees for Distance and TD21 functions. Default=64.0');
- AddProperty('Z0mag', 32, 'Zero sequence reach impedance in primary ohms for Distance and TD21 functions. Default=2.1');
- AddProperty('Z0ang', 33, 'Zero sequence reach impedance angle in degrees for Distance and TD21 functions. Default=68.0');
- AddProperty('Mphase', 34, 'Phase reach multiplier in per-unit for Distance and TD21 functions. Default=0.7');
- AddProperty('Mground', 35, 'Ground reach multiplier in per-unit for Distance and TD21 functions. Default=0.7');
- AddProperty('EventLog', 36, '{Yes/True* | No/False} Default is Yes for Relay. Write trips, reclose and reset events to EventLog.');
- AddProperty('DebugTrace', 37, '{Yes/True* | No/False} Default is No for Relay. Write extra details to Eventlog.');
- AddProperty('DistReverse', 38, '{Yes/True* | No/False} Default is No; reverse direction for distance and td21 types.');
- AddProperty('Normal', 39, '{Open | Closed} Normal state of the relay. The relay reverts to this state for reset, change of mode, etc. ' +
- 'Defaults to "State" if not specifically declared.');
- AddProperty('State', 40, '{Open | Closed} Actual state of the relay. Upon setting, immediately forces state of the relay, overriding the Relay control. ' +
- 'Simulates manual control on relay. Defaults to Closed. "Open" causes the controlled element to open and lock out. "Closed" causes the ' +
- 'controlled element to close and the relay to reset to its first operation.');
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enums
+ PropertyType[ord(TProp.typ)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.typ)] := ptruint(@obj.ControlType);
+ PropertyOffset2[ord(TProp.typ)] := PtrInt(RelayTypeEnum);
+
+ PropertyType[ord(TProp.Action)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.Action)] := ptruint(@obj.FPresentState);
+ PropertyOffset2[ord(TProp.Action)] := PtrInt(ActionEnum);
+ PropertyFlags[ord(TProp.Action)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.Action)] := ord(TProp.State);
+
+ PropertyType[ord(TProp.Normal)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.Normal)] := ptruint(@obj.NormalState);
+ PropertyOffset2[ord(TProp.Normal)] := PtrInt(StateEnum);
+
+ PropertyType[ord(TProp.State)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.State)] := ptruint(@obj.FPresentState);
+ PropertyOffset2[ord(TProp.State)] := PtrInt(StateEnum);
+
+
+ // double arrays/vectors
+ PropertyType[ord(TProp.RecloseIntervals)] := TPropertyType.DoubleVArrayProperty;
+ PropertyOffset[ord(TProp.RecloseIntervals)] := ptruint(@obj.RecloseIntervals);
+ PropertyOffset2[ord(TProp.RecloseIntervals)] := ptruint(@obj.NumReclose);
+ PropertyOffset3[ord(TProp.RecloseIntervals)] := 4;
+ PropertyFlags[ord(TProp.RecloseIntervals)] := [TPropertyFlag.AllowNone, TPropertyFlag.ArrayMaxSize];
+
+ // object properties
+ PropertyType[ord(TProp.PhaseCurve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.PhaseCurve)] := ptruint(@obj.PhaseCurve);
+ PropertyOffset2[ord(TProp.PhaseCurve)] := ptruint(TCC_CurveClass);
+
+ PropertyType[ord(TProp.Groundcurve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.Groundcurve)] := ptruint(@obj.Groundcurve);
+ PropertyOffset2[ord(TProp.Groundcurve)] := ptruint(TCC_CurveClass);
+
+ PropertyType[ord(TProp.Overvoltcurve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.Overvoltcurve)] := ptruint(@obj.OVCurve);
+ PropertyOffset2[ord(TProp.Overvoltcurve)] := ptruint(TCC_CurveClass);
+
+ PropertyType[ord(TProp.Undervoltcurve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.Undervoltcurve)] := ptruint(@obj.UVCurve);
+ PropertyOffset2[ord(TProp.Undervoltcurve)] := ptruint(TCC_CurveClass);
+
+ PropertyType[ord(TProp.DOC_TDPhaseInner)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.DOC_TDPhaseInner)] := ptruint(@obj.DOC_PhaseCurveInner);
+ PropertyOffset2[ord(TProp.DOC_TDPhaseInner)] := ptruint(TCC_CurveClass);
+
+
+ PropertyType[ord(TProp.MonitoredObj)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.MonitoredObj)] := ptruint(@obj.FMonitoredElement);
+ PropertyOffset2[ord(TProp.MonitoredObj)] := 0;
+ PropertyWriteFunction[ord(TProp.MonitoredObj)] := @SetMonitoredElement;
+ PropertyFlags[ord(TProp.MonitoredObj)] := [TPropertyFlag.WriteByFunction];//[TPropertyFlag.CheckForVar]; // not required for general cktelements
+
+ PropertyType[ord(TProp.SwitchedObj)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.SwitchedObj)] := ptruint(@obj.FControlledElement);
+ PropertyOffset2[ord(TProp.SwitchedObj)] := 0;
+ PropertyWriteFunction[ord(TProp.SwitchedObj)] := @SetControlledElement;
+ PropertyFlags[ord(TProp.SwitchedObj)] := [TPropertyFlag.WriteByFunction];
+
+ // boolean properties
+ PropertyType[ord(TProp.EventLog)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.DebugTrace)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.DistReverse)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.EventLog)] := ptruint(@obj.ShowEventLog);
+ PropertyOffset[ord(TProp.DebugTrace)] := ptruint(@obj.DebugTrace);
+ PropertyOffset[ord(TProp.DistReverse)] := ptruint(@obj.Dist_Reverse);
+
+ // string properties (with lower case transformation)
+ PropertyType[ord(TProp.Variable)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.Variable)] := ptruint(@obj.MonitorVariable);
+
+ // integer properties
+ PropertyType[ord(TProp.SwitchedTerm)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.MonitoredTerm)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.SwitchedTerm)] := ptruint(@obj.ElementTerminal);
+ PropertyOffset[ord(TProp.MonitoredTerm)] := ptruint(@obj.MonitoredElementTerminal);
+
+ PropertyType[ord(TProp.Shots)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Shots)] := ptruint(@obj.NumReclose);
+ PropertyFlags[ord(TProp.Shots)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero, TPropertyFlag.ValueOffset];
+ PropertyValueOffset[ord(TProp.Shots)] := -1;
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.PhaseTrip)] := ptruint(@obj.PhaseTrip);
+ PropertyOffset[ord(TProp.GroundTrip)] := ptruint(@obj.GroundTrip);
+ PropertyOffset[ord(TProp.PhaseInst)] := ptruint(@obj.PhaseInst);
+ PropertyOffset[ord(TProp.GroundInst)] := ptruint(@obj.GroundInst);
+ PropertyOffset[ord(TProp.Reset)] := ptruint(@obj.ResetTime);
+ PropertyOffset[ord(TProp.kvbase)] := ptruint(@obj.kVBase);
+ PropertyOffset[ord(TProp.Breakertime)] := ptruint(@obj.Breaker_time);
+ PropertyOffset[ord(TProp.__46pctPickup)] := ptruint(@obj.PctPickup46);
+ PropertyOffset[ord(TProp.__46isqt)] := ptruint(@obj.Isqt46);
+ PropertyOffset[ord(TProp.__46BaseAmps)] := ptruint(@obj.BaseAmps46);
+ PropertyOffset[ord(TProp.Delay)] := ptruint(@obj.Delay_Time);
+ PropertyOffset[ord(TProp.__47pctPickup)] := ptruint(@obj.PctPickup47);
+ PropertyOffset[ord(TProp.overtrip)] := ptruint(@obj.Overtrip);
+ PropertyOffset[ord(TProp.undertrip)] := ptruint(@obj.Undertrip);
+ PropertyOffset[ord(TProp.TDPhase)] := ptruint(@obj.TDPhase);
+ PropertyOffset[ord(TProp.TDGround)] := ptruint(@obj.TDGround);
+ PropertyOffset[ord(TProp.Z1mag)] := ptruint(@obj.Z1mag);
+ PropertyOffset[ord(TProp.Z1ang)] := ptruint(@obj.Z1ang);
+ PropertyOffset[ord(TProp.Z0mag)] := ptruint(@obj.Z0mag);
+ PropertyOffset[ord(TProp.Z0ang)] := ptruint(@obj.Z0ang);
+ PropertyOffset[ord(TProp.Mphase)] := ptruint(@obj.Mphase);
+ PropertyOffset[ord(TProp.Mground)] := ptruint(@obj.Mground);
+ PropertyOffset[ord(TProp.DOC_TiltAngleLow)] := ptruint(@obj.DOC_TiltAngleLow);
+ PropertyOffset[ord(TProp.DOC_TiltAngleHigh)] := ptruint(@obj.DOC_TiltAngleHigh);
+ PropertyOffset[ord(TProp.DOC_TripSettingLow)] := ptruint(@obj.DOC_TripSetLow);
+ PropertyOffset[ord(TProp.DOC_TripSettingHigh)] := ptruint(@obj.DOC_TripSetHigh);
+ PropertyOffset[ord(TProp.DOC_TripSettingMag)] := ptruint(@obj.DOC_TripSetMag);
+ PropertyOffset[ord(TProp.DOC_DelayInner)] := ptruint(@obj.DOC_DelayInner);
+ PropertyOffset[ord(TProp.DOC_PhaseCurveInner)] := ptruint(@obj.DOC_PhaseTripInner);
+ PropertyOffset[ord(TProp.DOC_PhaseTripInner)] := ptruint(@obj.DOC_TDPhaseInner);
ActiveProperty := NumPropsThisClass;
-
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
+ inherited DefineProperties;
end;
-{--------------------------------------------------------------------------}
-function TRelay.NewObject(const ObjName: String): Integer;
-begin
- // Make a new Relay and add it to Relay class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TRelayObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
-end;
-
-{--------------------------------------------------------------------------}
-
-function TRelay.GetTccCurve(const CurveName: String): TTCC_CurveObj;
-
+function TRelay.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
-
- Result := TCC_CurveClass.Find(CurveName);
-
- if Result = NIL then
- DoSimpleMsg('TCC Curve object: "' + CurveName + '" not found.', 380);
-
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-{--------------------------------------------------------------------------}
-function TRelay.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
+procedure TRelayObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
-
- // continue parsing WITH contents of Parser
- DSS.ActiveRelayObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveRelayObj;
-
- Result := 0;
-
- with DSS.ActiveRelayObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[PropertyIdxMap[ParamPointer]] := Param
+ case Idx of
+ // Default the controlled element to the monitored element
+ ord(TProp.MonitoredObj):
+ ControlledElement := MonitoredElement;
+ ord(TProp.MonitoredTerm):
+ ElementTerminal := MonitoredElementTerminal;
+ // ord(TProp.RecloseIntervals): -- changed in r3326, zero allowed
+ // if NumReclose = 0 then
+ // NumReclose := 1;
+ ord(TProp.Variable):
+ MonitorVariable := AnsiLowerCase(MonitorVariable);
+ ord(TProp.typ):
+ begin // Set Default Reclose Intervals
+ // Set Definite Time Defaults
+ case ControlType of
+ CURRENT:
+ Delay_Time := 0.0;
+ VOLTAGE:
+ Delay_Time := 0.0;
+ REVPOWER:
+ Delay_Time := 0.1;
+ NEGCURRENT, NEGVOLTAGE:
+ Delay_Time := 0.1;
+ GENERIC:
+ Delay_Time := 0.1;
+ DISTANCE:
+ Delay_Time := 0.1;
+ TD21:
+ Delay_Time := 0.1;
+ DOC:
+ Delay_Time := 0.0;
else
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Relay "' + Name + '"', 381);
-
- if ParamPointer > 0 then
- case PropertyIdxMap[ParamPointer] of
- {internal Relay Property commands}
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 382);
- 1:
- MonitoredElementName := lowercase(param);
- 2:
- MonitoredElementTerminal := Parser.IntValue;
- 3:
- ElementName := lowercase(param);
- 4:
- ElementTerminal := Parser.IntValue;
- 5:
- InterpretRelayType(Param);
- 6:
- PhaseCurve := GetTccCurve(Param);
- 7:
- GroundCurve := GetTCCCurve(Param);
- 8:
- PhaseTrip := Parser.Dblvalue;
- 9:
- GroundTrip := Parser.Dblvalue;
- 10:
- PhaseInst := Parser.Dblvalue;
- 11:
- GroundInst := Parser.Dblvalue;
- 12:
- ResetTime := Parser.Dblvalue;
- 13:
- NumReclose := Parser.Intvalue - 1; // one less than number of shots
- 14:
- if Comparetext(Param, 'NONE') = 0 then
- NumReclose := 1
- else
- NumReclose := Parser.ParseAsVector(4, RecloseIntervals); // max of 4 allowed
- 15:
- OVCurve := GetTCCCurve(Param);
- 16:
- UVCurve := GetTCCCurve(Param);
- 17:
- kVBase := Parser.DblValue;
- 18:
- Breaker_time := Parser.DblValue;
- 20:
- MonitorVariable := lowercase(param); // for pc elements
- 21:
- PctPickup46 := Parser.DblValue;
- 22:
- Isqt46 := Parser.DblValue;
- 23:
- BaseAmps46 := Parser.DblValue;
- 24:
- Delay_Time := Parser.DblValue;
- 25:
- PctPickup47 := Parser.DblValue;
- 26:
- Overtrip := Parser.DblValue;
- 27:
- Undertrip := Parser.DblValue;
- 28:
- TDPhase := Parser.DblValue;
- 29:
- TDGround := Parser.DblValue;
- 30:
- Z1mag := Parser.DblValue;
- 31:
- Z1ang := Parser.DblValue;
- 32:
- Z0mag := Parser.DblValue;
- 33:
- Z0ang := Parser.DblValue;
- 34:
- Mphase := Parser.DblValue;
- 35:
- Mground := Parser.DblValue;
- 36:
- ShowEventLog := InterpretYesNo(param);
- 37:
- DebugTrace := InterpretYesNo(Param);
- 38:
- Dist_Reverse := InterpretYesNo(Param);
- 39:
- InterpretRelayState(Param, ParamName); // set normal state
- 19, 40:
- InterpretRelayState(Param, ParamName); // set state
+ Delay_Time := 0.0;
+ end;
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveRelayObj, ParamPointer - NumPropsthisClass)
+ case ControlType of
+ CURRENT:
+ begin
+ RecloseIntervals[1] := 0.5;
+ RecloseIntervals[2] := 2.0;
+ RecloseIntervals[3] := 2.0;
+ NumReclose := 3;
+ SetAsNextSeq(Ord(TProp.Shots));
+ SetAsNextSeq(Ord(TProp.RecloseIntervals));
end;
-
- if ParamPointer > 0 then
- case PropertyIdxMap[ParamPointer] of
- {Default the controlled element to the monitored element}
- 1:
- ElementName := MonitoredElementName;
- 2:
- ElementTerminal := MonitoredElementTerminal;
- 5:
- begin {Set Default Reclose Intervals}
- case lowercase(param)[1] of
- 'c':
- PropertyValue[14] := '(0.5, 2.0, 2.0)';
- 'v':
- PropertyValue[14] := '(5.0)';
- end;
- AuxParser.CmdString := PropertyValue[14];
- ParamName := AuxParser.NextParam;
- NumReclose := AuxParser.ParseAsVector(4, RecloseIntervals);
- end;
- 19, 40:
- if not NormalStateSet then
- begin
- NormalStateSet := TRUE; // 'normal state' defaults to 'state' only when the latter is specified for the first time
- FNormalState := FPresentState;
- end;
+ VOLTAGE:
+ begin
+ RecloseIntervals[3] := 5.0;
+ NumReclose := 1;
+ SetAsNextSeq(Ord(TProp.Shots));
+ SetAsNextSeq(Ord(TProp.RecloseIntervals));
end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ DOC:
+ begin
+ NumReclose := 0;
+ SetAsNextSeq(Ord(TProp.Shots));
+ SetAsNextSeq(Ord(TProp.RecloseIntervals));
+ end;
+ end;
end;
-
- RecalcElementData;
+ ord(TProp.Normal):
+ NormalStateSet := TRUE;
+ ord(TProp.action), ord(TProp.State):
+ if not NormalStateSet then
+ begin
+ NormalStateSet := TRUE; // 'normal state' defaults to 'state' only when the latter is specified for the first time
+ NormalState := FPresentState;
+ end;
end;
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-
-{--------------------------------------------------------------------------}
-function TRelay.MakeLike(const RelayName: String): Integer;
+procedure TRelayObj.MakeLike(OtherPtr: Pointer);
var
- OtherRelay: TRelayObj;
+ Other: TObj;
i: Integer;
begin
- Result := 0;
-
- {See if we can find this Relay name in the present collection}
- OtherRelay := Find(RelayName);
- if OtherRelay = NIL then
- begin
- DoSimpleMsg('Error in Relay MakeLike: "' + RelayName + '" Not Found.', 383);
- Exit;
- end;
-
- with DSS.ActiveRelayObj do
- begin
- NPhases := OtherRelay.Fnphases;
- NConds := OtherRelay.Fnconds; // Force Reallocation of terminal stuff
- ShowEventLog := OtherRelay.ShowEventLog; // but leave DebugTrace off
-
- ElementName := OtherRelay.ElementName;
- ElementTerminal := OtherRelay.ElementTerminal;
- ControlledElement := OtherRelay.ControlledElement; // Pointer to target circuit element
-
- MonitoredElement := OtherRelay.MonitoredElement; // Pointer to target circuit element
- MonitoredElementName := OtherRelay.MonitoredElementName; // Pointer to target circuit element
- MonitoredElementTerminal := OtherRelay.MonitoredElementTerminal; // Pointer to target circuit element
-
- PhaseCurve := OtherRelay.PhaseCurve;
- GroundCurve := OtherRelay.GroundCurve;
- OVCurve := OtherRelay.OVCurve;
- UVcurve := OtherRelay.UVcurve;
- PhaseTrip := OtherRelay.PhaseTrip;
- GroundTrip := OtherRelay.GroundTrip;
- TDPhase := OtherRelay.TDPhase;
- TDGround := OtherRelay.TDGround;
- PhaseInst := OtherRelay.PhaseInst;
- GroundInst := OtherRelay.GroundInst;
- ResetTime := OtherRelay.Resettime;
- NumReclose := OtherRelay.NumReclose;
- Delay_Time := OtherRelay.Delay_Time;
- Breaker_time := OtherRelay.Breaker_time;
-
- Reallocmem(RecloseIntervals, SizeOf(RecloseIntervals^[1]) * 4); // Always make a max of 4
- for i := 1 to NumReclose do
- RecloseIntervals^[i] := OtherRelay.RecloseIntervals^[i];
-
- kVBase := OtherRelay.kVBase;
- LockedOut := OtherRelay.LockedOut;
-
- FPresentState := OtherRelay.FPresentState;
- FNormalState := OtherRelay.NormalState;
- NormalStateSet := OtherRelay.NormalStateSet;
-
- ControlType := OtherRelay.ControlType;
- CondOffset := OtherRelay.CondOffset;
-
- {46 Relay Neg Seq Current}
- PickupAmps46 := OtherRelay.PickupAmps46;
- PctPickup46 := OtherRelay.PctPickup46;
- BaseAmps46 := OtherRelay.BaseAmps46;
- Isqt46 := OtherRelay.Isqt46;
-
- {47 Relay}
- PickupVolts47 := OtherRelay.PickupVolts47;
- PctPickup47 := OtherRelay.PctPickup47;
-
- {Generic Relay}
- MonitorVariable := OtherRelay.MonitorVariable;
- OverTrip := OtherRelay.OverTrip;
- UnderTrip := OtherRelay.UnderTrip;
-
- {Distance Relays}
- Z1Mag := OtherRelay.Z1Mag;
- Z1Ang := OtherRelay.Z1Ang;
- Z0Mag := OtherRelay.Z0Mag;
- Z0Ang := OtherRelay.Z0Ang;
- Mphase := OtherRelay.Mphase;
- Mground := OtherRelay.Mground;
- Dist_Reverse := OtherRelay.Dist_Reverse;
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherRelay.PropertyValue[i];
-
- end
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
+ ShowEventLog := Other.ShowEventLog; // but leave DebugTrace off
+
+ ElementTerminal := Other.ElementTerminal;
+ ControlledElement := Other.ControlledElement; // Pointer to target circuit element
+
+ MonitoredElement := Other.MonitoredElement; // Pointer to target circuit element
+ MonitoredElementTerminal := Other.MonitoredElementTerminal; // Pointer to target circuit element
+
+ PhaseCurve := Other.PhaseCurve;
+ GroundCurve := Other.GroundCurve;
+ OVCurve := Other.OVCurve;
+ UVcurve := Other.UVcurve;
+ PhaseTrip := Other.PhaseTrip;
+ GroundTrip := Other.GroundTrip;
+ TDPhase := Other.TDPhase;
+ TDGround := Other.TDGround;
+ PhaseInst := Other.PhaseInst;
+ GroundInst := Other.GroundInst;
+ ResetTime := Other.Resettime;
+ NumReclose := Other.NumReclose;
+ Delay_Time := Other.Delay_Time;
+ Breaker_time := Other.Breaker_time;
+
+ Reallocmem(RecloseIntervals, SizeOf(RecloseIntervals^[1]) * 4); // Always make a max of 4
+ for i := 1 to NumReclose do
+ RecloseIntervals^[i] := Other.RecloseIntervals^[i];
+
+ kVBase := Other.kVBase;
+ LockedOut := Other.LockedOut;
+
+ FPresentState := Other.FPresentState;
+ NormalState := Other.NormalState;
+ NormalStateSet := Other.NormalStateSet;
+
+ ControlType := Other.ControlType;
+ CondOffset := Other.CondOffset;
+
+ // 46 Relay Neg Seq Current
+ PickupAmps46 := Other.PickupAmps46;
+ PctPickup46 := Other.PctPickup46;
+ BaseAmps46 := Other.BaseAmps46;
+ Isqt46 := Other.Isqt46;
+
+ // 47 Relay
+ PickupVolts47 := Other.PickupVolts47;
+ PctPickup47 := Other.PctPickup47;
+
+ // Generic Relay
+ MonitorVariable := Other.MonitorVariable;
+ OverTrip := Other.OverTrip;
+ UnderTrip := Other.UnderTrip;
+
+ // Distance Relays
+ Z1Mag := Other.Z1Mag;
+ Z1Ang := Other.Z1Ang;
+ Z0Mag := Other.Z0Mag;
+ Z0Ang := Other.Z0Ang;
+ Mphase := Other.Mphase;
+ Mground := Other.Mground;
+ Dist_Reverse := Other.Dist_Reverse;
+
+ // Directional Overcurrent Relay
+ DOC_TiltAngleLow := Other.DOC_TiltAngleLow;
+ DOC_TiltAngleHigh := Other.DOC_TiltAngleHigh;
+ DOC_TripSetLow := Other.DOC_TripSetLow;
+ DOC_TripSetHigh := Other.DOC_TripSetHigh;
+ DOC_TripSetMag := Other.DOC_TripSetMag;
+ DOC_DelayInner := Other.DOC_DelayInner;
+ DOC_PhaseCurveInner := Other.DOC_PhaseCurveInner;
+ DOC_TDPhaseInner := Other.DOC_TDPhaseInner;
+ DOC_PhaseTripInner := Other.DOC_PhaseTripInner;
end;
-
-{==========================================================================}
-{ TRelayObj }
-{==========================================================================}
-
-
-{--------------------------------------------------------------------------}
constructor TRelayObj.Create(ParClass: TDSSClass; const RelayName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(RelayName);
+ Name := AnsiLowerCase(RelayName);
DSSObjType := ParClass.DSSClassType;
DebugTrace := FALSE;
- NPhases := 3; // Directly set conds and phases
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
- Nterms := 1; // this forces allocation of terminals and conductors
- // in base class
+ Nterms := 1; // this forces allocation of terminals and conductors in base class
- ElementName := '';
ControlledElement := NIL;
+ PreviousControlledElement := NIL;
ElementTerminal := 1;
- MonitoredElementName := '';
MonitoredElementTerminal := 1;
MonitoredElement := NIL;
@@ -697,12 +644,10 @@ constructor TRelayObj.Create(ParClass: TDSSClass; const RelayName: String);
RecloseIntervals^[2] := 2.0;
RecloseIntervals^[3] := 2.0;
-
FPresentState := CTRL_CLOSE;
- FNormalState := CTRL_CLOSE;
+ NormalState := CTRL_CLOSE;
NormalStateSet := FALSE;
-
Isqt46 := 1.0;
BaseAmps46 := 100.0;
PctPickup46 := 20.0;
@@ -729,6 +674,17 @@ constructor TRelayObj.Create(ParClass: TDSSClass; const RelayName: String);
td21_quiet := 0;
Dist_Reverse := FALSE;
+ DOC_TiltAngleLow := 90.0;
+ DOC_TiltAngleHigh := 90.0;
+ DOC_TripSetLow := 0;
+ DOC_TripSetHigh := -1.0;
+ DOC_TripSetMag := -1.0;
+
+ DOC_DelayInner := -1.0;
+ DOC_PhaseCurveInner := NIL;
+ DOC_PhaseTripInner := 1.0;
+ DOC_TDPhaseInner := 1.0;
+
Operationcount := 1;
LockedOut := FALSE;
ArmedForOpen := FALSE;
@@ -744,19 +700,16 @@ constructor TRelayObj.Create(ParClass: TDSSClass; const RelayName: String);
DSSObjType := ParClass.DSSClassType; //cap_CONTROL;
- InitPropertyValues(0);
-
-
// RecalcElementData;
-
end;
destructor TRelayObj.Destroy;
begin
- MonitoredElementName := '';
ReallocMem(RecloseIntervals, 0);
if Assigned(cBuffer) then
ReallocMem(cBuffer, 0);
+ if Assigned (cvBuffer) then
+ ReallocMem(cvBuffer, 0);
if Assigned (td21_h) then
ReallocMem (td21_h, 0);
if Assigned (td21_dV) then
@@ -769,37 +722,32 @@ destructor TRelayObj.Destroy;
inherited Destroy;
end;
-{--------------------------------------------------------------------------}
procedure TRelayObj.RecalcElementData;
-var
- DevIndex: Integer;
begin
if DebugTrace then
AppendToEventLog(
- 'Relay.' + self.Name,
+ 'Relay.' + self.Name,
Format('RecalcElementData NumReclose=%d', [NumReclose])
);
- Devindex := GetCktElementIndex(MonitoredElementName); // Global function
- if DevIndex > 0 then
+ if MonitoredElement <> NIL then
begin
- MonitoredElement := ActiveCircuit.CktElements.Get(DevIndex);
- Nphases := MonitoredElement.NPhases; // Force number of phases to be same
+ FNphases := MonitoredElement.NPhases; // Force number of phases to be same
if MonitoredElementTerminal > MonitoredElement.Nterms then
begin
- DoErrorMsg('Relay: "' + Name + '"',
- 'Terminal no. "' + '" does not exist.',
- 'Re-specify terminal no.', 384);
+ DoErrorMsg(Format(_('Relay: "%s"'), [Name]),
+ Format(_('Terminal no. "%d" does not exist.'), [MonitoredElementTerminal]),
+ _('Re-specify terminal no.'), 384);
end
else
begin
// Sets name of i-th terminal's connected bus in Relay's buslist
Setbus(1, MonitoredElement.GetBus(MonitoredElementTerminal));
-
+
// Allocate a buffer big enough to hold everything from the monitored element
ReAllocMem(cBuffer, SizeOf(cbuffer^[1]) * MonitoredElement.Yorder);
-
- if (ControlType = Distance) or (ControlType = TD21) then
+
+ if (ControlType = Distance) or (ControlType = TD21) or (ControlType = DOC) then
ReAllocMem(cvBuffer, SizeOf(cvBuffer^[1]) * MonitoredElement.Yorder);
CondOffset := (MonitoredElementTerminal - 1) * MonitoredElement.NConds; // for speedy sampling
@@ -808,43 +756,40 @@ procedure TRelayObj.RecalcElementData;
Generic:
begin
if (MonitoredElement.DSSObjType and BASECLASSMASK) <> PC_ELEMENT then
- DoSimpleMsg('Relay ' + Name + ': Monitored element for Generic relay is not a PC Element.', 385)
+ DoSimpleMsg('Relay %s: Monitored element for Generic relay is not a PC Element.', [Name], 385)
else
begin
MonitorVarIndex := (MonitoredElement as TPCelement).LookupVariable(MonitorVariable);
if MonitorVarIndex < 1 then // oops
begin
- DoSimpleMsg('Relay ' + Name + ': Monitor variable "' + MonitorVariable + '" does not exist.', 386);
+ DoSimpleMsg('Relay "%s": Monitor variable "%s" does not exist.', [Name, MonitorVariable], 386);
end;
end;
end;
- else
-
end;
end;
end;
-{Check for existence of Controlled Element}
+ {Check for existence of Controlled Element}
- // If previously assigned, reset HasOCPDevice flag in case this is a move
- if Assigned(ControlledElement) then
+ // If previously assigned, reset HasOCPDevice flag in case this is a move
+ if (PreviousControlledElement <> NIL) then
begin
- ControlledElement.HasOCPDevice := FALSE;
- ControlledElement.HasAutoOCPDevice := FALSE;
+ Exclude(PreviousControlledElement.Flags, Flg.HasOCPDevice);
+ Exclude(PreviousControlledElement.Flags, Flg.HasAutoOCPDevice);
+ PreviousControlledElement := ControlledElement;
end;
- Devindex := GetCktElementIndex(ElementName); // Global function
- if DevIndex > 0 then
+ if ControlledElement <> NIL then
begin // Both CktElement and monitored element must already exist
- ControlledElement := ActiveCircuit.CktElements.Get(DevIndex);
ControlledElement.ActiveTerminalIdx := ElementTerminal; // Make the 1 st terminal active
- // If the relay becomes disabled, leave at False
+ // If the relay becomes disabled, leave at False
if Enabled then
begin
- ControlledElement.HasOCPDevice := TRUE; // For Reliability calcs
- ControlledElement.HasAutoOCPDevice := TRUE; // For Reliability calcs
+ Include(ControlledElement.Flags, Flg.HasOCPDevice); // For Reliability calcs
+ Include(ControlledElement.Flags, Flg.HasAutoOCPDevice); // For Reliability calcs
end;
// Open/Close State of controlled element based on state assigned to the control
@@ -865,12 +810,13 @@ procedure TRelayObj.RecalcElementData;
end
else
begin
- ControlledElement := NIL; // element not found
- DoErrorMsg('Relay: "' + Self.Name + '"', 'CktElement Element "' + ElementName + '" Not Found.',
- ' Element must be defined previously.', 387);
+ // element not found/set
+ DoErrorMsg(Format(_('Relay: "%s"'), [Self.Name]),
+ _('CktElement for SwitchedObj is not set.'),
+ _('Element must be defined previously.'), 387);
end;
- {Misc stuff}
+ // Misc stuff
PickupAmps46 := BaseAmps46 * PctPickup46 * 0.01;
@@ -887,22 +833,22 @@ procedure TRelayObj.RecalcElementData;
begin
Dist_Z1 := pclx(Z1Mag, Z1Ang / RadiansToDegrees);
Dist_Z0 := pclx(Z0Mag, Z0Ang / RadiansToDegrees);
- Dist_K0 := cdiv(cdivreal(csub(Dist_Z0, Dist_Z1), 3.0), Dist_Z1);
+ Dist_K0 := ((Dist_Z0 - Dist_Z1) / 3.0) / Dist_Z1;
end;
end;
-procedure TRelayObj.MakePosSequence;
+procedure TRelayObj.MakePosSequence();
begin
if MonitoredElement <> NIL then
begin
- Nphases := MonitoredElement.NPhases;
+ FNphases := MonitoredElement.NPhases;
Nconds := FNphases;
Setbus(1, MonitoredElement.GetBus(ElementTerminal));
// Allocate a buffer big enough to hold everything from the monitored element
ReAllocMem(cBuffer, SizeOf(cbuffer^[1]) * MonitoredElement.Yorder);
-
- if (ControlType = Distance) or (ControlType = TD21) then
+
+ if (ControlType = Distance) or (ControlType = TD21) or (ControlType = DOC) then
ReAllocMem(cvBuffer, SizeOf(cvBuffer^[1]) * MonitoredElement.Yorder);
CondOffset := (ElementTerminal - 1) * MonitoredElement.NConds; // for speedy sampling
@@ -917,37 +863,17 @@ procedure TRelayObj.MakePosSequence;
inherited;
end;
-{--------------------------------------------------------------------------}
-procedure TRelayObj.CalcYPrim;
-begin
- // leave YPrims as nil and they will be ignored
- // Yprim is zeroed when created. Leave it as is.
- // IF YPrim=nil THEN YPrim := TcMatrix.CreateMatrix(Yorder);
-end;
-
-{--------------------------------------------------------------------------}
-procedure TRelayObj.GetCurrents(Curr: pComplexArray);
-var
- i: Integer;
-begin
-
- for i := 1 to Fnconds do
- Curr^[i] := CZERO;
-
-end;
-
-{--------------------------------------------------------------------------}
procedure TRelayObj.DoPendingAction(const Code, ProxyHdl: Integer);
begin
- if DebugTrace then
+ if DebugTrace then
AppendToEventLog('Relay.' + self.Name, Format(
'DoPendingAction Code=%d State=%d ArmedOpen=%s Close=%s Reset=%s Count=%d NumReclose=%d', [
- Integer (Code),
- Integer (FPresentState),
- BoolToStr(ArmedForOpen),
- BoolToStr(ArmedForClose),
- BoolToStr(ArmedForReset),
- OperationCount,
+ Integer (Code),
+ Integer (FPresentState),
+ BoolToStr(ArmedForOpen),
+ BoolToStr(ArmedForClose),
+ BoolToStr(ArmedForReset),
+ OperationCount,
NumReclose
]));
@@ -964,20 +890,20 @@ procedure TRelayObj.DoPendingAction(const Code, ProxyHdl: Integer);
begin
LockedOut := TRUE;
if ShowEventLog then
- AppendtoEventLog('Relay.' + Self.Name, 'Opened on ' + RelayTarget + ' & Locked Out ');
+ AppendtoEventLog('Relay.' + Self.Name, Format(_('Opened on %s & Locked Out'), [RelayTarget]));
end
else
if ShowEventLog then
- AppendtoEventLog('Relay.' + Self.Name, 'Opened on' + RelayTarget);
+ AppendtoEventLog('Relay.' + Self.Name, Format(_('Opened on %s'), [RelayTarget]));
if PhaseTarget and ShowEventLog then
- AppendtoEventLog(' ', 'Phase Target');
+ AppendtoEventLog(' ', _('Phase Target'));
if GroundTarget and ShowEventLog then
- AppendtoEventLog(' ', 'Ground Target');
-
+ AppendtoEventLog(' ', _('Ground Target'));
+
ArmedForOpen := FALSE;
- if ControlType = td21 then
+ if ControlType = td21 then
td21_quiet := td21_pt + 1;
end;
@@ -985,14 +911,14 @@ procedure TRelayObj.DoPendingAction(const Code, ProxyHdl: Integer);
if FPresentState = CTRL_OPEN then
if ArmedForClose and not LockedOut then
begin
- ControlledElement.Closed[0] := TRUE; // Close all phases of active terminal
+ ControlledElement.Closed[0] := TRUE; // Close all phases of active terminal
Inc(OperationCount);
if ShowEventLog then
- AppendtoEventLog('Relay.' + Self.Name, 'Closed');
+ AppendtoEventLog('Relay.' + Self.Name, _('Closed'));
ArmedForClose := FALSE;
- if ControlType = td21 then
+ if ControlType = td21 then
td21_quiet := td21_pt div 2;
end;
@@ -1000,46 +926,21 @@ procedure TRelayObj.DoPendingAction(const Code, ProxyHdl: Integer);
if ArmedForClose and not LockedOut then
begin
if ShowEventLog then
- if ShowEventLog then AppendToEventLog('Relay.'+Self.Name, 'Reset');
+ if ShowEventLog then AppendToEventLog('Relay.'+Self.Name, _('Reset'));
Reset();
- if ControlType = td21 then
+ if ControlType = td21 then
td21_quiet := td21_pt div 2
end;
end;
end;
-{--------------------------------------------------------------------------}
-procedure TRelayObj.InterpretRelayState(const Action: String; const PropertyName: String);
-begin
- if (LowerCase(PropertyName[1]) = 's') or (LowerCase(PropertyName[1]) = 'a') then
- begin
- // state or action (deprecated)
- case LowerCase(Action[1]) of
- 'o', 't':
- FPresentState := CTRL_OPEN;
- 'c':
- FPresentState := CTRL_CLOSE;
- end;
- end
- else // Normal
- begin
- case LowerCase(Action)[1] of
- 'o', 't':
- FNormalState := CTRL_OPEN;
- 'c':
- FNormalState := CTRL_CLOSE;
- end;
- NormalStateSet := TRUE;
- end;
-end;
-{--------------------------------------------------------------------------}
procedure TRelayObj.Sample;
begin
ControlledElement.ActiveTerminalIdx := ElementTerminal;
- if ControlledElement.Closed[0] // Check state of phases of active terminal
+ if ControlledElement.Closed[0] // Check state of phases of active terminal
then
FPresentState := CTRL_CLOSE
else
@@ -1047,88 +948,32 @@ procedure TRelayObj.Sample;
case ControlType of
CURRENT:
- OverCurrentLogic; {Current}
+ OverCurrentLogic; // Current
VOLTAGE:
- VoltageLogic; {Reclosing Voltage Relay - definite time}
+ VoltageLogic; // Reclosing Voltage Relay - definite time
REVPOWER:
- RevPowerLogic; // one shot to lockout
+ RevPowerLogic; // one shot to lockout
NEGCURRENT:
NegSeq46Logic; // one shot to lockout
NEGVOLTAGE:
NegSeq47Logic; // one shot to lockout
GENERIC:
- GenericLogic;// one shot to lockout
- DISTANCE:
+ GenericLogic; // one shot to lockout
+ DISTANCE:
DistanceLogic;
- TD21:
+ TD21:
TD21Logic;
+ DOC:
+ DirectionalOvercurrentLogic;
end;
end;
-
-{--------------------------------------------------------------------------}
-procedure TRelayObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-{Note PropertyValue is aligned with the internal indices}
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[PropertyIdxMap[i]]);
- end;
-
- if Complete then
- begin
- FSWriteln(F);
- end;
-
-end;
-
-function TRelayObj.GetPropertyValue(Index: Integer): String;
-var
- i: Integer;
-begin
- Result := '';
- with ParentClass do
- case Index of
- 14:
- begin
- Result := '(';
- if NumReclose = 0 then
- Result := Result + 'NONE'
- else
- for i := 1 to NumReclose do
- Result := Result + Format('%-g, ', [RecloseIntervals^[i]]);
- Result := Result + ')';
- end;
- 39:
- if FNormalState = CTRL_OPEN then
- Result := 'open'
- else
- Result := 'closed';
- 19, 40:
- if FPresentState = CTRL_OPEN then
- Result := 'open'
- else
- Result := 'closed';
- else
- Result := inherited GetPropertyValue(Index);
- end;
-end;
-
-
procedure TRelayObj.Reset;
begin
- if ShowEventLog then
- AppendToEventLog ('Relay.' + self.Name, 'Resetting');
+ if ShowEventLog then
+ AppendToEventLog ('Relay.' + self.Name, _('Resetting'));
- FPresentState := FNormalState;
+ FPresentState := NormalState;
ArmedForOpen := FALSE;
ArmedForClose := FALSE;
@@ -1143,7 +988,7 @@ procedure TRelayObj.Reset;
ControlledElement.ActiveTerminalIdx := ElementTerminal;
- if FNormalState = CTRL_OPEN then
+ if NormalState = CTRL_OPEN then
begin
ControlledElement.Closed[0] := FALSE; // Open all phases of active terminal
LockedOut := TRUE;
@@ -1162,7 +1007,7 @@ function TRelayObj.get_PresentState: EControlAction;
if ControlledElement <> NIL then
begin
ControlledElement.ActiveTerminalIdx := ElementTerminal;
-
+
if not ControlledElement.Closed[0] then
FPresentState:= CTRL_OPEN
else
@@ -1177,7 +1022,7 @@ procedure TRelayObj.set_PresentState(const Value: EControlAction);
Exit;
FPresentState := Value;
-
+
if ControlledElement = NIL then
Exit;
@@ -1200,122 +1045,16 @@ procedure TRelayObj.set_PresentState(const Value: EControlAction);
end;
end;
-procedure TRelayObj.set_NormalState(const Value: EControlAction);
-begin
- FNormalState := Value;
- NormalStateSet := TRUE;
-end;
-
-procedure TRelayObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := ''; //'element';
- PropertyValue[2] := '1'; //'terminal';
- PropertyValue[3] := '';
- PropertyValue[4] := '1'; //'terminal';
- PropertyValue[5] := 'current';
- PropertyValue[6] := '';
- PropertyValue[7] := '';
- PropertyValue[8] := '1.0';
- PropertyValue[9] := '1.0';
- PropertyValue[10] := '0.0';
- PropertyValue[11] := '0.0';
- PropertyValue[12] := '15';
- PropertyValue[13] := '4';
- PropertyValue[14] := '(0.5, 2.0, 2.0)';
- PropertyValue[15] := '';
- PropertyValue[16] := '';
- PropertyValue[17] := '0.0';
- PropertyValue[18] := '0.0';
- PropertyValue[19] := 'closed';
- PropertyValue[20] := '';
- PropertyValue[21] := '20';
- PropertyValue[22] := '1';
- PropertyValue[23] := '100';
- PropertyValue[24] := '0';
- PropertyValue[25] := '2';
- PropertyValue[26] := '1.2';
- PropertyValue[27] := '0.8';
- PropertyValue[28] := '1.0';
- PropertyValue[29] := '1.0';
- PropertyValue[30] := '0.7';
- PropertyValue[31] := '64.0';
- PropertyValue[32] := '2.1';
- PropertyValue[33] := '68.0';
- PropertyValue[34] := '0.7';
- PropertyValue[35] := '0.7';
- PropertyValue[36] := 'Yes';
- PropertyValue[37] := 'No';
- PropertyValue[39] := 'closed';
- PropertyValue[40] := 'closed';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-procedure TRelayObj.InterpretRelayType(const S: String);
-begin
- case lowercase(S[1]) of
- 'c':
- ControlType := CURRENT;
- 'v':
- ControlType := VOLTAGE;
- 'r':
- ControlType := REVPOWER;
- '4':
- case S[2] of
- '6':
- ControlType := NEGCURRENT;
- '7':
- ControlType := NEGVOLTAGE;
- end;
- 'g':
- ControlType := GENERIC;
- 'd':
- ControlType := DISTANCE;
- 't':
- ControlType := TD21;
- else
- ControlType := CURRENT;
- end;
-
- {Set Definite Time Defaults}
- case lowercase(S)[1] of
- 'c':
- Delay_Time := 0.0;
- 'v':
- Delay_Time := 0.0;
- 'r':
- Delay_Time := 0.1;
- '4':
- Delay_Time := 0.1;
- 'g':
- Delay_Time := 0.1;
- 'd':
- Delay_Time := 0.1;
- 't':
- Delay_Time := 0.1;
- else
- Delay_Time := 0.0;
- end;
-
- PropertyValue[24] := Format('%-.g', [Delay_Time]);
-end;
-
procedure TRelayObj.GenericLogic;
-{ Generic relays only work on PC Elements With control terminals
-}
-
+// Generic relays only work on PC Elements With control terminals
var
VarValue: Double;
-
begin
-
- with MonitoredElement do
+ with MonitoredElement do
begin
VarValue := TPCElement(MonitoredElement).Variable[MonitorVarIndex];
- {Check for Trip}
+ // Check for Trip
if (VarValue > OverTrip) or (VarValue < UnderTrip) then
begin
if not ArmedForOpen then // push the trip operation and arm to trip
@@ -1327,8 +1066,8 @@ procedure TRelayObj.GenericLogic;
ArmedForOpen := TRUE;
end
end
- else {Within bounds}
- begin {Less Than pickup value: reset if armed}
+ else // Within bounds
+ begin // Less Than pickup value: reset if armed
if ArmedForOpen then // We became unarmed, so reset and disarm
with ActiveCircuit do
begin
@@ -1336,27 +1075,18 @@ procedure TRelayObj.GenericLogic;
ArmedForOpen := FALSE;
end;
end;
-
-
end; {With MonitoredElement}
-
end;
procedure TRelayObj.NegSeq46Logic;
-
-{
- Negative Sequence Current Relay
- Patterned after Basler relay
-}
-
+// Negative Sequence Current Relay
+// Patterned after Basler relay
var
NegSeqCurrentMag, TripTime: Double;
iOffset: Integer;
I012: array[1..3] of Complex;
-
begin
-
- with MonitoredElement do
+ with MonitoredElement do
begin
MonitoredElement.ActiveTerminalIdx := MonitoredElementTerminal;
MonitoredElement.GetCurrents(cBuffer);
@@ -1369,7 +1099,7 @@ procedure TRelayObj.NegSeq46Logic;
with ActiveCircuit do
begin
RelayTarget := '-Seq Curr';
- {simple estimate of trip time assuming current will be constant}
+ // simple estimate of trip time assuming current will be constant
if Delay_Time > 0.0 then
Triptime := Delay_Time
else
@@ -1380,7 +1110,7 @@ procedure TRelayObj.NegSeq46Logic;
end
end
else
- begin {Less Than pickup value: reset if armed}
+ begin // Less Than pickup value: reset if armed
if ArmedForOpen then // We became unarmed, so reset and disarm
with ActiveCircuit do
begin
@@ -1388,9 +1118,7 @@ procedure TRelayObj.NegSeq46Logic;
ArmedForOpen := FALSE;
end;
end;
- end; {With MonitoredElement}
-
-
+ end; // With MonitoredElement
end;
procedure TRelayObj.OvercurrentLogic;
@@ -1412,16 +1140,16 @@ procedure TRelayObj.OvercurrentLogic;
GroundTime := -1.0;
PhaseTime := -1.0; {No trip}
- // Check largest Current of all phases of monitored element
+ // Check largest Current of all phases of monitored element
MonitoredElement.GetCurrents(cBuffer);
- {Check Ground Trip, if any}
+ // Check Ground Trip, if any
if ((GroundCurve <> NIL) or (Delay_Time > 0.0)) and (GroundTrip > 0.0) then
begin
Csum := CZERO;
for i := (1 + CondOffset) to (Fnphases + CondOffset) do
begin
- caccum(Csum, cBuffer^[i]);
+ Csum += cBuffer^[i];
end;
Cmag := Cabs(Csum);
if (GroundInst > 0.0) and (Cmag >= GroundInst) and (OperationCount = 1) then
@@ -1439,7 +1167,7 @@ procedure TRelayObj.OvercurrentLogic;
if DebugTrace then
AppendToEventLog('Relay.' + Self.Name, Format(
- 'Ground Trip: Mag=%.3g, Mult=%.3g, Time=%.3g',
+ _('Ground Trip: Mag=%.3g, Mult=%.3g, Time=%.3g'),
[Cmag, Cmag / GroundTrip, GroundTime]
));
end;
@@ -1450,9 +1178,9 @@ procedure TRelayObj.OvercurrentLogic;
GroundTarget := TRUE;
end;
- // If GroundTime > 0 then we have a ground trip
+ // If GroundTime > 0 then we have a ground trip
- {Check Phase Trip, if any}
+ // Check Phase Trip, if any
if ((PhaseCurve <> NIL) or (Delay_Time > 0.0)) and (PhaseTrip > 0.0) then
begin
@@ -1484,15 +1212,15 @@ procedure TRelayObj.OvercurrentLogic;
end;
end;
end;
-
+
if DebugTrace then
AppendToEventLog(
'Relay.' + Self.Name, Format(
- 'Phase %d Trip: Mag=%.3g, Mult=%.3g, Time=%.3g',
+ _('Phase %d Trip: Mag=%.3g, Mult=%.3g, Time=%.3g'),
[i-CondOffset, Cmag, Cmag / PhaseTrip, PhaseTime]
));
end;
- // If PhaseTime > 0 then we have a phase trip
+ // If PhaseTime > 0 then we have a phase trip
if PhaseTime > 0.0 then
begin
@@ -1532,10 +1260,8 @@ procedure TRelayObj.OvercurrentLogic;
GroundTarget := FALSE;
end;
end;
- end; {IF PresentState=CLOSE}
-
- end; {With MonitoredElement}
-
+ end; // IF PresentState=CLOSE
+ end; // With MonitoredElement
end;
procedure TRelayObj.DistanceLogic;
@@ -1543,13 +1269,13 @@ procedure TRelayObj.DistanceLogic;
i, j: Integer;
Vloop, Iloop, Zloop, Ires, kIres, Zreach: Complex;
i2, min_distance, fault_distance, t_event: Double;
- Targets: TStringList;
+ Targets: TStringList = NIL;
PickedUp: Boolean;
begin
- If LockedOut Then
+ If LockedOut Then
Exit;
-
- with MonitoredElement do
+
+ with MonitoredElement do
begin
PickedUp := False;
min_distance := 1.0e30;
@@ -1557,86 +1283,89 @@ procedure TRelayObj.DistanceLogic;
if Dist_Reverse then
for i := 1 to MonitoredElement.NPhases do
- cBuffer^[i + CondOffset] := cnegate (cBuffer^[i + CondOffset]);
+ cBuffer^[i + CondOffset] := -cBuffer^[i + CondOffset];
Ires := cZERO;
- for i := 1 to MonitoredElement.Nphases do
- caccum(Ires, cBuffer^[i + CondOffset]);
+ for i := 1 to MonitoredElement.Nphases do
+ Ires += cBuffer^[i + CondOffset];
- kIres := cmul(Dist_K0, Ires);
+ kIres := Dist_K0 * Ires;
MonitoredElement.GetTermVoltages(MonitoredElementTerminal, cvBuffer);
- for i := 1 to MonitoredElement.NPhases do
+ for i := 1 to MonitoredElement.NPhases do
begin
- for j := i to MonitoredElement.NPhases do
+ for j := i to MonitoredElement.NPhases do
begin
- if (i = j) then
+ if (i = j) then
begin
Vloop := cvBuffer^[i];
- Iloop := cadd (cBuffer^[i + CondOffset], kIres);
- Zreach := cmulreal (Dist_Z1, Mground); // not Dist_Z0 because it's included in Dist_K0
- end
- else
+ Iloop := cBuffer^[i + CondOffset] + kIres;
+ Zreach := Dist_Z1 * Mground; // not Dist_Z0 because it's included in Dist_K0
+ end
+ else
begin
- Vloop := csub (cvBuffer^[i], cvBuffer^[j]);
- Iloop := csub (cBuffer^[i + CondOffset], cBuffer^[j + CondOffset]);
- Zreach := cmulreal (Dist_Z1, Mphase);
+ Vloop := cvBuffer^[i] - cvBuffer^[j];
+ Iloop := cBuffer^[i + CondOffset] - cBuffer^[j + CondOffset];
+ Zreach := Dist_Z1 * Mphase;
end;
-
+
i2 := Iloop.re * Iloop.re + Iloop.im * Iloop.im;
- if i2 > 0.1 then
+ if i2 > 0.1 then
begin
- Zloop := cdiv (Vloop, Iloop);
-
+ Zloop := Vloop / Iloop;
+
// start with a very simple rectangular characteristic
- if (Zloop.re >= 0) and (Zloop.im >= 0.0) and (Zloop.re <= Zreach.re) and (Zloop.im <= Zreach.im) then
+ if DebugTrace and (ActiveCircuit.Solution.DynaVars.t > 0.043) then
+ AppendToEventLog(self.FullName, Format('Zloop[%d,%d]=%.4f+j%.4f', [i, j, Zloop.re, Zloop.im]));
+
+ if (Zloop.re >= 0) and (Zloop.im >= MIN_DISTANCE_REACTANCE) and (Zloop.re <= Zreach.re) and (Zloop.im <= Zreach.im) then
begin
if not PickedUp then
begin
Targets := TStringList.Create();
Targets.Sorted := True;
end;
- if (i = j) then
+ if (i = j) then
begin
Targets.Add(Format('G%d', [i]));
- end
- else
+ end
+ else
begin
Targets.Add(Format('P%d%d', [i, j]));
end;
-
+
fault_distance := cabs2(zloop) / cabs2 (zreach);
- if fault_distance < min_distance then
+ if fault_distance < min_distance then
min_distance := fault_distance;
-
+
PickedUp := True;
end;
end;
end;
end;
- if PickedUp then
+ if PickedUp then
begin
- if DebugTrace then
+ if DebugTrace then
AppendToEventLog ('Relay.' + Self.Name, 'Picked up');
- if ArmedForReset then
+ if ArmedForReset then
begin
ActiveCircuit.ControlQueue.Delete(LastEventHandle);
ArmedForReset := FALSE;
end;
- if not ArmedForOpen then
- with ActiveCircuit do
+ if not ArmedForOpen then
+ with ActiveCircuit do
begin
- RelayTarget := Format('21 %.3f pu dist', [min_distance]);
+ RelayTarget := Format(_('21 %.3f pu dist'), [min_distance]);
t_event := Solution.DynaVars.t + Delay_Time + Breaker_time;
for i := 0 to pred(Targets.Count) do
RelayTarget := RelayTarget + ' ' + Targets[i];
LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, t_event, CTRL_OPEN, 0, Self);
ArmedForOpen := TRUE;
- if OperationCount <= NumReclose then
+ if OperationCount <= NumReclose then
begin
LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, t_event + RecloseIntervals^[OperationCount], CTRL_CLOSE, 0, Self);
ArmedForClose := TRUE;
@@ -1644,22 +1373,22 @@ procedure TRelayObj.DistanceLogic;
end;
Targets.Free();
- end
- else
+ end
+ else
begin // not picked up; reset if necessary
- if (OperationCount > 1) and (ArmedForReset = FALSE) then
+ if (OperationCount > 1) and (ArmedForReset = FALSE) then
begin // this implements the reset, whether picked up or not
ArmedForReset := TRUE;
with ActiveCircuit do
LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, Solution.DynaVars.t + ResetTime, CTRL_RESET, 0, Self);
end;
- if ArmedForOpen then
+ if ArmedForOpen then
begin // this implements the drop-out, if picked up
ArmedForOpen := FALSE;
ArmedForClose := FALSE;
end;
end;
- end; {With MonitoredElement}
+ end; // With MonitoredElement
end;
procedure TRelayObj.TD21Logic;
@@ -1667,22 +1396,22 @@ procedure TRelayObj.TD21Logic;
i, j: Integer;
Vloop, Iloop, Zhsd, Zdir, Uhsd, Uref, Ires, kIres: Complex;
i2, i2fault, min_distance, fault_distance, Uref2, Uhsd2, t_event, dt: Double;
- Targets: TStringList;
+ Targets: TStringList = NIL;
PickedUp, FaultDetected: Boolean;
ib, iv, ii: Integer;
begin
dt := ActiveCircuit.Solution.DynaVars.h;
- if dt > 0.0 then
+ if dt > 0.0 then
begin
if dt > 1.0 / ActiveCircuit.Solution.Frequency then
- DoErrorMsg('Relay: "' + Name + '"',
- 'Has type TD21 with time step greater than one cycle.',
- 'Reduce time step, or change type to Distance.',
+ DoErrorMsg(Format(_('Relay: "%s"'), [Name]),
+ _('Has type TD21 with time step greater than one cycle.'),
+ _('Reduce time step, or change type to Distance.'),
388
);
-
+
i := round (1.0 / 60.0 / dt + 0.5);
- if i > td21_pt then
+ if i > td21_pt then
begin
td21_i := 0; // ring buffer index to be incremented before actual use
td21_pt := i;
@@ -1693,49 +1422,51 @@ procedure TRelayObj.TD21Logic;
ReAllocMem(td21_Uref, SizeOf(td21_Uref^[1]) * Nphases);
ReAllocMem(td21_dI, SizeOf(td21_dI^[1]) * Nphases);
if DebugTrace then
- AppendToEventLog('Relay.' + Self.Name, Format(
- 'TD21 prep %d phases, %.3g dt, %d points, %d elements',
- [NPhases, dt, td21_pt, td21_stride * td21_pt]
- ));
+ AppendToEventLog(self.FullName,
+ Format(
+ _('TD21 prep %d phases, %.3g dt, %d points, %d elements'),
+ [NPhases, dt, td21_pt, td21_stride * td21_pt]
+ )
+ );
end;
end;
- if LockedOut then
+ if LockedOut then
Exit;
- with MonitoredElement Do
+ with MonitoredElement Do
begin
- FaultDetected := False;
+ FaultDetected := False;
MonitoredElement.GetCurrents(cBuffer);
if Dist_Reverse then
for i := 1 to MonitoredElement.NPhases do
- cBuffer^[i+CondOffset] := cnegate (cBuffer^[i+CondOffset]);
+ cBuffer^[i+CondOffset] := -cBuffer^[i+CondOffset];
i2fault := PhaseTrip * PhaseTrip;
- for i := 1 to Nphases do
+ for i := 1 to Nphases do
begin
i2 := cabs2 (cBuffer^[i+CondOffset]);
if i2 > i2fault then
FaultDetected := True;
end;
- if DebugTrace then
+ if DebugTrace then
AppendToEventLog('Relay.' + self.Name, Format(
- 'FaultDetected=%s',
+ 'FaultDetected=%s',
[BoolToStr(FaultDetected)]
));
MonitoredElement.GetTermVoltages(MonitoredElementTerminal, cvBuffer);
- if td21_i < 1 then
+ if td21_i < 1 then
begin
- if DebugTrace then
+ if DebugTrace then
AppendToEventLog ('Relay.' + self.Name, 'Initialize cqueue');
-
- for i := 1 to td21_pt do
+
+ for i := 1 to td21_pt do
begin
ib := (i - 1) * td21_stride;
- for j := 1 to Nphases do
+ for j := 1 to Nphases do
begin
iv := ib + j;
td21_h^[iv] := cvBuffer^[j];
@@ -1745,25 +1476,25 @@ procedure TRelayObj.TD21Logic;
end;
td21_i := 1;
end;
-
+
td21_next := (td21_i mod td21_pt) + 1; // this points to the oldest sample, and the next write location
-
+
// calculate the differential currents and voltages
ib := (td21_next - 1) * td21_stride;
- for j := 1 to Nphases do
+ for j := 1 to Nphases do
begin
iv := ib + j;
td21_Uref^[j] := td21_h^[iv];
- td21_dV^[j] := csub (cvBuffer^[j], td21_h^[iv]);
+ td21_dV^[j] := cvBuffer^[j] - td21_h^[iv];
ii := ib + Nphases + j;
- td21_dI^[j] := csub (cBuffer^[j+CondOffset], td21_h^[ii]);
+ td21_dI^[j] := cBuffer^[j+CondOffset] - td21_h^[ii];
end;
// do the relay processing
- if ActiveCircuit.Solution.DynaVars.IterationFlag < 1 then
+ if ActiveCircuit.Solution.DynaVars.IterationFlag < 1 then
begin
ib := (td21_i - 1) * td21_stride;
- for j := 1 to Nphases do
+ for j := 1 to Nphases do
begin
iv := ib + j;
td21_h^[iv] := cvBuffer^[j];
@@ -1772,75 +1503,75 @@ procedure TRelayObj.TD21Logic;
end;
td21_i := td21_next;
- if td21_quiet > 0 then
+ if td21_quiet > 0 then
dec(td21_quiet);
end;
- if td21_quiet <= 0 then
+ if td21_quiet <= 0 then
begin // one cycle since we started, or since the last operation
PickedUp := False;
min_distance := 1.0e30;
Ires := cZERO;
- for i := 1 to MonitoredElement.Nphases do
- caccum (Ires, td21_dI^[i]);
+ for i := 1 to MonitoredElement.Nphases do
+ Ires += td21_dI^[i];
- kIres := cmul (Dist_K0, Ires);
- for i := 1 to MonitoredElement.NPhases do
+ kIres := Dist_K0 * Ires;
+ for i := 1 to MonitoredElement.NPhases do
begin
- for j := i to MonitoredElement.NPhases do
+ for j := i to MonitoredElement.NPhases do
begin
- if (i = j) then
+ if (i = j) then
begin
Uref := td21_Uref^[i];
Vloop := td21_dV^[i];
- Iloop := cadd (td21_dI^[i], kIres);
- Zhsd := cmulreal (Dist_Z1, Mground); // not Dist_Z0 because it's included in Dist_K0
- end
- else
+ Iloop := td21_dI^[i] + kIres;
+ Zhsd := Dist_Z1 * Mground; // not Dist_Z0 because it's included in Dist_K0
+ end
+ else
begin
- Uref := csub (td21_Uref^[i], td21_Uref^[j]);
- Vloop := csub (td21_dV^[i], td21_dV^[j]);
- Iloop := csub (td21_dI^[i], td21_dI^[j]);
- Zhsd := cmulreal (Dist_Z1, Mphase);
+ Uref := td21_Uref^[i] - td21_Uref^[j];
+ Vloop := td21_dV^[i] - td21_dV^[j];
+ Iloop := td21_dI^[i] - td21_dI^[j];
+ Zhsd := Dist_Z1 * Mphase;
end;
-
+
i2 := cabs2 (Iloop);
Uref2 := cabs2 (Uref);
- if FaultDetected and (i2 > 0.1) and (Uref2 > 0.1) then
+ if FaultDetected and (i2 > 0.1) and (Uref2 > 0.1) then
begin
- Zdir := cnegate (cdiv (Vloop, Iloop));
+ Zdir := -(Vloop / Iloop);
if DebugTrace then
AppendToEventLog('Relay.' + self.Name, Format(
- 'Zhsd[%d,%d]=%.4f+j%.4f, Zdir=%.4f+j%.4f',
+ 'Zhsd[%d,%d]=%.4f+j%.4f, Zdir=%.4f+j%.4f',
[i, j, Zhsd.re, Zhsd.im, Zdir.re, Zdir.im]
));
- if (Zdir.re > 0.0) and (Zdir.im > 0.0) then
+ if (Zdir.re > 0.0) and (Zdir.im > 0.0) then
begin
- Uhsd := csub (cmul (Zhsd, Iloop), Vloop);
+ Uhsd := Zhsd * Iloop - Vloop;
Uhsd2 := cabs2 (Uhsd);
if DebugTrace then
AppendToEventLog('Relay.' + self.Name, Format(
- ' Uhsd=%.2f, Uref=%.2f',
+ ' Uhsd=%.2f, Uref=%.2f',
[cabs(Uhsd), cabs(Uref)]
));
- if Uhsd2 / Uref2 > 1.0 then
+ if Uhsd2 / Uref2 > 1.0 then
begin // this loop trips
- if not PickedUp then
+ if not PickedUp then
begin
Targets := TStringList.Create();
Targets.Sorted := True;
end;
- if (i = j) then
+ if (i = j) then
Targets.Add(Format('G%d', [i]))
else
Targets.Add(Format('P%d%d', [i, j]));
-
+
fault_distance := 1.0 / sqrt(Uhsd2 / Uref2);
if fault_distance < min_distance then
min_distance := fault_distance;
-
+
PickedUp := True;
end;
end;
@@ -1850,19 +1581,19 @@ procedure TRelayObj.TD21Logic;
if PickedUp then
begin
- if DebugTrace then
+ if DebugTrace then
AppendToEventLog ('Relay.'+Self.Name, 'Picked up');
-
- if ArmedForReset then
+
+ if ArmedForReset then
begin
ActiveCircuit.ControlQueue.Delete(LastEventHandle);
ArmedForReset := FALSE;
if DebugTrace then
AppendToEventLog('Relay.' + self.Name, 'Dropping last event.');
end;
-
- if not ArmedForOpen then
- with ActiveCircuit do
+
+ if not ArmedForOpen then
+ with ActiveCircuit do
begin
RelayTarget := Format ('TD21 %.3f pu dist', [min_distance]);
t_event := Solution.DynaVars.t + Delay_Time + Breaker_time;
@@ -1870,17 +1601,17 @@ procedure TRelayObj.TD21Logic;
RelayTarget := RelayTarget + ' ' + Targets[i];
LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, t_event, CTRL_OPEN, 0, Self);
- if DebugTrace then
+ if DebugTrace then
AppendToEventLog('Relay.' + self.Name, Format('Pushing trip event for %.3f', [t_event]));
-
+
ArmedForOpen := TRUE;
- if OperationCount <= NumReclose then
+ if OperationCount <= NumReclose then
begin
LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, t_event + RecloseIntervals^[OperationCount], CTRL_CLOSE, 0, Self);
- if DebugTrace then
+ if DebugTrace then
AppendToEventLog ('Relay.' + self.Name, Format(
- 'Pushing reclose event for %.3f',
+ 'Pushing reclose event for %.3f',
[t_event + RecloseIntervals^[OperationCount]]
));
ArmedForClose := TRUE;
@@ -1890,17 +1621,17 @@ procedure TRelayObj.TD21Logic;
Targets.Free();
end;
- if not FaultDetected then
+ if not FaultDetected then
begin // not picked up; reset if necessary
if (OperationCount > 1) and (not ArmedForReset) then
begin // this implements the reset, whether picked up or not
ArmedForReset := TRUE;
with ActiveCircuit do
LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, Solution.DynaVars.t + ResetTime, CTRL_RESET, 0, Self);
-
+
if DebugTrace then
AppendToEventLog('Relay.' + self.Name, Format(
- 'Pushing reset event for %.3f',
+ 'Pushing reset event for %.3f',
[ActiveCircuit.Solution.DynaVars.t + ResetTime]
));
end;
@@ -1912,24 +1643,432 @@ procedure TRelayObj.TD21Logic;
ArmedForClose := FALSE;
if DebugTrace then
AppendToEventLog('Relay.' + self.Name, Format (
- 'Dropping out at %.3f',
+ 'Dropping out at %.3f',
[ActiveCircuit.Solution.DynaVars.t]
));
end;
end;
- end; { td21_quiet}
- end; {With MonitoredElement}
+ end; // td21_quiet
+ end; // With MonitoredElement
end;
+procedure TRelayObj.DirectionalOvercurrentLogic();
+var
+ i: Integer;
+ TripTime, TimeTest: Double;
+ Cmag, Cangle: Double;
+begin
+ with MonitoredElement do
+ begin
+ if FPresentState = CTRL_CLOSE then
+ begin
+ TripTime := -1.0;
+
+ MonitoredElement.GetCurrents(cBuffer);
+ MonitoredElement.GetTermVoltages(MonitoredElementTerminal, cvBuffer);
+
+ // Shift angle to cBuffer to be relative to cvBuffer
+ for i := (1 + CondOffset) to (Fnphases + CondOffset) do
+ cBuffer^[i] := PDEGtoCompLeX(Cabs(cBuffer^[i]), CDANG(cBuffer^[i]) - CDANG(cvBuffer^[i - CondOffset]));
+
+ for i := (1 + CondOffset) to (Fnphases + CondOffset) do
+ begin
+ TimeTest := -1.0;
+ Cmag := Cabs(cBuffer^[i]);
+ Cangle := Cdang(cBuffer^[i]);
+
+ if (DOC_TiltAngleLow = 90.0) or (DOC_TiltAngleLow = 270.0) then
+ begin
+ if cBuffer^[i].re <= -1 * DOC_TripSetLow then
+ begin
+ if (DOC_TripSetMag > 0.0) then
+ begin // Circle Specified.
+ if Cmag <= DOC_TripSetMag then
+ begin // Within the Circle
+ if DOC_TripSetHigh > 0.0 then // High Straight-Line Specified.
+ begin
+ if (DOC_TiltAngleHigh = 90.0) or (DOC_TiltAngleHigh = 270.0) then
+ begin
+ if cBuffer^[i].re < -1 * DOC_TripSetHigh then
+ begin // Left-side of High Straight-Line
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end
+ else
+ begin // Right-Side of High Straight-Line
+ if DOC_DelayInner > 0.0 then
+ TimeTest := DOC_DelayInner
+ else
+ if DOC_PhaseCurveInner <> NIL then
+ TimeTest := DOC_TDPhaseInner * DOC_PhaseCurveInner.GetTCCTime(Cmag / DOC_PhaseTripInner)
+ else
+ if DOC_DelayInner = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end
+ else
+ begin
+ if cBuffer^[i].im < Tan(DegToRad(DOC_TiltAngleHigh)) * (cBuffer^[i].re + DOC_TripSetHigh) then
+ begin // Left-side of High Straight-Line
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end
+ else
+ begin // Right-Side of High Straight-Line
+ if DOC_DelayInner > 0.0 then
+ TimeTest := DOC_DelayInner
+ else
+ if DOC_PhaseCurveInner <> NIL then
+ TimeTest := DOC_TDPhaseInner * DOC_PhaseCurveInner.GetTCCTime(Cmag / DOC_PhaseTripInner)
+ else
+ if DOC_DelayInner = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end;
+ end
+ else
+ begin // High Straight-Line Not Specified.
+ if DOC_DelayInner > 0.0 then
+ TimeTest := DOC_DelayInner
+ else
+ if DOC_PhaseCurveInner <> NIL then
+ TimeTest := DOC_TDPhaseInner * DOC_PhaseCurveInner.GetTCCTime(Cmag / DOC_PhaseTripInner)
+ else
+ if DOC_DelayInner = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end
+ else
+ begin // Out of the Circle
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end
+ else
+ begin // Circle not Specified
+ if DOC_TripSetHigh > 0.0 then
+ begin // High Straight-Line Specified.
+ if (DOC_TiltAngleHigh = 90.0) or (DOC_TiltAngleHigh = 270.0) then
+ begin
+ if cBuffer^[i].re < -1 * DOC_TripSetHigh then
+ begin // Left-side of High Straight-Line
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end
+ else
+ begin // Right-Side of High Straight-Line
+ if DOC_DelayInner > 0.0 then
+ TimeTest := DOC_DelayInner
+ else
+ if DOC_PhaseCurveInner <> NIL then
+ TimeTest := DOC_TDPhaseInner * DOC_PhaseCurveInner.GetTCCTime(Cmag / DOC_PhaseTripInner)
+ else
+ if DOC_DelayInner = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end
+ else
+ begin
+ if cBuffer^[i].im < Tan(DegToRad(DOC_TiltAngleHigh)) * (cBuffer^[i].re + DOC_TripSetHigh) then
+ begin // Left-side of High Straight-Line
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end
+ else
+ begin // Right-Side of High Straight-Line
+ if DOC_DelayInner > 0.0 then
+ TimeTest := DOC_DelayInner
+ else
+ if DOC_PhaseCurveInner <> NIL then
+ TimeTest := DOC_TDPhaseInner * DOC_PhaseCurveInner.GetTCCTime(Cmag / DOC_PhaseTripInner)
+ else
+ if DOC_DelayInner = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end;
+ end
+ else
+ begin // High Straight-Line Not Specified.
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end;
+ end;
+ end
+ else
+ begin // 90, 270
+ if cBuffer^[i].im < Tan(DegToRad(DOC_TiltAngleLow)) * (cBuffer^[i].re + DOC_TripSetLow) then
+ begin
+ if DOC_TripSetMag > 0.0 then
+ begin // Circle Specified.
+ if Cmag <= DOC_TripSetMag then
+ begin // Within the Circle
+ if DOC_TripSetHigh > 0.0 then // High Straight-Line Specified.
+ begin
+ if (DOC_TiltAngleHigh = 90.0) or (DOC_TiltAngleHigh = 270.0) then
+ begin
+ if cBuffer^[i].re < -1 * DOC_TripSetHigh then
+ begin // Left-side of High Straight-Line
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end
+ else
+ begin // Right-Side of High Straight-Line
+ if DOC_DelayInner > 0.0 then
+ TimeTest := DOC_DelayInner
+ else
+ if DOC_PhaseCurveInner <> NIL then
+ TimeTest := DOC_TDPhaseInner * DOC_PhaseCurveInner.GetTCCTime(Cmag / DOC_PhaseTripInner)
+ else
+ if DOC_DelayInner = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end
+ else
+ begin
+ if cBuffer^[i].im < Tan(DegToRad(DOC_TiltAngleHigh)) * (cBuffer^[i].re + DOC_TripSetHigh) then
+ begin // Left-side of High Straight-Line
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end
+ else
+ begin // Right-Side of High Straight-Line
+ if DOC_DelayInner > 0.0 then
+ TimeTest := DOC_DelayInner
+ else
+ if DOC_PhaseCurveInner <> NIL then
+ TimeTest := DOC_TDPhaseInner * DOC_PhaseCurveInner.GetTCCTime(Cmag / DOC_PhaseTripInner)
+ else
+ if DOC_DelayInner = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end;
+ end
+ else
+ begin // High Straight-Line Not Specified.
+
+ if DOC_DelayInner > 0.0 then
+ TimeTest := DOC_DelayInner
+ else
+ if DOC_PhaseCurveInner <> NIL then
+ TimeTest := DOC_TDPhaseInner * DOC_PhaseCurveInner.GetTCCTime(Cmag / DOC_PhaseTripInner)
+ else
+ if DOC_DelayInner = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end
+ else
+ begin // Out of the Circle
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end
+ else
+ begin // Circle not Specified
+ if DOC_TripSetHigh > 0.0 then
+ begin // High Straight-Line Specified.
+ if (DOC_TiltAngleHigh = 90.0) or (DOC_TiltAngleHigh = 270.0) then
+ begin
+ if cBuffer^[i].re < -1 * DOC_TripSetHigh then
+ begin // Left-side of High Straight-Line
+
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end
+ else
+ begin // Right-Side of High Straight-Line
+ if DOC_DelayInner > 0.0 then
+ TimeTest := DOC_DelayInner
+ else
+ if DOC_PhaseCurveInner <> NIL then
+ TimeTest := DOC_TDPhaseInner * DOC_PhaseCurveInner.GetTCCTime(Cmag / DOC_PhaseTripInner)
+ else
+ if DOC_DelayInner = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end
+ else
+ begin
+ if cBuffer^[i].im < Tan(DegToRad(DOC_TiltAngleHigh)) * (cBuffer^[i].re + DOC_TripSetHigh) then
+ begin // Left-side of High Straight-Line
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end
+ else
+ begin // Right-Side of High Straight-Line
+ if DOC_DelayInner > 0.0 then
+ TimeTest := DOC_DelayInner
+ else
+ if DOC_PhaseCurveInner <> NIL then
+ TimeTest := DOC_TDPhaseInner * DOC_PhaseCurveInner.GetTCCTime(Cmag / DOC_PhaseTripInner)
+ else
+ if DOC_DelayInner = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end;
+ end
+ else
+ begin // High Straight-Line Not Specified.
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end;
+ end;
+ end
+ else
+ begin
+ // There might be an intersection between Straight Line Low and High depending on their angles.
+ // Straight Line High takes precedence.
+ if DOC_TripSetHigh > 0.0 then
+ begin
+ if (DOC_TiltAngleHigh = 90.0) or (DOC_TiltAngleHigh = 270.0) then
+ begin
+ if cBuffer^[i].re < -1 * DOC_TripSetHigh then
+ begin // Left-side of High Straight-Line
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end
+ end
+ else
+ begin
+ if cBuffer^[i].im < Tan(DegToRad(DOC_TiltAngleHigh)) * (cBuffer^[i].re + DOC_TripSetHigh) then
+ begin // Left-side of High Straight-Line
+ if Delay_Time > 0.0 then
+ TimeTest := Delay_Time
+ else
+ if PhaseCurve <> NIL then
+ TimeTest := TDPhase * PhaseCurve.GetTCCTime(Cmag / PhaseTrip)
+ else
+ if Delay_Time = 0.0 then
+ TimeTest := Delay_Time;
+ end
+ end;
+ end;
+ end;
+ end;
+
+ if (TimeTest >= 0.0) then
+ begin
+ if DebugTrace then
+ AppendToEventLog('Relay.' + Self.Name, Format('Directional Overcurrent - Phase %d Trip: Mag=%.5g, Ang=%.5g, Time=%.5g', [i - CondOffset, Cmag, Cangle, TimeTest]));
+ if TripTime < 0.0 then
+ TripTime := TimeTest
+ else
+ TripTime := Min(TripTime, TimeTest);
+ end;
+ end;
+
+ if TripTime >= 0.0 then
+ begin
+ if not ArmedForOpen then
+ with ActiveCircuit do // Then arm for an open operation
+ begin
+ RelayTarget := 'DOC';
+ LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, Solution.DynaVars.t + TripTime + Breaker_time, CTRL_OPEN, 0, Self);
+ if OperationCount <= NumReclose then
+ LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, Solution.DynaVars.t + TripTime + Breaker_time + RecloseIntervals^[OperationCount], CTRL_CLOSE, 0, Self);
+ ArmedForOpen := TRUE;
+ ArmedForClose := TRUE;
+ end;
+ end
+ else
+ begin
+ if ArmedForOpen then
+ with ActiveCircuit do // If current dropped below pickup, disarm trip and set for reset
+ begin
+ LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, Solution.DynaVars.t + ResetTime, CTRL_RESET, 0, Self);
+ ArmedForOpen := FALSE;
+ ArmedForClose := FALSE;
+ end;
+ end;
+ end; // IF PresentState=CLOSE
+ end; // with MonitoredElement
+end;
procedure TRelayObj.RevPowerLogic;
var
S: Complex;
begin
-
- with MonitoredElement do
+ with MonitoredElement do
begin
- //----MonitoredElement.ActiveTerminalIdx := MonitoredElementTerminal;
+ // MonitoredElement.ActiveTerminalIdx := MonitoredElementTerminal;
S := MonitoredElement.Power[MonitoredElementTerminal];
if S.re < 0.0 then
begin
@@ -1952,11 +2091,10 @@ procedure TRelayObj.RevPowerLogic;
ArmedForOpen := FALSE;
end;
end;
- end; {With MonitoredElement}
+ end;
end;
procedure TRelayObj.VoltageLogic;
-
var
i: Integer;
VMax,
@@ -1965,129 +2103,129 @@ procedure TRelayObj.VoltageLogic;
OVTime,
UVTime,
TripTime: Double;
-
begin
+ if LockedOut then
+ Exit;
- if not LockedOut then
- with MonitoredElement do
+ with MonitoredElement do
+ begin
+ //**** Fix so that fastest trip time applies ****
+ MonitoredElement.GetTermVoltages(MonitoredElementTerminal, cBuffer);
+
+ Vmin := 1.0E50;
+ Vmax := 0.0;
+ for i := 1 to MonitoredElement.NPhases do
begin
- {**** Fix so that fastest trip time applies ****}
- MonitoredElement.GetTermVoltages(MonitoredElementTerminal, cBuffer);
+ Vmag := Cabs(cBuffer^[i]);
+ if Vmag > Vmax then
+ Vmax := Vmag;
+ if Vmag < Vmin then
+ Vmin := Vmag;
+ end;
- Vmin := 1.0E50;
- Vmax := 0.0;
- for i := 1 to MonitoredElement.NPhases do
+ // Convert to Per Unit
+ Vmax := Vmax / Vbase;
+ Vmin := Vmin / Vbase;
+
+ if FPresentState = CTRL_CLOSE then
+ begin
+ TripTime := -1.0;
+ OVTime := -1.0;
+ UVTime := -1.0;
+
+
+ // Check OverVoltage Trip, if any
+ if OVCurve <> NIL then
+ OVTime := OVCurve.GetOVtime(Vmax);
+
+ if OVTime > 0.0 then
begin
- Vmag := Cabs(cBuffer^[i]);
- if Vmag > Vmax then
- Vmax := Vmag;
- if Vmag < Vmin then
- Vmin := Vmag;
+ TripTime := OVTime;
end;
- {Convert to Per Unit}
- Vmax := Vmax / Vbase;
- Vmin := Vmin / Vbase;
+ // If OVTime > 0 then we have a OV trip
- if FPresentState = CTRL_CLOSE then
+ // Check UV Trip, if any
+ if UVCurve <> NIL then
begin
- TripTime := -1.0;
- OVTime := -1.0;
- UVTime := -1.0;
-
+ UVTime := UVCurve.GetUVtime(Vmin);
+ end;
- {Check OverVoltage Trip, if any}
- if OVCurve <> NIL then
- OVTime := OVCurve.GetOVtime(Vmax);
+ // If UVTime > 0 then we have a UV trip
- if OVTime > 0.0 then
+ if UVTime > 0.0 then
+ begin
+ if TripTime > 0.0 then
begin
- TripTime := OVTime;
- end;
-
- // If OVTime > 0 then we have a OV trip
-
- {Check UV Trip, if any}
- if UVCurve <> NIL then
+ TripTime := Min(TripTime, UVTime) // Min of UV or OV time
+ end
+ else
begin
- UVTime := UVCurve.GetUVtime(Vmin);
+ TripTime := UVTime;
end;
+ end;
- // If UVTime > 0 then we have a UV trip
-
- if UVTime > 0.0 then
+ if TripTime > 0.0 then
+ with ActiveCircuit do
begin
- if TripTime > 0.0 then
+ if ArmedForOpen and ((Solution.DynaVars.t + TripTime + Breaker_time) < NextTripTime) then
begin
- TripTime := Min(TripTime, UVTime) // Min of UV or OV time
- end
- else
- begin
- TripTime := UVTime;
+ ControlQueue.Delete(LastEventHandle); // Delete last event from Queue
+ ArmedForOpen := FALSE; // force it to go through next IF
end;
- end;
-
- if TripTime > 0.0 then
- with ActiveCircuit do
- begin
- if ArmedForOpen and ((Solution.DynaVars.t + TripTime + Breaker_time) < NextTripTime) then
+ if not ArmedForOpen then
+ begin // Then arm for an open operation
+ if TripTime = UVTime then
begin
- ControlQueue.Delete(LastEventHandle); // Delete last event from Queue
- ArmedForOpen := FALSE; // force it to go through next IF
- end;
-
- if not ArmedForOpen then
- begin // Then arm for an open operation
- if TripTime = UVTime then
- begin
- if TripTime = OVTime then
- RelayTarget := 'UV + OV'
- else
- RelayTarget := 'UV';
- end
+ if TripTime = OVTime then
+ RelayTarget := 'UV + OV'
else
- Relaytarget := 'OV';
+ RelayTarget := 'UV';
+ end
+ else
+ Relaytarget := 'OV';
- NextTripTime := Solution.DynaVars.t + TripTime + Breaker_time;
- LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, NextTripTime, CTRL_OPEN, 0, Self);
- ArmedforOpen := TRUE;
- end;
- end
- else
+ NextTripTime := Solution.DynaVars.t + TripTime + Breaker_time;
+ LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, NextTripTime, CTRL_OPEN, 0, Self);
+ ArmedforOpen := TRUE;
+ end;
+ end
+ else
+ begin
+ if ArmedForOpen then
+ with ActiveCircuit do // If voltage dropped below pickup, disarm trip and set for reset
+ begin
+ ControlQueue.Delete(LastEventHandle); // Delete last event from Queue
+ NextTripTime := -1.0;
+ LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, Solution.DynaVars.t + ResetTime, CTRL_RESET, 0, Self);
+ ArmedForOpen := FALSE;
+ end;
+ end;
+ end // IF PresentState=CLOSE
+ else
+ begin
+ // Present state is Open, Check for Voltage and then set reclose Interval
+ if (OperationCount <= NumReclose) then
+ if not ArmedForClose then
begin
- if ArmedForOpen then
- with ActiveCircuit do // If voltage dropped below pickup, disarm trip and set for reset
+ if (Vmax > 0.9) then
+ with ActiveCircuit do // OK if voltage > 90%
begin
- ControlQueue.Delete(LastEventHandle); // Delete last event from Queue
- NextTripTime := -1.0;
- LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, Solution.DynaVars.t + ResetTime, CTRL_RESET, 0, Self);
- ArmedForOpen := FALSE;
+ LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, Solution.DynaVars.t + RecloseIntervals^[OperationCount], CTRL_CLOSE, 0, Self);
+ ArmedForClose := TRUE;
end;
- end;
- end {IF PresentState=CLOSE}
- else
- begin {Present state is Open, Check for Voltage and then set reclose Interval}
- if (OperationCount <= NumReclose) then
- if not ArmedForClose then
- begin
- if (Vmax > 0.9) then
- with ActiveCircuit do // OK if voltage > 90%
- begin
- LastEventHandle := ControlQueue.Push(Solution.DynaVars.intHour, Solution.DynaVars.t + RecloseIntervals^[OperationCount], CTRL_CLOSE, 0, Self);
- ArmedForClose := TRUE;
- end;
- end
- else {Armed, but check to see if voltage dropped before it reclosed and cancel action}
- if Vmax < 0.9 then
- ArmedForClose := FALSE;
+ end
+ else // Armed, but check to see if voltage dropped before it reclosed and cancel action
+ if Vmax < 0.9 then
+ ArmedForClose := FALSE;
- end;
- end; {With MonitoredElement}
+ end;
+ end; // With MonitoredElement
end;
procedure TRelayObj.NegSeq47Logic;
-{Neg Seq voltage Relay}
+// Neg Seq voltage Relay
var
NegSeqVoltageMag: Double;
V012: array[1..3] of Complex;
@@ -2109,7 +2247,7 @@ procedure TRelayObj.NegSeq47Logic;
end
end
else
- begin {Less Than pickup value: reset if armed}
+ begin // Less Than pickup value: reset if armed
if ArmedForOpen then // We became unarmed, so reset and disarm
with ActiveCircuit do
begin
@@ -2117,9 +2255,10 @@ procedure TRelayObj.NegSeq47Logic;
ArmedForOpen := FALSE;
end;
end;
- end; {With MonitoredElement}
+ end;
end;
-initialization
-
+finalization ActionEnum.Free;
+ StateEnum.Free;
+ RelayTypeEnum.Free;
end.
diff --git a/src/Controls/RollAvgWindow.pas b/src/Controls/RollAvgWindow.pas
new file mode 100644
index 000000000..0c931a176
--- /dev/null
+++ b/src/Controls/RollAvgWindow.pas
@@ -0,0 +1,106 @@
+unit RollAvgWindow;
+
+interface
+{$PUSH}
+uses
+ gqueue;
+
+type
+ TRollAvgWindow = class(TObject)
+ PRIVATE
+ bufferlength: Integer;
+ sample: TQueue;
+ sampletime: TQueue;
+ runningsumsample: Double;
+ runningsumsampletime: Double;
+ bufferfull: Boolean;
+
+ PUBLIC
+ constructor Create();
+ destructor Destroy; OVERRIDE;
+ procedure Add(IncomingSampleValue: Double; IncomingSampleTime: Double; VAvgWindowLengthSec: Double);
+ procedure SetLength(const Value: Integer);
+ function AvgVal: Double;
+ function AccumSec: Double;
+ end;
+{$POP}
+
+implementation
+
+procedure TRollAvgWindow.Add(IncomingSampleValue: Double; IncomingSampleTime: Double; VAvgWindowLengthSec: Double);
+begin
+ if (sample.size > 0) and (bufferfull) then
+ begin
+ runningsumsample := runningsumsample - sample.front;
+ if (bufferlength = 0) then
+ begin
+ IncomingSampleValue := 0.0;
+ end;
+ sample.pop;sample.push(IncomingSampleValue);
+ sampletime.pop;sampletime.push(IncomingSampleTime);
+
+ runningsumsample := runningsumsample + IncomingSampleValue;
+ runningsumsampletime := runningsumsampletime - sampletime.front;
+ runningsumsampletime := runningsumsampletime + IncomingSampleTime;
+ end
+ else
+ begin
+ if (bufferlength = 0) then
+ begin
+ IncomingSampleValue := 0.0;
+ end;
+
+ sample.push(IncomingSampleValue);
+ sampletime.push(IncomingSampleTime);
+
+ runningsumsample := runningsumsample + IncomingSampleValue;
+ runningsumsampletime := runningsumsampletime + IncomingSampleTime;
+
+ if (runningsumsampletime > VAvgWindowLengthSec) then
+ bufferfull := TRUE;
+ if (sample.size = bufferlength) then
+ bufferfull := TRUE;
+ end;
+end;
+
+constructor TRollAvgWindow.Create();
+begin
+ sample := TQueue.Create();
+ sampletime := TQueue.Create();
+
+ runningsumsample := 0.0;
+ runningsumsampletime := 0.0;
+ bufferlength := 0;
+ bufferfull := FALSE;
+end;
+
+destructor TRollAvgWindow.Destroy;
+begin
+ sample := NIL;
+ sampletime := NIL;
+
+ inherited;
+end;
+
+procedure TRollAvgWindow.SetLength(const Value: Integer);
+begin
+ bufferlength := Value;
+end;
+
+function TRollAvgWindow.AvgVal: Double;
+begin
+ if (sample.size = 0) then
+ Result := 0.0
+ else
+ Result := runningsumsample / sample.size;
+end;
+
+function TRollAvgWindow.AccumSec: Double;
+begin
+ if (sample.size = 0) then
+ Result := 0.0
+ else
+ Result := runningsumsampletime;
+end;
+
+end.
\ No newline at end of file
diff --git a/src/Controls/StorageController.pas b/src/Controls/StorageController.pas
index 25b848f92..7ea318194 100644
--- a/src/Controls/StorageController.pas
+++ b/src/Controls/StorageController.pas
@@ -6,21 +6,19 @@
All rights reserved.
----------------------------------------------------------
}
-{
- A StorageController is a control element that is connected to a terminal of another
- circuit element and sends dispatch signals to a fleet of energy storage elements it controls
-
- A StorageController is defined by a New command:
-
- New StorageController.Name=myname Element=devclass.name terminal=[ 1|2|...] Elementlist = (elem1 elem2 ...)
- or ... ElementList = [File=filename] where storage class elements are listed one to a line
- If omitted, all storage elements found in the active circuit are included by default and controlled as a fleet.
-
- Added new control mode for charging 12/19/2018
- Proposed by Valentin Rigoni
-
-}
+// A StorageController is a control element that is connected to a terminal of another
+// circuit element and sends dispatch signals to a fleet of energy storage elements it controls
+//
+// A StorageController is defined by a New command:
+//
+// New StorageController.Name=myname Element=devclass.name terminal=[ 1|2|...] Elementlist = (elem1 elem2 ...)
+//
+// or ... ElementList = [File=filename] where storage class elements are listed one to a line
+// If omitted, all storage elements found in the active circuit are included by default and controlled as a fleet.
+//
+// Added new control mode for charging 12/19/2018
+// Proposed by Valentin Rigoni
interface
@@ -31,31 +29,66 @@ interface
CktElement,
DSSClass,
Arraydef,
- ucomplex,
+ UComplex, DSSUcomplex,
utilities,
DSSPointerList,
Classes,
Loadshape;
type
+{$SCOPEDENUMS ON}
+ TStorageControllerProp = (
+ INVALID = 0,
+ Element = 1,
+ Terminal = 2,
+ kWTarget = 3,
+ kWTargetLow = 4,
+ pctkWBand = 5,
+ pctkWBandLow = 6,
+ PFTarget = 7,
+ PFBand = 8,
+ ElementList = 9,
+ Weights = 10,
+ ModeDischarge = 11,
+ ModeCharge = 12,
+ TimeDischargeTrigger = 13,
+ TimeChargeTrigger = 14,
+ pctRatekW = 15,
+ pctRatekvar = 16,
+ pctRateCharge = 17,
+ pctReserve = 18,
+ kWhTotal = 19,
+ kWTotal = 20,
+ kWhActual = 21,
+ kWActual = 22,
+ kWneed = 23,
+ pctParticipation = 24, // TODO: unused, remove?
+ Yearly = 25,
+ Daily = 26,
+ Duty = 27,
+ EventLog = 28,
+ VarDispatch = 29,
+ InhibitTime = 30,
+ Tup = 31,
+ TFlat = 32,
+ Tdn = 33,
+ kWThreshold = 34,
+ ResetLevel = 35,
+ Seasons = 36,
+ SeasonTargets = 37,
+ SeasonTargetsLow = 38
+ );
+{$SCOPEDENUMS OFF}
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TStorageController = class(TControlClass)
- PRIVATE
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const StorageControllerName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
-
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TStorageControllerObj = class(TControlElem)
PRIVATE
FkWTarget,
@@ -80,12 +113,14 @@ TStorageControllerObj = class(TControlElem)
pctkvarRate,
pctChargeRate,
LastpctDischargeRate,
- TotalkWCapacity,
- TotalkWhCapacity,
+ //TotalkWCapacity,
+ //TotalkWhCapacity,
pctFleetReserve,
ResetLevel,
kWNeeded: Double;
+ ParticipationStr: String;
+
FStorageNameList: TStringList;
FleetPointerList: TDSSPointerList;
SeasonTargets,
@@ -94,12 +129,12 @@ TStorageControllerObj = class(TControlElem)
FleetListChanged,
ChargingAllowed,
- DispatchVars,
DischargeTriggeredByTime,
DischargeInhibited,
OutOfOomph,
FElementListSpecified,
Wait4Step: Boolean;
+ DispatchVars: LongBool;
Seasons,
FleetSize,
@@ -108,11 +143,8 @@ TStorageControllerObj = class(TControlElem)
InhibitHrs,
ChargeMode: Integer;
- YearlyShape: String; // ='fixed' means no variation on all the time
YearlyShapeObj: TLoadShapeObj; // Shape for this Storage element
- DailyShape: String; // Daily (24 HR) Storage element shape
DailyShapeObj: TLoadShapeObj; // Daily Storage element Shape for this load
- DutyShape: String; // Duty cycle load shape for changes typically less than one hour
DutyShapeObj: TLoadShapeObj; // Shape for this Storage element
LoadShapeMult: Complex;
@@ -126,21 +158,11 @@ TStorageControllerObj = class(TControlElem)
procedure SetFleetToDisCharge;
procedure SetFleetToIdle;
procedure SetFleetToExternal;
- function InterpretMode(Opt: Integer; const S: String): Integer;
- function GetModeString(Opt, Mode: Integer): String;
- function GetkWTotal(var Sum: Double): String;
- function GetkWhTotal(var Sum: Double): String;
- function GetkWhActual: String;
- function GetkWActual: String;
procedure CalcYearlyMult(Hr: Double);
procedure CalcDailyMult(Hr: Double);
procedure CalcDutyMult(Hr: Double);
- function ReturnSeasonTarget(THigh: Integer): String;
- function ReturnElementsList: String;
- function ReturnWeightsList: String;
-
function MakeFleetList: Boolean;
procedure DoLoadFollowMode;
procedure DoLoadShapeMode;
@@ -161,33 +183,24 @@ TStorageControllerObj = class(TControlElem)
constructor Create(ParClass: TDSSClass; const StorageControllerName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
procedure RecalcElementData; OVERRIDE;
- procedure CalcYPrim; OVERRIDE; // Always Zero for a StorageController
procedure Sample; OVERRIDE; // Sample control quantities and set action times in Control Queue
procedure DoPendingAction(const Code, ProxyHdl: Integer); OVERRIDE; // Do the action that is pending from last sample
procedure Reset; OVERRIDE; // Reset to initial defined state
-
- procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present value of terminal Curr
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
-
+
property PFBand: Double READ FPFBand WRITE Set_PFBand;
- property FleetkW: Double READ Get_FleetkW;
- property FleetkWh: Double READ Get_FleetkWh;
property FleetkWhRating: Double READ Get_FleetkWhRating;
property FleetReservekWh: Double READ Get_FleetReservekWh;
-
end;
-{--------------------------------------------------------------------------}
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Circuit,
@@ -203,51 +216,13 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TStorageControllerObj;
+ TProp = TStorageControllerProp;
const
-
- propELEMENT = 1;
- propTERMINAL = 2;
- propKWTARGET = 3;
- propKWTARGETLOW = 4;
- propKWBAND = 5;
- propKWBANDLOW = 6;
- propPFTARGET = 7;
- propPFBAND = 8;
- propELEMENTLIST = 9;
- propWEIGHTS = 10;
- propMODEDISCHARGE = 11;
- propMODECHARGE = 12;
- propTIMEDISCHARGETRIGGER = 13;
- propTIMECHARGETRIGGER = 14;
- propRATEKW = 15;
- propRATEKVAR = 16;
- propRATECHARGE = 17;
- propRESERVE = 18;
- propKWHTOTAL = 19;
- propKWTOTAL = 20;
- propKWHACTUAL = 21;
- propKWACTUAL = 22;
- propKWNEED = 23;
- propPARTICIPATION = 24;
- propYEARLY = 25;
- propDAILY = 26;
- propDUTY = 27;
- propEVENTLOG = 28;
- propVARDISPATCH = 29;
- propINHIBITTIME = 30;
- propTUPRAMP = 31;
- propTFLAT = 32;
- propTDNRAMP = 33;
- propKWTHRESHOLD = 34;
- propRESETLEVEL = 35;
- propSEASONS = 36;
- propSEASONTARGETS = 37;
- propSEASONTARGETSLOW = 38;
-
- NumPropsThisClass = 38;
+ NumPropsThisClass = Ord(High(TProp));
//= = = = = = = = = = = = = = DEFINE CONTROL MODE CONSTANTS = = = = = = = = = = = = = = = = = = = = = = = = =
-
MODEFOLLOW = 1;
MODELOADSHAPE = 2;
MODESUPPORT = 3;
@@ -260,545 +235,355 @@ implementation
//= = = = = = = = = = = = = = DEFINE OTHER CONSTANTS = = = = = = = = = = = = = = = = = = = = = = = = =
RELEASE_INHIBIT = 999;
-
var
- CDoubleOne: Complex;
+ PropInfo: Pointer = NIL;
+ ChargeModeEnum, DischargeModeEnum: TDSSEnum;
-{--------------------------------------------------------------------------}
-constructor TStorageController.Create(dssContext: TDSSContext); // Creates superstructure for all StorageController objects
+function ConvertPFToPFRange2(const value: Double): Double;
+// Convert PF from +/- 1 to 0..2 Where 1..2 is leading
begin
- inherited Create(dssContext);
-
- Class_name := 'StorageController';
- DSSClassType := DSSClassType + STORAGE_CONTROL;
+ if value < 0.0 then
+ Result := 2.0 + Value
+ else
+ Result := Value;
+end;
- DefineProperties;
+function ConvertPFRange2ToPF(const value: Double): Double;
+begin
+ if value > 1.0 then
+ Result := value - 2.0
+ else
+ Result := Value;
+end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+constructor TStorageController.Create(dssContext: TDSSContext);
+begin
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ DischargeModeEnum := TDSSEnum.Create('LegacyStorageController: Discharge mode', False, 1, 2,
+ ['Peakshave', 'Follow', 'Support', 'Loadshape', 'Time', 'Schedule', 'I-Peakshave'],
+ [MODEPEAKSHAVE, MODEFOLLOW, MODESUPPORT, MODELOADSHAPE, MODETIME, MODESCHEDULE, CURRENTPEAKSHAVE]);
+ ChargeModeEnum := TDSSEnum.Create('LegacyStorageController: Charge mode', False, 1, 1,
+ ['Loadshape', 'Time', 'PeakshaveLow', 'I-PeakshaveLow'],
+ [MODELOADSHAPE, MODETIME, MODEPEAKSHAVELOW, CURRENTPEAKSHAVELOW]);
+ end;
+ inherited Create(dssContext, STORAGE_CONTROL, 'StorageController');
end;
-{--------------------------------------------------------------------------}
destructor TStorageController.Destroy;
-
begin
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TStorageController.DefineProperties;
+function GetkWhTotal(obj: TObj): Double;
+var
+ pStorage: TStorageObj;
+ i: Integer;
begin
+ Result := 0.0;
+ with obj do
+ for i := 1 to FleetPointerList.Count do
+ begin
+ pStorage := FleetPointerList.Get(i);
+ Result := Result + pStorage.StorageVars.kWhRating;
+ end;
+end;
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
-
- PropertyName[propELEMENT] := 'Element';
- PropertyName[propTERMINAL] := 'Terminal';
- PropertyName[propKWTARGET] := 'kWTarget';
- PropertyName[propKWTARGETLOW] := 'kWTargetLow';
- PropertyName[propKWBAND] := '%kWBand';
- PropertyName[propKWBANDLOW] := '%kWBandLow';
- PropertyName[propPFTARGET] := 'PFTarget';
- PropertyName[propPFBAND] := 'PFBand';
- PropertyName[propELEMENTLIST] := 'ElementList';
- PropertyName[propWEIGHTS] := 'Weights';
- PropertyName[propMODEDISCHARGE] := 'ModeDischarge';
- PropertyName[propMODECHARGE] := 'ModeCharge';
- PropertyName[propTIMEDISCHARGETRIGGER] := 'TimeDischargeTrigger';
- PropertyName[propTIMECHARGETRIGGER] := 'TimeChargeTrigger';
- PropertyName[propRATEKW] := '%RatekW';
- PropertyName[propRATEKVAR] := '%Ratekvar';
- PropertyName[propRATECHARGE] := '%RateCharge';
- PropertyName[propRESERVE] := '%Reserve';
- PropertyName[propKWHTOTAL] := 'kWhTotal';
- PropertyName[propKWTOTAL] := 'kWTotal';
- PropertyName[propKWHACTUAL] := 'kWhActual';
- PropertyName[propKWACTUAL] := 'kWActual';
- PropertyName[propKWNEED] := 'kWneed';
- PropertyName[propPARTICIPATION] := '%Participation';
- PropertyName[propYEARLY] := 'Yearly';
- PropertyName[propDAILY] := 'Daily';
- PropertyName[propDUTY] := 'Duty';
- PropertyName[propEVENTLOG] := 'EventLog';
- PropertyName[propVARDISPATCH] := 'VarDispatch';
- PropertyName[propINHIBITTIME] := 'InhibitTime';
- PropertyName[propTUPRAMP] := 'Tup';
- PropertyName[propTFLAT] := 'TFlat';
- PropertyName[propTDNRAMP] := 'Tdn';
- PropertyName[propKWTHRESHOLD] := 'kWThreshold';
- PropertyName[propRESETLEVEL] := 'ResetLevel';
- PropertyName[propSEASONS] := 'Seasons';
- PropertyName[propSEASONTARGETS] := 'SeasonTargets';
- PropertyName[propSEASONTARGETSLOW] := 'SeasonTargetsLow';
-
-
- PropertyHelp[propELEMENT] :=
- 'Full object name of the circuit element, typically a line or transformer, ' +
- 'which the control is monitoring. There is no default; must be specified.';
- PropertyHelp[propTERMINAL] :=
- 'Number of the terminal of the circuit element to which the StorageController control is connected. ' +
- '1 or 2, typically. Default is 1. Make sure you have the direction on the power matching the sign of kWLimit.';
- PropertyHelp[propKWTARGET] :=
- 'kW/kamps target for Discharging. The storage element fleet is dispatched to try to hold the power/current in band ' +
- 'at least until the storage is depleted. The selection of power or current depends on the Discharge mode (PeakShave->kW, I-PeakShave->kamps).';
- PropertyHelp[propKWTARGETLOW] :=
- 'kW/kamps target for Charging. The storage element fleet is dispatched to try to hold the power/current in band ' +
- 'at least until the storage is fully charged. The selection of power or current depends on the charge mode (PeakShavelow->kW, I-PeakShavelow->kamps).';
- PropertyHelp[propKWBAND] :=
- 'Bandwidth (% of Target kW/kamps) of the dead band around the kW/kamps target value. Default is 2% (+/-1%).' +
- 'No dispatch changes are attempted If the power in the monitored terminal stays within this band.';
- PropertyHelp[propKWBANDLOW] :=
- 'Bandwidth (% of TargetkWLow) of the dead band around the kWtargetLow value. Default is 2% (+/-1%).' +
- 'No charging is attempted if the power in the monitored terminal stays within this band.';
- PropertyHelp[propPFTARGET] :=
- 'Power Factor target for dispatching the reactive power. Default is 0.96. The reactive power of the storage element fleet is dispatched to try to hold the power factor in band. ' +
- 'It is assumed that the storage element inverter can produce kvar up to its kVA limit regardless of storage level.';
- PropertyHelp[propPFBAND] :=
- 'Bandwidth of the Target power factor of the monitored element. of the dead band around the kvar target value. Default is 0.04 (+/- 0.02).' +
- 'No dispatch changes of the kvar are attempted If the power factor of the monitored terminal stays within this band.';
- PropertyHelp[propELEMENTLIST] :=
- 'Array list of Storage elements to be controlled. If not specified, all storage elements in the circuit not presently dispatched by another controller ' +
- 'are assumed dispatched by this controller.';
- PropertyHelp[propWEIGHTS] :=
- 'Array of proportional weights corresponding to each storage element in the ElementList. ' +
- 'The needed kW or kvar to get back to center band is dispatched to each storage element according to these weights. ' +
- 'Default is to set all weights to 1.0.';
- PropertyHelp[propMODEDISCHARGE] :=
- '{PeakShave* | Follow | Support | Loadshape | Time | Schedule | I-PeakShave} Mode of operation for the DISCHARGE FUNCTION of this controller. ' +
- CRLF + CRLF + 'In PeakShave mode (Default), the control attempts to discharge storage to keep power in the monitored element below the kWTarget. ' +
- CRLF + CRLF + 'In Follow mode, the control is triggered by time and resets the kWTarget value to the present monitored element power. ' +
- 'It then attempts to discharge storage to keep power in the monitored element below the new kWTarget. See TimeDischargeTrigger.' +
- CRLF + CRLF + 'In Suport mode, the control operates oppositely of PeakShave mode: storage is discharged to keep kW power output up near the target. ' +
- CRLF + CRLF + 'In Loadshape mode, both charging and discharging precisely follows the per unit loadshape. ' +
- 'Storage is discharged when the loadshape value is positive. ' +
- CRLF + CRLF + 'In Time mode, the storage discharge is turned on at the specified %RatekW and %Ratekvar at the specified discharge trigger time in fractional hours.' +
- CRLF + CRLF + 'In Schedule mode, the Tup, TFlat, and Tdn properties specify the up ramp duration, flat duration, and down ramp duration for the schedule. ' +
- 'The schedule start time is set by TimeDischargeTrigger and the rate of discharge for the flat part is determined by RatekW.' +
- CRLF + CRLF + 'In I-PeakShave mode, the control attempts to discharge storage to keep current in the monitored element below the target given in k-amps ' +
- '(thousands of amps), when this control mode is active, the property kWTarget will be expressed in k-amps. ';
- PropertyHelp[propMODECHARGE] :=
- '{Loadshape | Time* | PeakShaveLow | I-PeakShaveLow} Mode of operation for the CHARGE FUNCTION of this controller. ' +
- CRLF + CRLF + 'In Loadshape mode, both charging and discharging precisely follows the per unit loadshape. ' +
- 'Storage is charged when the loadshape value is negative. ' +
- CRLF + CRLF + 'In Time mode, the storage charging FUNCTION is triggered at the specified %RateCharge at the specified sharge trigger time in fractional hours.' +
- CRLF + CRLF + 'In PeakShaveLow mode, the charging operation will charge the storage fleet when the power at a' +
- 'monitored element is bellow a specified KW target (kWTarget_low). The storage will charge as much power as necessary to keep the power within the deadband around kWTarget_low.' +
- CRLF + CRLF + 'In I-PeakShaveLow mode, the charging operation will charge the storage fleet when the current (Amps) at a' +
- 'monitored element is below a specified amps target (kWTarget_low). The storage will charge as much power as necessary to keep the amps within the deadband around kWTarget_low. ' +
- 'When this control mode is active, the property kWTarget_low will be expressed in k-amps and all the other parameters will be adjusted to match the amps (current) control criteria.';
- PropertyHelp[propTIMEDISCHARGETRIGGER] :=
- 'Default time of day (hr) for initiating Discharging of the fleet. During Follow or Time mode discharging is triggered at a fixed time ' +
- 'each day at this hour. If Follow mode, storage will be discharged to attempt to hold the load at or below the power level at the time of triggering. ' +
- 'In Time mode, the discharge is based on the %RatekW property value. ' +
- 'Set this to a negative value to ignore. Default is 12.0 for Follow mode; otherwise it is -1 (ignored). ';
- PropertyHelp[propTIMECHARGETRIGGER] :=
- 'Default time of day (hr) for initiating charging in Time control mode. Set this to a negative value to ignore. Default is 2.0. (0200).' +
- 'When this value is >0 the storage fleet is set to charging at this time regardless of other control criteria to make sure storage is ' +
- 'topped off for the next discharge cycle.';
- PropertyHelp[propRATEKW] :=
- 'Sets the kW discharge rate in % of rated capacity for each element of the fleet. Applies to TIME control mode, SCHEDULE mode, or anytime discharging is triggered ' +
- 'by time.';
- PropertyHelp[propRATEKVAR] :=
- 'Sets the kvar discharge rate in % of rated capacity for each element of the fleet. Applies to TIME control mode or anytime discharging is triggered ' +
- 'by time.';
- PropertyHelp[propRATECHARGE] :=
- 'Sets the kW charging rate in % of rated capacity for each element of the fleet. Applies to TIME control mode and anytime charging mode is ' +
- 'entered due to a time trigger.';
- PropertyHelp[propRESERVE] :=
- 'Use this property to change the % reserve for each storage element under control of this controller. This might be used, for example, to ' +
- 'allow deeper discharges of storage or in case of emergency operation to use the remainder of the storage element.';
- PropertyHelp[propKWHTOTAL] :=
- '(Read only). Total rated kWh energy storage capacity of storage elements controlled by this controller.';
- PropertyHelp[propKWTOTAL] :=
- '(Read only). Total rated kW power capacity of storage elements controlled by this controller.';
- PropertyHelp[propKWHACTUAL] :=
- '(Read only). Actual kWh output of all controlled storage elements. ';
- PropertyHelp[propKWACTUAL] :=
- '(Read only). Actual kW output of all controlled storage elements. ';
- PropertyHelp[propKWNEED] :=
- '(Read only). KW needed to meet target.';
- PropertyHelp[propPARTICIPATION] :=
- 'Participation factor, %. Default = 100.';
- PropertyHelp[propYEARLY] :=
- 'Dispatch loadshape object, If any, for Yearly solution Mode.';
- PropertyHelp[propDAILY] :=
- 'Dispatch loadshape object, If any, for Daily solution mode.';
- PropertyHelp[propDUTY] :=
- 'Dispatch loadshape object, If any, for Dutycycle solution mode.';
- PropertyHelp[propEVENTLOG] :=
- '{Yes/True | No/False} Default is No. Log control actions to Eventlog.';
- PropertyHelp[propVARDISPATCH] :=
- '{Yes/True | No/False} Default is No. Flag to indicate whether or not to disatch vars as well as watts.';
- PropertyHelp[propINHIBITTIME] :=
- 'Hours (integer) to inhibit Discharging after going into Charge mode. Default is 5';
- PropertyHelp[propTUPRAMP] := 'Duration, hrs, of upramp part for SCHEDULE mode. Default is 0.25.';
- PropertyHelp[propTFLAT] := 'Duration, hrs, of flat part for SCHEDULE mode. Default is 2.0.';
- PropertyHelp[propTDNRAMP] := 'Duration, hrs, of downramp part for SCHEDULE mode. Default is 0.25.';
- PropertyHelp[propKWTHRESHOLD] := 'Threshold, kW, for Follow mode. kW has to be above this value for the Storage element ' +
- 'to be dispatched on. Defaults to 75% of the kWTarget value. Must reset this property after ' +
- 'setting kWTarget if you want a different value.';
- PropertyHelp[propRESETLEVEL] := 'The level of charge required for allowing the storage to discharge again after reaching ' +
- 'the reserve storage level. After reaching this level, the storage control will not allow ' +
- 'the storage device to discharge, forcing the storage to charge. Once the storage reaches this' +
- 'level, the storage will be able to discharge again. This value is a number between 0.2 and 1';
- PropertyHelp[propSEASONS] := 'With this property the user can' +
- ' specify the number of targets to be used by the controller using the list given at "SeasonTargets"/' +
- '"SeasonTargetsLow", which can be used to dynamically adjust the storage controller during a QSTS' +
- ' simulation. The default value is 1. This property needs to be defined before defining SeasonTargets/SeasonTargetsLow.';
- PropertyHelp[propSEASONTARGETS] := 'An array of doubles specifying the targets to be used during a QSTS simulation. These targets will take effect' +
- ' only if SeasonRating=true. The number of targets cannot exceed the number of seasons defined at the SeasonSignal.' +
- 'The difference between the targets defined at SeasonTargets and SeasonTargetsLow is that SeasonTargets' +
- ' applies to discharging modes, while SeasonTargetsLow applies to charging modes.';
- PropertyHelp[propSEASONTARGETSLOW] := 'An array of doubles specifying the targets to be used during a QSTS simulation. These targets will take effect' +
- ' only if SeasonRating=true. The number of targets cannot exceed the number of seasons defined at the SeasonSignal.' +
- 'The difference between the targets defined at SeasonTargets and SeasonTargetsLow is that SeasonTargets' +
- ' applies to discharging modes, while SeasonTargetsLow applies to charging modes.';
-
+function GetkWTotal(obj: TObj): Double;
+var
+ pStorage: TStorageObj;
+ i: Integer;
+begin
+ Result := 0.0;
+ with obj do
+ for i := 1 to FleetPointerList.Count do
+ begin
+ pStorage := FleetPointerList.Get(i);
+ Result := Result + pStorage.StorageVars.kWRating;
+ end;
+end;
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
+function GetFleetkW(obj: TObj): Double;
+begin
+ Result := obj.Get_FleetkW();
+end;
+function GetFleetkWh(obj: TObj): Double;
+begin
+ Result := obj.Get_FleetkWh();
end;
-{--------------------------------------------------------------------------}
-function TStorageController.NewObject(const ObjName: String): Integer;
+function GetPctkvarRate(obj: TObj): Double;
begin
- // Make a new StorageController and add it to StorageController class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TStorageControllerObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Result := ConvertPFRange2ToPF(Obj.FPFTarget);
end;
-{--------------------------------------------------------------------------}
-function TStorageController.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
- i: Integer;
- casemult: Double;
+procedure SetPctkvarRate(obj: TObj; Value: Double);
+begin
+ Obj.FPFTarget := ConvertPFToPFRange2(Value);
+end;
+procedure TStorageController.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
+ Numproperties := NumPropsThisClass;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enum properties
+ PropertyType[ord(TProp.ModeDischarge)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.ModeDischarge)] := ptruint(@obj.DischargeMode);
+ PropertyOffset2[ord(TProp.ModeDischarge)] := PtrInt(DischargeModeEnum);
+
+ PropertyType[ord(TProp.ModeCharge)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.ModeCharge)] := ptruint(@obj.ChargeMode);
+ PropertyOffset2[ord(TProp.ModeCharge)] := PtrInt(ChargeModeEnum);
+
+ // double functions
+ PropertyFlags[ord(TProp.kWhTotal)] := [TPropertyFlag.SilentReadOnly, TPropertyFlag.ReadByFunction];
+ PropertyFlags[ord(TProp.kWTotal)] := [TPropertyFlag.SilentReadOnly, TPropertyFlag.ReadByFunction];
+ PropertyFlags[ord(TProp.kWhActual)] := [TPropertyFlag.SilentReadOnly, TPropertyFlag.ReadByFunction];
+ PropertyFlags[ord(TProp.kWActual)] := [TPropertyFlag.SilentReadOnly, TPropertyFlag.ReadByFunction];
+ PropertyReadFunction[ord(TProp.kWhTotal)] := @GetkWhTotal;
+ PropertyReadFunction[ord(TProp.kWTotal)] := @GetkWTotal;
+ PropertyReadFunction[ord(TProp.kWhActual)] := @GetFleetkWh;
+ PropertyReadFunction[ord(TProp.kWActual)] := @GetFleetkW;
+
+ // double read-only
+ PropertyFlags[ord(TProp.kWneed)] := [TPropertyFlag.SilentReadOnly];
+ PropertyOffset[ord(TProp.kWneed)] := ptruint(@obj.kWNeeded);
+
+ // boolean properties
+ PropertyType[ord(TProp.EventLog)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.EventLog)] := ptruint(@obj.ShowEventLog);
+
+ PropertyType[ord(TProp.VarDispatch)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.VarDispatch)] := ptruint(@obj.DispatchVars);
+
+ // objects
+ PropertyType[ord(TProp.yearly)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.daily)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.duty)] := TPropertyType.DSSObjectReferenceProperty;
+
+ PropertyOffset[ord(TProp.yearly)] := ptruint(@obj.YearlyShapeObj);
+ PropertyOffset[ord(TProp.daily)] := ptruint(@obj.DailyShapeObj);
+ PropertyOffset[ord(TProp.duty)] := ptruint(@obj.DutyShapeObj);
+
+ PropertyOffset2[ord(TProp.yearly)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.daily)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.duty)] := ptruint(DSS.LoadShapeClass);
+
+ PropertyType[ord(TProp.Element)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.Element)] := ptruint(@obj.FMonitoredElement);
+ PropertyOffset2[ord(TProp.Element)] := 0;
+ PropertyWriteFunction[ord(TProp.Element)] := @SetMonitoredElement;
+ PropertyFlags[ord(TProp.Element)] := [TPropertyFlag.WriteByFunction];//[TPropertyFlag.CheckForVar]; // not required for general cktelements
+
+ // string properties
+ PropertyType[ord(TProp.pctParticipation)] := TPropertyType.StringProperty; // actually unused here
+ PropertyOffset[ord(TProp.pctParticipation)] := ptruint(@obj.ParticipationStr);
+
+ // string lists
+ PropertyType[ord(TProp.ElementList)] := TPropertyType.StringListProperty;
+ PropertyOffset[ord(TProp.ElementList)] := ptruint(@obj.FStorageNameList);
+
+ // integers
+ PropertyType[ord(TProp.Terminal)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Terminal)] := ptruint(@obj.ElementTerminal);
+ PropertyType[ord(TProp.Seasons)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Seasons)] := ptruint(@obj.Seasons);
+
+ PropertyType[ord(TProp.InhibitTime)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.InhibitTime)] := ptruint(@obj.Inhibithrs); // >=1, silent, handled as side effect
+
+ // double arrays
+ PropertyType[ord(TProp.SeasonTargets)] := TPropertyType.DoubleDArrayProperty;
+ PropertyOffset[ord(TProp.SeasonTargets)] := ptruint(@obj.SeasonTargets);
+ PropertyOffset2[ord(TProp.SeasonTargets)] := ptruint(@obj.Seasons);
+
+ PropertyType[ord(TProp.SeasonTargetsLow)] := TPropertyType.DoubleDArrayProperty;
+ PropertyOffset[ord(TProp.SeasonTargetsLow)] := ptruint(@obj.SeasonTargetsLow);
+ PropertyOffset2[ord(TProp.SeasonTargetsLow)] := ptruint(@obj.Seasons);
+
+ PropertyType[ord(TProp.Weights)] := TPropertyType.DoubleDArrayProperty;
+ PropertyOffset[ord(TProp.Weights)] := ptruint(@obj.FWeights);
+ PropertyOffset2[ord(TProp.Weights)] := ptruint(@obj.FleetSize);
+
+ // doubles
+ PropertyOffset[ord(TProp.ResetLevel)] := ptruint(@obj.ResetLevel);
+ PropertyOffset[ord(TProp.PFBand)] := ptruint(@obj.FPFBand);
+ PropertyOffset[ord(TProp.kWTarget)] := ptruint(@obj.FkWTarget);
+ PropertyOffset[ord(TProp.kWTargetLow)] := ptruint(@obj.FkWTargetLow);
+ PropertyOffset[ord(TProp.pctkWBand)] := ptruint(@obj.FpctkWBand);
+ PropertyOffset[ord(TProp.pctkWBandLow)] := ptruint(@obj.FpctkWBandLow);
+ PropertyOffset[ord(TProp.TimeDischargeTrigger)] := ptruint(@obj.DischargeTriggerTime);
+ PropertyOffset[ord(TProp.TimeChargeTrigger)] := ptruint(@obj.ChargeTriggerTime);
+ PropertyOffset[ord(TProp.pctRatekW)] := ptruint(@obj.pctkWRate);
+ PropertyOffset[ord(TProp.pctRateCharge)] := ptruint(@obj.pctChargeRate);
+ PropertyOffset[ord(TProp.pctReserve)] := ptruint(@obj.pctFleetReserve);
+ PropertyOffset[ord(TProp.Tup)] := ptruint(@obj.UpRamptime);
+ PropertyOffset[ord(TProp.TFlat)] := ptruint(@obj.FlatTime);
+ PropertyOffset[ord(TProp.Tdn)] := ptruint(@obj.DnrampTime);
+ PropertyOffset[ord(TProp.kWThreshold)] := ptruint(@obj.FkWThreshold);
+ PropertyOffset[ord(TProp.pctRatekvar)] := ptruint(@obj.pctkvarRate);
+
+ PropertyOffset[ord(TProp.PFTarget)] := 1;
+ PropertyWriteFunction[ord(TProp.PFTarget)] := @SetPctkvarRate;
+ PropertyReadFunction[ord(TProp.PFTarget)] := @GetPctkvarRate;
+ PropertyFlags[ord(TProp.PFTarget)] := [TPropertyFlag.WriteByFunction, TPropertyFlag.ReadByFunction];
- // continue parsing with contents of Parser
- DSS.ActiveStorageControllerObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveStorageControllerObj;
- Result := 0;
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
+end;
- with DSS.ActiveStorageControllerObj do
- begin
+function TStorageController.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
+begin
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
+end;
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
+procedure TStorageControllerObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
+var
+ i: Integer;
+ casemult: Double;
+begin
+ case Idx of
+ ord(TProp.kWTarget),
+ ord(TProp.pctkWBand):
begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
+ if DischargeMode = CURRENTPEAKSHAVE then // evaluates the discharging mode to apply
+ Casemult := 1000.0 // a compensation value (for kamps)
else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 14407);
- propELEMENT:
- ElementName := lowercase(param);
- propTERMINAL:
- ElementTerminal := Parser.IntValue;
- propKWTARGET:
- FkWTarget := Parser.DblValue;
- propKWTARGETLOW:
- FkWTargetLow := Parser.DblValue;
- propKWBAND:
- FpctkWBand := Parser.DblValue;
- propKWBANDLOW:
- FpctkWBandLow := Parser.DblValue;
- propPFTARGET:
- FPFTarget := ConvertPFToPFRange2(Parser.DblValue);
- propPFBAND:
- FPFBand := Parser.DblValue;
- propELEMENTLIST:
- InterpretTStringListArray(Param, FStorageNameList);
- propWEIGHTS:
- begin
- FleetSize := FStorageNameList.count;
- if FleetSize > 0 then
- begin
- Reallocmem(FWeights, Sizeof(FWeights^[1]) * FleetSize);
- FleetSize := InterpretDblArray(Param, FleetSize, FWeights);
- end;
- end;
- propMODEDISCHARGE:
- DisChargeMode := InterpretMode(propMODEDISCHARGE, Param);
- propMODECHARGE:
- ChargeMode := InterpretMode(propMODECHARGE, Param);
- propTIMEDISCHARGETRIGGER:
- DischargeTriggerTime := Parser.DblValue;
- propTIMECHARGETRIGGER:
- ChargeTriggerTime := Parser.DblValue;
- propRATEKW:
- pctkWRate := Parser.DblValue;
- propRATEKVAR:
- pctkvarRate := Parser.DblValue;
- propRATECHARGE:
- pctChargeRate := Parser.DblValue;
- propRESERVE:
- pctFleetReserve := Parser.DblValue;
- propKWHTOTAL: ; // Do nothing (Read ONly)
- propKWTOTAL: ; // Do nothing (Read ONly)
- propKWHACTUAL: ; // Do nothing (Read ONly)
- propKWACTUAL: ; // Do nothing (Read ONly)
- propKWNEED: ; // Do nothing (Read ONly)
- propPARTICIPATION: ;
- propYEARLY:
- YearlyShape := Param;
- propDAILY:
- DailyShape := Param;
- propDUTY:
- DutyShape := Param;
- propEVENTLOG:
- ShowEventLog := InterpretYesNo(Param);
- propVARDISPATCH:
- DispatchVars := InterpretYesNo(Param);
- propINHIBITTIME:
- Inhibithrs := Max(1, Parser.IntValue); // >=1
- propTUPRAMP:
- UpRamptime := Parser.DblValue;
- propTFLAT:
- FlatTime := Parser.DblValue;
- propTDNRAMP:
- DnrampTime := Parser.DblValue;
- propKWTHRESHOLD:
- FkWThreshold := Parser.DblValue;
- propRESETLEVEL:
- ResetLevel := Parser.DblValue;
- propSEASONS:
- Seasons := Parser.IntValue;
- propSEASONTARGETS:
- begin
- if Seasons > 1 then
- begin
- setlength(SeasonTargets, Seasons);
- Seasons := InterpretDblArray(Param, Seasons, Pointer(SeasonTargets));
- end;
- end;
- propSEASONTARGETSLOW:
- begin
- if Seasons > 1 then
- begin
- setlength(SeasonTargetsLow, Seasons);
- Seasons := InterpretDblArray(Param, Seasons, Pointer(SeasonTargetsLow));
- end;
- end;
-
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveStorageControllerObj, ParamPointer - NumPropsthisClass)
- end;
-
- // Side effects of setting properties above
-
- case ParamPointer of
- propKWTARGET,
- propKWBAND:
- begin
- if DischargeMode = CURRENTPEAKSHAVE then // evaluates the discharging mode to apply
- Casemult := 1000.0 // a compensation value (for kamps)
- else
- Casemult := 1.0;
-
- HalfkWBand := FpctkWBand / 200.0 * FkWTarget * Casemult;
- FkWThreshold := FkWTarget * 0.75 * Casemult;
- end;
- propKWTARGETLOW,
- propKWBANDLOW:
- begin
- if ChargeMode = CURRENTPEAKSHAVELOW then // evaluates the charging mode to apply
- Casemult := 1000.0 // a compensation value (for kamps)
- else
- Casemult := 1.0;
-
- HalfkWBandLow := FpctkWBandLow / 200.0 * FkWTargetLow * Casemult;
- end;
- propPFBAND:
- HalfPFBand := FPFBand / 2.0;
- propMODEDISCHARGE:
- if DischargeMode = MODEFOLLOW then
- DischargeTriggerTime := 12.0; // Noon
-
- propELEMENTLIST:
- begin // levelize the list
- FleetPointerList.Clear; // clear this for resetting on first sample
- FleetListChanged := TRUE;
- FElementListSpecified := TRUE;
- FleetSize := FStorageNameList.count;
- // Realloc weights to be same size as possible number of storage elements
- Reallocmem(FWeights, Sizeof(FWeights^[1]) * FleetSize);
- for i := 1 to FleetSize do
- FWeights^[i] := 1.0;
- end;
- propYEARLY:
- begin
- YearlyShapeObj := DSS.LoadShapeClass.Find(YearlyShape);
- if YearlyShapeObj = NIL then
- DoSimpleMsg('Yearly loadshape "' + YearlyShape + '" not found.', 14404);
- end;
- propDAILY:
- begin
- DailyShapeObj := DSS.LoadShapeClass.Find(DailyShape);
- if DailyShapeObj = NIL then
- DoSimpleMsg('Daily loadshape "' + DailyShape + '" not found.', 14405);
- end;
- propDUTY:
- begin
- DutyShapeObj := DSS.LoadShapeClass.Find(DutyShape);
- if DutyShapeObj = NIL then
- DoSimpleMsg('Dutycycle loadshape "' + DutyShape + '" not found.', 14406);
- end
+ Casemult := 1.0;
+ HalfkWBand := FpctkWBand / 200.0 * FkWTarget * Casemult;
+ FkWThreshold := FkWTarget * 0.75 * Casemult;
+ end;
+ ord(TProp.kWTargetLow),
+ ord(TProp.pctkWBandLow):
+ begin
+ if ChargeMode = CURRENTPEAKSHAVELOW then // evaluates the charging mode to apply
+ Casemult := 1000.0 // a compensation value (for kamps)
else
+ Casemult := 1.0;
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ HalfkWBandLow := FpctkWBandLow / 200.0 * FkWTargetLow * Casemult;
end;
-
- RecalcElementData;
+ ord(TProp.PFBand):
+ HalfPFBand := FPFBand / 2.0;
+ ord(TProp.ModeDischarge):
+ if DischargeMode = MODEFOLLOW then
+ DischargeTriggerTime := 12.0; // Noon
+
+ ord(TProp.ElementList):
+ begin // levelize the list
+ FleetPointerList.Clear; // clear this for resetting on first sample
+ FleetListChanged := TRUE;
+ FElementListSpecified := TRUE;
+ FleetSize := FStorageNameList.count;
+ // Realloc weights to be same size as possible number of storage elements
+ Reallocmem(FWeights, Sizeof(FWeights^[1]) * FleetSize);
+ for i := 1 to FleetSize do
+ FWeights^[i] := 1.0;
+ end;
+ ord(TProp.InhibitTime):
+ Inhibithrs := Max(1, Inhibithrs); // >=1, silent
end;
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-
-{--------------------------------------------------------------------------}
-function TStorageController.MakeLike(const StorageControllerName: String): Integer;
+procedure TStorageControllerObj.MakeLike(OtherPtr: Pointer);
var
- OtherStorageController: TStorageControllerObj;
+ Other: TObj;
i: Integer;
begin
- Result := 0;
- {See If we can find this StorageController name in the present collection}
- OtherStorageController := Find(StorageControllerName);
- if OtherStorageController <> NIL then
- with DSS.ActiveStorageControllerObj do
- begin
-
- NPhases := OtherStorageController.Fnphases;
- NConds := OtherStorageController.Fnconds; // Force Reallocation of terminal stuff
-
- ElementName := OtherStorageController.ElementName;
- ControlledElement := OtherStorageController.ControlledElement; // Pointer to target circuit element
- MonitoredElement := OtherStorageController.MonitoredElement; // Pointer to target circuit element
- ElementTerminal := OtherStorageController.ElementTerminal;
-
- FkWTarget := OtherStorageController.FkWTarget;
- FkWTargetLow := OtherStorageController.FkWTargetLow;
- FkWThreshold := OtherStorageController.FkWThreshold;
- FpctkWBand := OtherStorageController.FpctkWBand;
- FpctkWBandLow := OtherStorageController.FpctkWBandLow;
- FPFTarget := OtherStorageController.FPFTarget;
- FPFBand := OtherStorageController.FPFBand;
- HalfPFBand := OtherStorageController.HalfPFBand;
- ResetLevel := OtherStorageController.ResetLevel;
-
- FStorageNameList.Clear;
- for i := 1 to OtherStorageController.FStorageNameList.Count do
- FStorageNameList.Add(OtherStorageController.FStorageNameList.Strings[i - 1]);
-
- FleetSize := FStorageNameList.count;
- if FleetSize > 0 then
- begin
- Reallocmem(FWeights, Sizeof(FWeights^[1]) * FleetSize);
- for i := 1 to FleetSize do
- FWeights^[i] := OtherStoragecontroller.FWeights^[i];
- end;
-
- DisChargeMode := OtherStorageController.DisChargeMode;
- ChargeMode := OtherStorageController.ChargeMode;
- DischargeTriggerTime := OtherStorageController.DischargeTriggerTime;
- ChargeTriggerTime := OtherStorageController.ChargeTriggerTime;
- pctkWRate := OtherStorageController.pctkWRate;
- pctkvarRate := OtherStorageController.pctkvarRate;
- pctChargeRate := OtherStorageController.pctChargeRate;
- pctFleetReserve := OtherStorageController.pctFleetReserve;
- YearlyShape := OtherStorageController.YearlyShape;
- DailyShape := OtherStorageController.DailyShape;
- DutyShape := OtherStorageController.DutyShape;
- DispatchVars := OtherStorageController.DispatchVars;
- ShowEventLog := OtherStorageController.ShowEventLog;
- Inhibithrs := OtherStorageController.Inhibithrs;
-
- UpRamptime := OtherStorageController.UpRamptime;
- FlatTime := OtherStorageController.FlatTime;
- DnrampTime := OtherStorageController.DnrampTime;
-
- Seasons := OtherStorageController.Seasons;
- if Seasons > 1 then
- begin
- setlength(SeasonTargets, Seasons);
- setlength(SeasonTargetsLow, Seasons);
- for i := 0 to (Seasons - 1) do
- begin
- SeasonTargets[i] := OtherStoragecontroller.SeasonTargets[i];
- SeasonTargetsLow[i] := OtherStoragecontroller.SeasonTargetsLow[i];
- end;
- end;
-
-
-//**** fill in private properties
-
- for i := 1 to ParentClass.NumProperties do
- // Skip Read only properties
- case i of
- propKWHTOTAL: ; {Do Nothing}
- propKWTOTAL: ; {Do Nothing}
- propKWHACTUAL: ; {Do Nothing}
- propKWACTUAL: ; {Do Nothing}
- propKWNEED: ; {Do Nothing}
- else
- PropertyValue[i] := OtherStorageController.PropertyValue[i];
- end;
-
-
- end
- else
- DoSimpleMsg('Error in StorageController MakeLike: "' + StorageControllerName + '" Not Found.', 370);
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
+
+ // ControlledElement := Other.ControlledElement; // Pointer to target circuit element
+ MonitoredElement := Other.MonitoredElement; // Pointer to target circuit element
+ ElementTerminal := Other.ElementTerminal;
+
+ FkWTarget := Other.FkWTarget;
+ FkWTargetLow := Other.FkWTargetLow;
+ FkWThreshold := Other.FkWThreshold;
+ FpctkWBand := Other.FpctkWBand;
+ FpctkWBandLow := Other.FpctkWBandLow;
+ FPFTarget := Other.FPFTarget;
+ FPFBand := Other.FPFBand;
+ HalfPFBand := Other.HalfPFBand;
+ ResetLevel := Other.ResetLevel;
+
+ FStorageNameList.Clear;
+ for i := 1 to Other.FStorageNameList.Count do
+ FStorageNameList.Add(Other.FStorageNameList.Strings[i - 1]);
+
+ FleetSize := FStorageNameList.count;
+ if FleetSize > 0 then
+ begin
+ Reallocmem(FWeights, Sizeof(FWeights^[1]) * FleetSize);
+ for i := 1 to FleetSize do
+ FWeights^[i] := Other.FWeights^[i];
+ end;
+ DisChargeMode := Other.DisChargeMode;
+ ChargeMode := Other.ChargeMode;
+ DischargeTriggerTime := Other.DischargeTriggerTime;
+ ChargeTriggerTime := Other.ChargeTriggerTime;
+ pctkWRate := Other.pctkWRate;
+ pctkvarRate := Other.pctkvarRate;
+ pctChargeRate := Other.pctChargeRate;
+ pctFleetReserve := Other.pctFleetReserve;
+ YearlyShapeObj := Other.YearlyShapeObj;
+ DailyShapeObj := Other.DailyShapeObj;
+ DutyShapeObj := Other.DutyShapeObj;
+ DispatchVars := Other.DispatchVars;
+ ShowEventLog := Other.ShowEventLog;
+ Inhibithrs := Other.Inhibithrs;
+
+ UpRamptime := Other.UpRamptime;
+ FlatTime := Other.FlatTime;
+ DnrampTime := Other.DnrampTime;
+
+ Seasons := Other.Seasons;
+ if Seasons > 1 then
+ begin
+ setlength(SeasonTargets, Seasons);
+ setlength(SeasonTargetsLow, Seasons);
+ for i := 0 to (Seasons - 1) do
+ begin
+ SeasonTargets[i] := Other.SeasonTargets[i];
+ SeasonTargetsLow[i] := Other.SeasonTargetsLow[i];
+ end;
+ end;
end;
-
-{==========================================================================}
-{ TStorageControllerObj }
-{==========================================================================}
-
-
-{--------------------------------------------------------------------------}
constructor TStorageControllerObj.Create(ParClass: TDSSClass; const StorageControllerName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(StorageControllerName);
+ Name := AnsiLowerCase(StorageControllerName);
DSSObjType := ParClass.DSSClassType;
- NPhases := 3; // Directly set conds and phases
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 1; // this forces allocation of terminals and conductors
- ElementName := '';
+ ParticipationStr := '';
ControlledElement := NIL; // not used in this control
ElementTerminal := 1;
MonitoredElement := NIL;
- FStorageNameList := TSTringList.Create;
+ FStorageNameList := TStringList.Create;
FWeights := NIL;
FleetPointerList := TDSSPointerList.Create(20); // Default size and increment
FleetSize := 0;
@@ -845,167 +630,17 @@ constructor TStorageControllerObj.Create(ParClass: TDSSClass; const StorageContr
SeasonTargets[0] := FkWTarget;
setlength(SeasonTargetsLow, 1);
SeasonTargetsLow[0] := FkWTargetLow;
-
-
- InitPropertyValues(0);
-
end;
destructor TStorageControllerObj.Destroy;
begin
- ElementName := '';
- YearlyShape := '';
- DailyShape := '';
- DutyShape := '';
-
-(* Don't Do this here!! Disposes of actual object;
- YearlyShapeObj.Free;
- DailyShapeObj.Free;
- DutyShapeObj.Free;
-*)
-
-
FleetPointerList.Free;
FStorageNameList.Free;
inherited Destroy;
end;
-//----------------------------------------------------------------------------
-procedure TStorageControllerObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
-
- PropertyValue[propELEMENT] := '';
- PropertyValue[propTERMINAL] := '1';
- PropertyValue[propKWTARGET] := '8000';
- PropertyValue[propKWTARGETLOW] := '4000';
- PropertyValue[propKWBAND] := '2';
- PropertyValue[propKWBANDLOW] := '2';
- PropertyValue[propPFTARGET] := '.96';
- PropertyValue[propPFBAND] := '.04';
- PropertyValue[propELEMENTLIST] := '';
- PropertyValue[propWEIGHTS] := '';
- PropertyValue[propMODEDISCHARGE] := 'Follow';
- PropertyValue[propMODECHARGE] := 'Time';
- PropertyValue[propTIMEDISCHARGETRIGGER] := '-1';
- PropertyValue[propTIMECHARGETRIGGER] := '2';
- PropertyValue[propRATEKW] := '20';
- PropertyValue[propRATEKVAR] := '20';
- PropertyValue[propRATECHARGE] := '20';
- PropertyValue[propRESERVE] := '25';
- PropertyValue[propKWHTOTAL] := '';
- PropertyValue[propKWTOTAL] := '';
- PropertyValue[propKWACTUAL] := '';
- PropertyValue[propKWNEED] := '';
- PropertyValue[propPARTICIPATION] := '';
- PropertyValue[propYEARLY] := '';
- PropertyValue[propDAILY] := '';
- PropertyValue[propDUTY] := '';
- PropertyValue[propEVENTLOG] := 'No';
- PropertyValue[propINHIBITTIME] := '5';
- PropertyValue[propTUPRAMP] := '0.25';
- PropertyValue[propTFLAT] := '2.0';
- PropertyValue[propTDNRAMP] := '0.25';
- PropertyValue[propKWTHRESHOLD] := '4000';
- PropertyValue[propRESETLEVEL] := '0.8';
- PropertyValue[propSEASONS] := '1';
- PropertyValue[propSEASONTARGETS] := '[8000,]';
- PropertyValue[propSEASONTARGETSLOW] := '[4000,]';
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-function TStorageControllerObj.GetPropertyValue(Index: Integer): String;
-//var
- // I: Integer;
- // TempStr: String;
-begin
- Result := '';
- case Index of
-
- propKWTARGET:
- Result := Format('%-.6g', [FkWTarget]);
- propKWTARGETLOW:
- Result := Format('%-.6g', [FkWTargetLow]);
- propKWBAND:
- Result := Format('%-.6g', [FpctkWBand]);
- propKWBANDLOW:
- Result := Format('%-.6g', [FpctkWBandLow]);
- propPFTARGET:
- Result := Format('%-.6g', [ConvertPFRange2ToPF(FPFTarget)]);
- propPFBAND:
- Result := Format('%-.6g', [FPFBand]);
- propELEMENTLIST:
- Result := ReturnElementsList;
- propWEIGHTS:
- Result := ReturnWeightsList;
- propMODEDISCHARGE:
- Result := GetModeString(propMODEDISCHARGE, DischargeMode);
- propMODECHARGE:
- Result := GetModeString(propMODECHARGE, ChargeMode);
- propTIMEDISCHARGETRIGGER:
- Result := Format('%.6g', [DisChargeTriggerTime]);
- propTIMECHARGETRIGGER:
- Result := Format('%.6g', [ChargeTriggerTime]);
- propRATEKW:
- Result := Format('%-.8g', [pctkWRate]);
- propRATEKVAR:
- Result := Format('%-.8g', [pctkvarRate]);
- propRATECHARGE:
- Result := Format('%-.8g', [pctChargeRate]);
- propRESERVE:
- Result := Format('%-.8g', [pctFleetReserve]);
- propKWHTOTAL:
- Result := GetkWhTotal(TotalkWhCapacity);
- propKWTOTAL:
- Result := GetkWTotal(TotalkWCapacity);
- propKWHACTUAL:
- Result := GetkWhActual;
- propKWACTUAL:
- Result := GetkWActual;
- propKWNEED:
- Result := Format('%-.6g', [kWNeeded]);
- {propPARTICIPATION : Result := PropertyValue[Index]; }
- propYEARLY:
- Result := YearlyShape;
- propDAILY:
- Result := DailyShape;
- propDUTY:
- Result := DutyShape;
- propEVENTLOG:
- Result := StrYorN(ShowEventLog);
- propVARDISPATCH:
- Result := StrYorN(DispatchVars);
- propINHIBITTIME:
- Result := Format('%d', [InhibitHrs]);
- propTUPRAMP:
- Result := Format('%.6g', [UpRamptime]);
- propTFLAT:
- Result := Format('%.6g', [FlatTime]);
- propTDNRAMP:
- Result := Format('%.6g', [DnrampTime]);
- propKWTHRESHOLD:
- Result := Format('%.6g', [FkWThreshold]);
- propRESETLEVEL:
- Result := Format('%.6g', [ResetLevel]);
- propSEASONS:
- Result := Format('%d', [seasons]);
- propSEASONTARGETS:
- Result := ReturnSeasonTarget(1);
- propSEASONTARGETSLOW:
- Result := ReturnSeasonTarget(0);
-
- else // take the generic handler
- Result := inherited GetPropertyValue(index);
-
- end;
-end;
-
function TStorageControllerObj.Get_FleetkW: Double;
-
var
pStorage: TStorageObj;
i: Integer;
@@ -1055,48 +690,38 @@ function TStorageControllerObj.Get_FleetReservekWh: Double;
pStorage := FleetPointerList.Get(i);
Result := Result + pStorage.StorageVars.kWhReserve;
end;
-
end;
-{--------------------------------------------------------------------------}
procedure TStorageControllerObj.RecalcElementData;
-
// Recalculate critical element values after changes have been made
-
-var
- DevIndex: Integer;
-
begin
-
- {Check for existence of monitored element}
-
- Devindex := GetCktElementIndex(ElementName); // Global FUNCTION
- if DevIndex > 0 then
+ {Check for existence of monitored element}
+ if MonitoredElement <> NIL then
begin
- MonitoredElement := ActiveCircuit.CktElements.Get(DevIndex);
if ElementTerminal > MonitoredElement.Nterms then
begin
- DoErrorMsg('StorageController: "' + Name + '"',
- 'Terminal no. "' + '" Does not exist.',
- 'Re-specify terminal no.', 371);
+ DoErrorMsg(
+ Format(_('StorageController: "%s"'), [Name]),
+ Format(_('Terminal no. "%d" Does not exist.'), [MonitoredElement]),
+ _('Re-specify terminal no.'), 371);
end
else
begin
- Nphases := MonitoredElement.Nphases;
+ FNphases := MonitoredElement.Nphases;
NConds := FNphases;
- // Sets name of i-th terminal's connected bus in StorageController's buslist
+ // Sets name of i-th terminal's connected bus in StorageController's buslist
Setbus(1, MonitoredElement.GetBus(ElementTerminal));
end;
end
else
- DoSimpleMsg('Monitored Element in StorageController.' + Name + ' Does not exist:"' + ElementName + '"', 372);
+ DoSimpleMsg('Monitored Element in "%s" is not set', [FullName], 372);
if FleetListChanged then
if not MakeFleetList then
- DoSimpleMsg('No unassigned Storage Elements found to assign to StorageController.' + Name, 37201);
+ DoSimpleMsg('No unassigned Storage Elements found to assign to %s', [FullName], 37201);
- GetkWTotal(TotalkWCapacity);
- GetkWhTotal(TotalkWhCapacity);
+ // TotalkWCapacity := GetkWTotal();
+ // TotalkWhCapacity := GetkWhTotal();
if FleetSize > 0 then
begin
@@ -1106,172 +731,33 @@ procedure TStorageControllerObj.RecalcElementData;
UpPlusFlat := UpRampTime + FlatTime;
UpPlusFlatPlusDn := UpPlusFlat + DnRampTime;
-
end;
-procedure TStorageControllerObj.MakePosSequence;
+procedure TStorageControllerObj.MakePosSequence();
begin
if MonitoredElement <> NIL then
begin
- Nphases := MonitoredElement.NPhases;
+ FNphases := MonitoredElement.NPhases;
Nconds := FNphases;
Setbus(1, MonitoredElement.GetBus(ElementTerminal));
end;
inherited;
end;
-{--------------------------------------------------------------------------}
-procedure TStorageControllerObj.CalcYPrim;
-begin
- // leave YPrims as nil and they will be ignored
- // Yprim is zeroed when created. Leave it as is.
- // IF YPrim=nil THEN YPrim := TcMatrix.CreateMatrix(Yorder);
-
-
-end;
-
-
-{--------------------------------------------------------------------------}
-procedure TStorageControllerObj.GetCurrents(Curr: pComplexArray);
-var
- i: Integer;
-
-begin
-
- for i := 1 to Fnconds do
- Curr^[i] := CZERO;
-
-end;
-
-function TStorageControllerObj.GetkWActual: String;
-begin
- Result := Format('%-.8g', [FleetkW]);
-end;
-
-function TStorageControllerObj.GetkWhActual: String;
-
-begin
- Result := Format('%-.8g', [FleetkWh]);
-end;
-
-function TStorageControllerObj.GetkWhTotal(var Sum: Double): String;
-var
- pStorage: TStorageObj;
- i: Integer;
-
-begin
- Sum := 0.0;
- for i := 1 to FleetPointerList.Count do
- begin
- pStorage := FleetPointerList.Get(i);
- sum := sum + pStorage.StorageVars.kWhRating;
- end;
- Result := Format('%-.8g', [sum]);
-end;
-
-function TStorageControllerObj.GetkWTotal(var Sum: Double): String;
-var
- pStorage: TStorageObj;
- i: Integer;
-
-begin
- Sum := 0.0;
- for i := 1 to FleetPointerList.Count do
- begin
- pStorage := FleetPointerList.Get(i);
- sum := sum + pStorage.StorageVars.kWRating;
- end;
- Result := Format('%-.8g', [sum]);
-end;
-
-function TStorageControllerObj.GetModeString(Opt, Mode: Integer): String;
-begin
- Result := '';
- case Opt of
- propMODEDISCHARGE:
- case Mode of
- MODEFOLLOW:
- Result := 'Follow';
- MODELOADSHAPE:
- Result := 'Loadshape';
- MODESUPPORT:
- Result := 'Support';
- MODETIME:
- Result := 'Time';
- MODEPEAKSHAVE:
- Result := 'Peakshave';
- CURRENTPEAKSHAVE:
- Result := 'I-Peakshave';
- else
- Result := 'UNKNOWN'
- end;
- propMODECHARGE:
- case Mode of
- // 1: Result := 'Follow';
- MODELOADSHAPE:
- Result := 'Loadshape';
- // 3: Result := 'Support';
- MODETIME:
- Result := 'Time';
- MODEPEAKSHAVELOW:
- Result := 'PeakshaveLow';
- CURRENTPEAKSHAVELOW:
- Result := 'I-PeakshaveLow';
- else
- Result := 'UNKNOWN'
- end;
- else
- DoSimpleMsg('Unknown Charge/Discharge designation', 14401);
- end;
-end;
-
-
-{--------------------------------------------------------------------------}
-procedure TStorageControllerObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
- if Complete then
- begin
- FSWriteln(F);
- end;
-
-end;
-
-
-{--------------------------------------------------------------------------}
procedure TStorageControllerObj.DoPendingAction(const Code, ProxyHdl: Integer);
begin
-
- {
- Release the discharge inhibit .
- Do nothing for other codes
- }
-
+ // Release the discharge inhibit .
+ // Do nothing for other codes
if (Code = RELEASE_INHIBIT) and (DischargeMode <> MODEFOLLOW) then
DischargeInhibited := FALSE;
-
end;
procedure TStorageControllerObj.DoScheduleMode;
-{
- In SCHEDULE mode we ramp up the storage from zero to the specified pctkWRate.
- This value is held for the flattime or until they turn themselves
- off when they are either fully discharged, or ramped down
-
- The discharge trigger time must be greater than 0
-}
+// In SCHEDULE mode we ramp up the storage from zero to the specified pctkWRate.
+// This value is held for the flattime or until they turn themselves
+// off when they are either fully discharged, or ramped down
+// The discharge trigger time must be greater than 0
var
TDiff: Double;
pctDischargeRate: Double;
@@ -1304,17 +790,14 @@ procedure TStorageControllerObj.DoScheduleMode;
TDiff := NormalizeToTOD(DynaVars.intHour, DynaVars.t) - DisChargeTriggerTime;
if TDiff < UpRampTime then
begin
-
pctDischargeRate := min(pctkWRate, max(pctKWRate * Tdiff / UpRampTime, 0.0));
SetFleetkWRate(pctDischargeRate);
end
else
begin
-
if TDiff < UpPlusFlat then
begin
-
pctDischargeRate := pctkWRate;
if PctDischargeRate <> LastpctDischargeRate then
SetFleetkWRate(pctkWRate); // on the flat part
@@ -1323,7 +806,6 @@ procedure TStorageControllerObj.DoScheduleMode;
else
if TDiff > UpPlusFlatPlusDn then
begin
-
SetFleetToIdle;
ChargingAllowed := TRUE;
pctDischargeRate := 0.0;
@@ -1351,13 +833,10 @@ procedure TStorageControllerObj.DoScheduleMode;
end;
procedure TStorageControllerObj.DoTimeMode(Opt: Integer);
-{
- In Time mode we need to only turn the storage elements on. They will turn themselves
- off when they are either fully discharged, fully charged, or receive another command
- from the controller
-}
+// In Time mode we need to only turn the storage elements on. They will turn themselves
+// off when they are either fully discharged, fully charged, or receive another command
+// from the controller
begin
-
case Opt of
1:
@@ -1365,12 +844,12 @@ procedure TStorageControllerObj.DoTimeMode(Opt: Integer);
if (DisChargeTriggerTime > 0.0) then
with ActiveCircuit.Solution do
begin
- // turn on if time within 1/2 time step
+ // turn on if time within 1/2 time step
if abs(NormalizeToTOD(DynaVars.intHour, DynaVars.t) - DisChargeTriggerTime) < DynaVars.h / 7200.0 then
begin
if not (FleetState = STORE_DISCHARGING) then
begin
- {Time is within 1 time step of the trigger time}
+ {Time is within 1 time step of the trigger time}
if ShowEventLog then
AppendToEventLog('StorageController.' + Self.Name, 'Fleet Set to Discharging by Time Trigger');
SetFleetToDischarge;
@@ -1394,14 +873,14 @@ procedure TStorageControllerObj.DoTimeMode(Opt: Integer);
if abs(NormalizeToTOD(DynaVars.intHour, DynaVars.t) - ChargeTriggerTime) < DynaVars.h / 7200.0 then
if not (FleetState = STORE_CHARGING) then
begin
- {Time is within 1 time step of the trigger time}
+ {Time is within 1 time step of the trigger time}
if ShowEventLog then
AppendToEventLog('StorageController.' + Self.Name, 'Fleet Set to Charging by Time Trigger');
SetFleetToCharge;
DischargeInhibited := TRUE;
OutOfOomph := FALSE;
PushTimeOntoControlQueue(STORE_CHARGING); // force re-solve at this time step
- // Push message onto control queue to release inhibit at a later time
+ // Push message onto control queue to release inhibit at a later time
with ActiveCircuit do
begin
Solution.LoadsNeedUpdating := TRUE; // Force recalc of power parms
@@ -1411,10 +890,8 @@ procedure TStorageControllerObj.DoTimeMode(Opt: Integer);
end;
end; //Charge mode
end;
-
end;
-//----------------------------------------------------------------------------
function TStorageControllerObj.NormalizeToTOD(h: Integer; sec: Double): Double;
// Normalize time to a floating point number representing time of day If Hour > 24
// time should be 0 to 23.999999....
@@ -1422,7 +899,6 @@ function TStorageControllerObj.NormalizeToTOD(h: Integer; sec: Double): Double;
HourOfDay: Integer;
begin
-
if h > 23 then
HourOfDay := (h - (h div 24) * 24)
else
@@ -1435,7 +911,6 @@ function TStorageControllerObj.NormalizeToTOD(h: Integer; sec: Double): Double;
end;
-
procedure TStorageControllerObj.PushTimeOntoControlQueue(Code: Integer);
{
Push present time onto control queue to force re solve at new dispatch value
@@ -1446,10 +921,8 @@ procedure TStorageControllerObj.PushTimeOntoControlQueue(Code: Integer);
LoadsNeedUpdating := TRUE; // Force recalc of power parms
ControlQueue.Push(DynaVars.intHour, DynaVars.t, Code, 0, Self);
end;
-
end;
-{--------------------------------------------------------------------------}
function TStorageControllerObj.Get_DynamicTarget(THigh: Integer): Double;
var
// Temp, temp2: Double;
@@ -1479,12 +952,8 @@ function TStorageControllerObj.Get_DynamicTarget(THigh: Integer): Double;
end;
end;
-
-{--------------------------------------------------------------------------}
procedure TStorageControllerObj.DoLoadFollowMode;
-
var
-
i: Integer;
S: Complex;
StorageObj: TSTorageObj;
@@ -1503,8 +972,6 @@ procedure TStorageControllerObj.DoLoadFollowMode;
RemainingkWh,
CtrlTarget,
ReservekWh: Double;
-
-
begin
// If list is not defined, go make one from all storage elements in circuit
if FleetPointerList.Count = 0 then
@@ -1512,7 +979,6 @@ procedure TStorageControllerObj.DoLoadFollowMode;
if FleetSize > 0 then
begin
-
StorekWChanged := FALSE;
StorekvarChanged := FALSE;
SkipkWDispatch := FALSE;
@@ -1581,13 +1047,13 @@ procedure TStorageControllerObj.DoLoadFollowMode;
if FleetState = STORE_CHARGING then
begin
if not (Dischargemode = CURRENTPEAKSHAVE) then
- Pdiff := Pdiff + FleetkW // ignore overload due to charging
+ Pdiff := Pdiff + Get_FleetkW() // ignore overload due to charging
else
begin
MonitoredElement.ComputeVterminal;
VoltsArr := MonitoredElement.Vterminal;
ElemVolts := cabs(VoltsArr^[1]);
- Pdiff := Pdiff + (FleetkW * 1000 / ElemVolts);
+ Pdiff := Pdiff + (Get_FleetkW() * 1000 / ElemVolts);
end;
end;
@@ -1611,7 +1077,7 @@ procedure TStorageControllerObj.DoLoadFollowMode;
end;
end;
STORE_DISCHARGING:
- if ((PDiff + FleetkW) < 0.0) or OutOfOomph then
+ if ((PDiff + Get_FleetkW()) < 0.0) or OutOfOomph then
begin // desired decrease is greater then present output; just cancel
SetFleetToIdle; // also sets presentkW = 0
PushTimeOntoControlQueue(STORE_IDLING); // force a new power flow solution
@@ -1626,7 +1092,7 @@ procedure TStorageControllerObj.DoLoadFollowMode;
if not SkipkWDispatch then
begin
- RemainingkWh := FleetkWh;
+ RemainingkWh := Get_FleetkWh();
ReservekWh := FleetReservekWh;
if (RemainingkWh > ReservekWh) then
begin
@@ -1711,11 +1177,8 @@ procedure TStorageControllerObj.DoLoadFollowMode;
{Else just continue}
end;
-
-
end;
-{--------------------------------------------------------------------------}
procedure TStorageControllerObj.DoPeakShaveModeLow;
// This is the peakShaving mode for controlling the charging operation of the storage fleet
// The objective is to charge the storage fleet when the power at a monitored element is bellow a specified KW target (kWTarget_low)
@@ -1768,8 +1231,8 @@ procedure TStorageControllerObj.DoPeakShaveModeLow;
PDiff := S.re * 0.001 - CtrlTarget; // Assume S.re is normally positive
end;
- // ActualkW := FleetkW;
- ActualkWh := FleetkWh;
+ // ActualkW := Get_FleetkW();
+ ActualkWh := Get_FleetkWh();
TotalRatingkWh := FleetkWhRating;
if Chargemode = CURRENTPEAKSHAVELOW then
@@ -1777,10 +1240,10 @@ procedure TStorageControllerObj.DoPeakShaveModeLow;
MonitoredElement.ComputeVterminal;
VoltsArr := MonitoredElement.Vterminal;
ElemVolts := cabs(VoltsArr^[1]);
- kWNeeded := ((Pdiff * ElemVolts) / 1000.0) + FleetkW;
+ kWNeeded := ((Pdiff * ElemVolts) / 1000.0) + Get_FleetkW();
end
else
- kWNeeded := Pdiff + FleetkW;
+ kWNeeded := Pdiff + Get_FleetkW();
case FleetState of
STORE_IDLING:
@@ -1817,7 +1280,6 @@ procedure TStorageControllerObj.DoPeakShaveModeLow;
StorageObj := FleetPointerList.Get(i);
with StorageObj do
begin
-
// Checks if PDiff needs to be adjusted considering the charging mode
if Chargemode = CURRENTPEAKSHAVELOW then
begin
@@ -1860,7 +1322,6 @@ procedure TStorageControllerObj.DoPeakShaveModeLow;
end;
end;
-{--------------------------------------------------------------------------}
procedure TStorageControllerObj.Sample;
begin
@@ -1888,7 +1349,7 @@ procedure TStorageControllerObj.Sample;
MODESCHEDULE:
DoScheduleMode;
else
- DoSimpleMsg(Format('Invalid DisCharging Mode: %d', [DisChargeMode]), 14408);
+ DoSimpleMsg('Invalid DisCharging Mode: %d', [DisChargeMode], 14408);
end;
if ChargingAllowed then
@@ -1901,14 +1362,11 @@ procedure TStorageControllerObj.Sample;
CURRENTPEAKSHAVELOW:
DoPeakShaveModeLow
else
- DoSimpleMsg(Format('Invalid Charging Mode: %d', [ChargeMode]), 14409);
+ DoSimpleMsg('Invalid Charging Mode: %d', [ChargeMode], 14409);
end;
-
-
end;
-//----------------------------------------------------------------------------
procedure TStorageControllerObj.CalcDailyMult(Hr: Double);
begin
@@ -1921,7 +1379,6 @@ procedure TStorageControllerObj.CalcDailyMult(Hr: Double);
end;
-//----------------------------------------------------------------------------
procedure TStorageControllerObj.CalcDutyMult(Hr: Double);
begin
@@ -1933,7 +1390,6 @@ procedure TStorageControllerObj.CalcDutyMult(Hr: Double);
CalcDailyMult(Hr); // Default to Daily Mult If no duty curve specified
end;
-//----------------------------------------------------------------------------
procedure TStorageControllerObj.CalcYearlyMult(Hr: Double);
begin
@@ -1945,7 +1401,6 @@ procedure TStorageControllerObj.CalcYearlyMult(Hr: Double);
CalcDailyMult(Hr); // Defaults to Daily curve
end;
-//----------------------------------------------------------------------------
procedure TStorageControllerObj.DoLoadShapeMode;
var
FleetStateSaved: Integer;
@@ -1954,7 +1409,6 @@ procedure TStorageControllerObj.DoLoadShapeMode;
NewkWRate,
NewkvarRate: Double;
begin
-
FleetStateSaved := FleetState;
RateChanged := FALSE;
@@ -2004,11 +1458,8 @@ procedure TStorageControllerObj.DoLoadShapeMode;
{Force a new power flow solution if fleet state has changed}
if (FleetState <> FleetStateSaved) or RateChanged then
PushTimeOntoControlQueue(0);
-
-
end;
-//----------------------------------------------------------------------------
procedure TStorageControllerObj.SetAllFleetValues;
var
i: Integer;
@@ -2023,7 +1474,6 @@ procedure TStorageControllerObj.SetAllFleetValues;
end;
end;
-//----------------------------------------------------------------------------
procedure TStorageControllerObj.SetFleetChargeRate;
var
i: Integer;
@@ -2032,7 +1482,6 @@ procedure TStorageControllerObj.SetFleetChargeRate;
TStorageObj(FleetPointerList.Get(i)).pctkWin := pctChargeRate;
end;
-//----------------------------------------------------------------------------
procedure TStorageControllerObj.SetFleetkvarRate;
var
i: Integer;
@@ -2042,7 +1491,6 @@ procedure TStorageControllerObj.SetFleetkvarRate;
TStorageObj(FleetPointerList.Get(i)).pctkvarout := pctkvarRate;
end;
-//----------------------------------------------------------------------------
procedure TStorageControllerObj.SetFleetkWRate(pctkw: Double);
var
i: Integer;
@@ -2051,7 +1499,6 @@ procedure TStorageControllerObj.SetFleetkWRate(pctkw: Double);
TStorageObj(FleetPointerList.Get(i)).pctkWout := pctkw;
end;
-//----------------------------------------------------------------------------
procedure TStorageControllerObj.SetFleetToCharge;
var
i: Integer;
@@ -2061,7 +1508,6 @@ procedure TStorageControllerObj.SetFleetToCharge;
FleetState := STORE_CHARGING;
end;
-//----------------------------------------------------------------------------
procedure TStorageControllerObj.SetFleetToDisCharge;
var
i: Integer;
@@ -2071,7 +1517,6 @@ procedure TStorageControllerObj.SetFleetToDisCharge;
FleetState := STORE_DISCHARGING;
end;
-//----------------------------------------------------------------------------
procedure TStorageControllerObj.SetFleetToIdle;
var
i: Integer;
@@ -2091,7 +1536,6 @@ procedure TStorageControllerObj.Set_PFBand(const Value: Double);
HalfPFBand := FPFBand / 2.0;
end;
-//----------------------------------------------------------------------------
procedure TStorageControllerObj.SetFleetToExternal;
var
i: Integer;
@@ -2100,73 +1544,11 @@ procedure TStorageControllerObj.SetFleetToExternal;
TStorageObj(FleetPointerList.Get(i)).DispatchMode := STORE_EXTERNALMODE;
end;
-//----------------------------------------------------------------------------
-(*
- PROCEDURE TStorageControllerObj.SetPctReserve;
- VAR
- i :Integer;
- Begin
- For i := 1 to FleetPointerList.Count Do
- TStorageObj(FleetPointerList.Get(i)).pctReserve := pctFleetReserve;
- End;
-*)
-
-
-//----------------------------------------------------------------------------
-function TStorageControllerObj.InterpretMode(Opt: Integer;
- const S: String): Integer;
-begin
-
- Result := -1; // Unknown: error
- case Opt of
- propMODEDISCHARGE:
- case LowerCase(S)[1] of
- 'f':
- Result := MODEFOLLOW;
- 'l':
- Result := MODELOADSHAPE;
- 'p':
- Result := MODEPEAKSHAVE;
- 's':
- if LowerCase(S)[2] = 'c' then
- Result := MODESCHEDULE
- else
- Result := MODESUPPORT;
- 't':
- Result := MODETIME;
- 'i':
- Result := CURRENTPEAKSHAVE;
- else
- DoSimpleMsg('Discharge Mode "' + S + '" not recognized.', 14402);
- end;
- propMODECHARGE:
- case LowerCase(S)[1] of
- // 'f': Result := MODEFOLLOW;
- 'l':
- Result := MODELOADSHAPE;
- // 's': Result := MODESUPPORT;
- 't':
- Result := MODETIME;
- 'p':
- Result := MODEPEAKSHAVELOW;
- 'i':
- Result := CURRENTPEAKSHAVELOW;
- else
- DoSimpleMsg('Charge Mode "' + S + '" not recognized.', 14402);
- end;
- else
- end;
-end;
-
-//----------------------------------------------------------------------------
function TStorageControllerObj.MakeFleetList: Boolean;
-
var
StorageObj: TStorageObj;
i: Integer;
-
begin
-
Result := FALSE;
if FElementListSpecified then
@@ -2179,11 +1561,11 @@ function TStorageControllerObj.MakeFleetList: Boolean;
if Assigned(StorageObj) then
begin
if StorageObj.Enabled then
- FleetPointerList.New := StorageObj;
+ FleetPointerList.Add(StorageObj);
end
else
begin
- DoSimpleMsg('Error: Storage Element "' + FStorageNameList.Strings[i - 1] + '" not found.', 14403);
+ DoSimpleMsg('Error: Storage Element "%s" not found.', [FStorageNameList.Strings[i - 1]], 14403);
Exit;
end;
end;
@@ -2192,7 +1574,6 @@ function TStorageControllerObj.MakeFleetList: Boolean;
else
begin
-
{Search through the entire circuit for enabled Storage Elements and add them to the list}
FStorageNameList.Clear;
FleetPointerList.Clear;
@@ -2203,7 +1584,7 @@ function TStorageControllerObj.MakeFleetList: Boolean;
if StorageObj.Enabled and (StorageObj.DispatchMode <> STORE_EXTERNALMODE) then
begin
FStorageNameList.Add(StorageObj.Name); // Add to list of names
- FleetPointerList.New := StorageObj;
+ FleetPointerList.Add(StorageObj);
end;
end;
@@ -2224,11 +1605,8 @@ function TStorageControllerObj.MakeFleetList: Boolean;
Result := TRUE;
FleetListChanged := FALSE;
-
end;
-
-//----------------------------------------------------------------------------
procedure TStorageControllerObj.Reset;
begin
// inherited;
@@ -2237,66 +1615,6 @@ procedure TStorageControllerObj.Reset;
// do we want to set fleet to 100% charged storage?
end;
-
-//----------------------------------------------------------------------------
-function TStorageControllerObj.ReturnElementsList: String;
-var
- i: Integer;
-begin
- if FleetSize = 0 then
- begin
- Result := '';
- Exit;
- end;
-
- Result := '[' + FStorageNameList.Strings[0];
- for i := 1 to FleetSize - 1 do
- begin
- Result := Result + ', ' + FStorageNameList.Strings[i];
- end;
- Result := Result + ']'; // terminate the array
-
-end;
-
-//----------------------------------------------------------------------------
-function TStorageControllerObj.ReturnSeasonTarget(THigh: Integer): String;
-var
- i: Integer;
-begin
- if Seasons = 1 then
- begin
- Result := '';
- Exit;
- end;
-
- Result := '[';
- for i := 0 to (Seasons - 1) do
- begin
- if THigh = 1 then
- Result := Result + format('%.6g', [SeasonTargets[i]]) + ', '
- else
- Result := Result + format('%.6g', [SeasonTargetsLow[i]]) + ', ';
- end;
- Result := Result + ']'; // terminate the array
-
-end;
-
-
-//----------------------------------------------------------------------------
-function TStorageControllerObj.ReturnWeightsList: String;
-begin
- if FleetSize = 0 then
- begin
- Result := '';
- Exit;
- end;
-
- Result := GetDSSArray_Real(FleetSize, FWeights);
-
-end;
-
-initialization
-
- CDoubleOne := Cmplx(1.0, 1.0);
-
+finalization ChargeModeEnum.Free;
+ DischargeModeEnum.Free;
end.
diff --git a/src/Controls/StorageController2.pas b/src/Controls/StorageController2.pas
index 439ff7a16..8140138fa 100644
--- a/src/Controls/StorageController2.pas
+++ b/src/Controls/StorageController2.pas
@@ -6,21 +6,19 @@
All rights reserved.
----------------------------------------------------------
}
-{
- A StorageController is a control element that is connected to a terminal of another
- circuit element and sends dispatch signals to a fleet of energy storage elements it controls
-
- A StorageController is defined by a New command:
-
- New StorageController.Name=myname Element=devclass.name terminal=[ 1|2|...] Elementlist = (elem1 elem2 ...)
- or ... ElementList = [File=filename] where storage class elements are listed one to a line
- If omitted, all storage elements found in the active circuit are included by default and controlled as a fleet.
-
- Added new control mode for charging 12/19/2018
- Proposed by Valentin Rigoni
-
-}
+// A StorageController is a control element that is connected to a terminal of another
+// circuit element and sends dispatch signals to a fleet of energy storage elements it controls
+//
+// A StorageController is defined by a New command:
+//
+// New StorageController.Name=myname Element=devclass.name terminal=[ 1|2|...] Elementlist = (elem1 elem2 ...)
+//
+// or ... ElementList = [File=filename] where storage class elements are listed one to a line
+// If omitted, all storage elements found in the active circuit are included by default and controlled as a fleet.
+//
+// Added new control mode for charging 12/19/2018
+// Proposed by Valentin Rigoni
interface
@@ -31,7 +29,7 @@ interface
CktElement,
DSSClass,
Arraydef,
- ucomplex,
+ UComplex, DSSUcomplex,
utilities,
DSSPointerList,
Classes,
@@ -43,27 +41,61 @@ interface
MINPHASE = -3;
type
+{$SCOPEDENUMS ON}
+ TStorageController2Prop = (
+ INVALID = 0,
+ Element = 1,
+ Terminal = 2,
+ MonPhase = 3,
+ kWTarget = 4,
+ kWTargetLow = 5,
+ pctkWBand = 6,
+ kWBand = 7,
+ pctkWBandLow = 8,
+ kWBandLow = 9,
+ ElementList = 10,
+ Weights = 11,
+ ModeDischarge = 12,
+ ModeCharge = 13,
+ TimeDischargeTrigger = 14,
+ TimeChargeTrigger = 15,
+ pctRatekW = 16,
+ pctRateCharge = 17,
+ pctReserve = 18,
+ kWhTotal = 19,
+ kWTotal = 20,
+ kWhActual = 21,
+ kWActual = 22,
+ kWneed = 23,
+ Yearly = 24,
+ Daily = 25,
+ Duty = 26,
+ EventLog = 27,
+ InhibitTime = 28,
+ Tup = 29,
+ TFlat = 30,
+ Tdn = 31,
+ kWThreshold = 32,
+ DispFactor = 33,
+ ResetLevel = 34,
+ Seasons = 35,
+ SeasonTargets = 36,
+ SeasonTargetsLow = 37
+ );
+{$SCOPEDENUMS OFF}
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TStorageController2 = class(TControlClass)
- PRIVATE
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const StorageController2Name: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit(): Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TStorageController2Obj = class(TControlElem)
PRIVATE
-
FkWTarget,
FkWTargetLow,
FkWThreshold,
@@ -73,10 +105,7 @@ TStorageController2Obj = class(TControlElem)
FkWBandLow,
HalfkWBand,
HalfkWBandLow,
-// FPFTarget, // Range on this is 0..2 where 1..2 is leading
TotalWeight,
-// HalfPFBand,
-// FPFBand,
UpRamptime,
FlatTime,
DnrampTime,
@@ -85,11 +114,10 @@ TStorageController2Obj = class(TControlElem)
DischargeTriggerTime,
ChargeTriggerTime,
pctKWRate,
-// pctkvarRate,
pctChargeRate,
LastpctDischargeRate,
- TotalkWCapacity,
- TotalkWhCapacity,
+ //TotalkWCapacity,
+ //TotalkWhCapacity,
pctFleetReserve,
ResetLevel,
kWNeeded,
@@ -104,7 +132,6 @@ TStorageController2Obj = class(TControlElem)
FleetListChanged,
ChargingAllowed,
-// DispatchVars,
DischargeTriggeredByTime,
DischargeInhibited,
OutOfOomph,
@@ -121,40 +148,24 @@ TStorageController2Obj = class(TControlElem)
FMonPhase,
CondOffset: Integer;
- YearlyShape: String; // ='fixed' means no variation on all the time
YearlyShapeObj: TLoadShapeObj; // Shape for this Storage element
- DailyShape: String; // Daily (24 HR) Storage element shape
DailyShapeObj: TLoadShapeObj; // Daily Storage element Shape for this load
- DutyShape: String; // Duty cycle load shape for changes typically less than one hour
DutyShapeObj: TLoadShapeObj; // Shape for this Storage element
LoadShapeMult: Complex;
- // PROCEDURE SetPctReserve;
procedure SetAllFleetValues;
procedure SetFleetkWRate(pctkw: Double);
-// PROCEDURE SetFleetkvarRate(pctkvar:Double);
procedure SetFleetChargeRate;
procedure SetFleetToCharge;
procedure SetFleetToDisCharge;
procedure SetFleetToIdle;
procedure SetFleetToExternal;
procedure SetFleetDesiredState(state: Integer);
- function InterpretMode(Opt: Integer; const S: String): Integer;
- function GetModeString(Opt, Mode: Integer): String;
- function GetkWTotal(var Sum: Double): String;
- function GetkWhTotal(var Sum: Double): String;
- function GetkWhActual: String;
- function GetkWActual: String;
-
procedure CalcYearlyMult(Hr: Double);
procedure CalcDailyMult(Hr: Double);
procedure CalcDutyMult(Hr: Double);
- function ReturnSeasonTarget(THigh: Integer): String;
- function ReturnElementsList: String;
- function ReturnWeightsList: String;
-
function MakeFleetList: Boolean;
procedure DoLoadFollowMode();
procedure DoLoadShapeMode();
@@ -165,7 +176,6 @@ TStorageController2Obj = class(TControlElem)
function NormalizeToTOD(h: Integer; sec: Double): Double;
procedure GetControlPower(var ControlPower: Complex);
procedure GetControlCurrent(var ControlCurrent: Double);
-// procedure Set_PFBand(const Value: Double);
function Get_FleetkW: Double;
function Get_FleetkWh: Double;
function Get_FleetkWhRating: Double;
@@ -174,37 +184,25 @@ TStorageController2Obj = class(TControlElem)
function Get_DynamicTarget(THigh: Integer): Double;
PUBLIC
-
constructor Create(ParClass: TDSSClass; const StorageController2Name: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
procedure RecalcElementData(); OVERRIDE;
- procedure CalcYPrim(); OVERRIDE; // Always Zero for a StorageController
procedure Sample(); OVERRIDE; // Sample control quantities and set action times in Control Queue
procedure DoPendingAction(const Code, ProxyHdl: Integer); OVERRIDE; // Do the action that is pending from last sample
procedure Reset; OVERRIDE; // Reset to initial defined state
- procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present value of terminal Curr
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
-
-// Property PFBand :Double Read FPFBand Write Set_PFBand;
- property FleetkW: Double READ Get_FleetkW;
- property FleetkWh: Double READ Get_FleetkWh;
property FleetkWhRating: Double READ Get_FleetkWhRating;
property FleetReservekWh: Double READ Get_FleetReservekWh;
-
end;
-{--------------------------------------------------------------------------}
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Circuit,
@@ -220,52 +218,11 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TStorageController2Obj;
+ TProp = TStorageController2Prop;
const
-
- propELEMENT = 1;
- propTERMINAL = 2;
- propMONPHASE = 3;
- propKWTARGET = 4;
- propKWTARGETLOW = 5;
- propPCTKWBAND = 6;
- propKWBAND = 7;
- propPCTKWBANDLOW = 8;
- propKWBANDLOW = 9;
-// propPFTARGET = 7;
-// propPFBAND = 8;
- propELEMENTLIST = 10;
- propWEIGHTS = 11;
- propMODEDISCHARGE = 12;
- propMODECHARGE = 13;
- propTIMEDISCHARGETRIGGER = 14;
- propTIMECHARGETRIGGER = 15;
- propRATEKW = 16;
-// propRATEKVAR = 16;
- propRATECHARGE = 17;
- propRESERVE = 18;
- propKWHTOTAL = 19;
- propKWTOTAL = 20;
- propKWHACTUAL = 21;
- propKWACTUAL = 22;
- propKWNEED = 23;
-// propPARTICIPATION = 24;
- propYEARLY = 24;
- propDAILY = 25;
- propDUTY = 26;
- propEVENTLOG = 27;
-// propVARDISPATCH = 29;
- propINHIBITTIME = 28;
- propTUPRAMP = 29;
- propTFLAT = 30;
- propTDNRAMP = 31;
- propKWTHRESHOLD = 32;
- propDispFactor = 33;
- propRESETLEVEL = 34;
- propSEASONS = 35;
- propSEASONTARGETS = 36;
- propSEASONTARGETSLOW = 37;
-
- NumPropsThisClass = 37;
+ NumPropsThisClass = Ord(High(TProp));
//= = = = = = = = = = = = = = DEFINE CONTROL MODE CONSTANTS = = = = = = = = = = = = = = = = = = = = = = = = =
@@ -284,623 +241,370 @@ implementation
type
TStorageObj = TStorage2Obj;
-
var
- CDoubleOne: Complex;
+ PropInfo: Pointer = NIL;
+ ChargeModeEnum, DischargeModeEnum: TDSSEnum;
-{--------------------------------------------------------------------------}
-constructor TStorageController2.Create(dssContext: TDSSContext); // Creates superstructure for all StorageController objects
+constructor TStorageController2.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ DischargeModeEnum := TDSSEnum.Create('StorageController: Discharge mode', False, 1, 2,
+ ['Peakshave', 'Follow', 'Support', 'Loadshape', 'Time', 'Schedule', 'I-Peakshave'],
+ [MODEPEAKSHAVE, MODEFOLLOW, MODESUPPORT, MODELOADSHAPE, MODETIME, MODESCHEDULE, CURRENTPEAKSHAVE]);
+ ChargeModeEnum := TDSSEnum.Create('StorageController: Charge mode', False, 1, 1,
+ ['Loadshape', 'Time', 'PeakshaveLow', 'I-PeakshaveLow'],
+ [MODELOADSHAPE, MODETIME, MODEPEAKSHAVELOW, CURRENTPEAKSHAVELOW]);
+ end;
+ inherited Create(dssContext, Storage_CONTROL, 'StorageController');
+end;
- Class_name := 'StorageController';
- DSSClassType := DSSClassType + Storage_CONTROL;
+destructor TStorageController2.Destroy;
+begin
+ inherited Destroy;
+end;
- DefineProperties;
+function GetkWActual(Obj: TObj): Double;
+begin
+ Result := Obj.Get_FleetkW();
+end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+function GetkWhActual(Obj: TObj): Double;
+begin
+ Result := Obj.Get_FleetkWh();
end;
-{--------------------------------------------------------------------------}
-destructor TStorageController2.Destroy;
+function GetkWhTotal(Obj: TObj): Double;
+var
+ pStorage: TStorageObj;
+ i: Integer;
+begin
+ Result := 0.0;
+ with obj do
+ for i := 1 to FleetPointerList.Count do
+ begin
+ pStorage := FleetPointerList.Get(i);
+ Result := Result + pStorage.StorageVars.kWhRating;
+ end;
+end;
+function GetkWTotal(Obj: TObj): Double;
+var
+ pStorage: TStorageObj;
+ i: Integer;
begin
- inherited Destroy;
+ Result := 0.0;
+ with obj do
+ for i := 1 to FleetPointerList.Count do
+ begin
+ pStorage := FleetPointerList.Get(i);
+ Result := Result + pStorage.StorageVars.kWRating;
+ end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TStorageController2.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
-
- PropertyName[propELEMENT] := 'Element';
- PropertyName[propTERMINAL] := 'Terminal';
- PropertyName[propMONPHASE] := 'MonPhase';
- PropertyName[propKWTARGET] := 'kWTarget';
- PropertyName[propKWTARGETLOW] := 'kWTargetLow';
- PropertyName[propPCTKWBAND] := '%kWBand';
- PropertyName[propKWBAND] := 'kWBand';
- PropertyName[propPCTKWBANDLOW] := '%kWBandLow';
- PropertyName[propKWBANDLOW] := 'kWBandLow';
-// PropertyName[propPFTARGET] := 'PFTarget';
-// PropertyName[propPFBAND] := 'PFBand';
- PropertyName[propELEMENTLIST] := 'ElementList';
- PropertyName[propWEIGHTS] := 'Weights';
- PropertyName[propMODEDISCHARGE] := 'ModeDischarge';
- PropertyName[propMODECHARGE] := 'ModeCharge';
- PropertyName[propTIMEDISCHARGETRIGGER] := 'TimeDischargeTrigger';
- PropertyName[propTIMECHARGETRIGGER] := 'TimeChargeTrigger';
- PropertyName[propRATEKW] := '%RatekW';
-// PropertyName[propRATEKVAR] := '%Ratekvar';
- PropertyName[propRATECHARGE] := '%RateCharge';
- PropertyName[propRESERVE] := '%Reserve';
- PropertyName[propKWHTOTAL] := 'kWhTotal';
- PropertyName[propKWTOTAL] := 'kWTotal';
- PropertyName[propKWHACTUAL] := 'kWhActual';
- PropertyName[propKWACTUAL] := 'kWActual';
- PropertyName[propKWNEED] := 'kWneed';
-// PropertyName[propPARTICIPATION] := '%Participation';
- PropertyName[propYEARLY] := 'Yearly';
- PropertyName[propDAILY] := 'Daily';
- PropertyName[propDUTY] := 'Duty';
- PropertyName[propEVENTLOG] := 'EventLog';
-// PropertyName[propVARDISPATCH] := 'VarDispatch';
- PropertyName[propINHIBITTIME] := 'InhibitTime';
- PropertyName[propTUPRAMP] := 'Tup';
- PropertyName[propTFLAT] := 'TFlat';
- PropertyName[propTDNRAMP] := 'Tdn';
- PropertyName[propKWTHRESHOLD] := 'kWThreshold';
- PropertyName[propDispFactor] := 'DispFactor';
- PropertyName[propRESETLEVEL] := 'ResetLevel';
- PropertyName[propSEASONS] := 'Seasons';
- PropertyName[propSEASONTARGETS] := 'SeasonTargets';
- PropertyName[propSEASONTARGETSLOW] := 'SeasonTargetsLow';
-
- PropertyHelp[propELEMENT] :=
- 'Full object name of the circuit element, typically a line or transformer, ' +
- 'which the control is monitoring. There is no default; Must be specified.';
- PropertyHelp[propTERMINAL] :=
- 'Number of the terminal of the circuit element to which the StorageController2 control is connected. ' +
- '1 or 2, typically. Default is 1. Make sure to select the proper direction on the power for the respective dispatch mode.';
- PropertyHelp[propMONPHASE] :=
- 'Number of the phase being monitored or one of {AVG | MAX | MIN} for all phases. Default=MAX. ' +
- 'Must be less than the number of phases. Used in PeakShave, Follow, Support and I-PeakShave discharging modes ' +
- 'and in PeakShaveLow, I-PeakShaveLow charging modes. For modes based on active power measurements, the value ' +
- 'used by the control is the monitored one multiplied by the number of phases of the monitored element.';
- PropertyHelp[propKWTARGET] :=
- 'kW/kamps target for Discharging. The Storage element fleet is dispatched to try to hold the power/current in band ' +
- 'at least until the Storage is depleted. The selection of power or current depends on the Discharge mode (PeakShave->kW, I-PeakShave->kamps).';
- PropertyHelp[propKWTARGETLOW] :=
- 'kW/kamps target for Charging. The Storage element fleet is dispatched to try to hold the power/current in band ' +
- 'at least until the Storage is fully charged. The selection of power or current depends on the charge mode (PeakShavelow->kW, I-PeakShavelow->kamps).';
- PropertyHelp[propPCTKWBAND] :=
- 'Bandwidth (% of Target kW/kamps) of the dead band around the kW/kamps target value. Default is 2% (+/-1%).' +
- 'No dispatch changes are attempted if the power in the monitored terminal stays within this band.';
- PropertyHelp[propKWBAND] :=
- 'Alternative way of specifying the bandwidth. (kW/kamps) of the dead band around the kW/kamps target value. Default is 2% of kWTarget (+/-1%).' +
- 'No dispatch changes are attempted if the power in the monitored terminal stays within this band.';
- PropertyHelp[propPCTKWBANDLOW] :=
- 'Bandwidth (% of kWTargetLow) of the dead band around the kW/kamps low target value. Default is 2% (+/-1%).' +
- 'No charging is attempted if the power in the monitored terminal stays within this band.';
- PropertyHelp[propKWBANDLOW] :=
- 'Alternative way of specifying the bandwidth. (kW/kamps) of the dead band around the kW/kamps low target value. Default is 2% of kWTargetLow (+/-1%).' +
- 'No charging is attempted if the power in the monitored terminal stays within this band.';
-// PropertyHelp[propPFTARGET] :=
-// 'Power Factor target for dispatching the reactive power. Default is 0.96. The reactive power of the storage element fleet is dispatched to try to hold the power factor in band. '+
-// 'It is assumed that the storage element inverter can produce kvar up to its kVA limit regardless of storage level.';
-// PropertyHelp[propPFBAND] :=
-// 'Bandwidth of the Target power factor of the monitored element. of the dead band around the kvar target value. Default is 0.04 (+/- 0.02).' +
-// 'No dispatch changes of the kvar are attempted If the power factor of the monitored terminal stays within this band.';
- PropertyHelp[propELEMENTLIST] :=
- 'Array list of Storage elements to be controlled. If not specified, all Storage elements in the circuit not presently dispatched by another controller ' +
- 'are assumed dispatched by this controller.';
- PropertyHelp[propWEIGHTS] :=
- 'Array of proportional weights corresponding to each Storage element in the ElementList. ' +
- 'The needed kW or kvar to get back to center band is dispatched to each Storage element according to these weights. ' +
- 'Default is to set all weights to 1.0.';
- PropertyHelp[propMODEDISCHARGE] :=
- '{PeakShave* | Follow | Support | Loadshape | Time | Schedule | I-PeakShave} Mode of operation for the DISCHARGE FUNCTION of this controller. ' +
- CRLF + CRLF + 'In PeakShave mode (Default), the control attempts to discharge Storage to keep power in the monitored element below the kWTarget. ' +
- CRLF + CRLF + 'In Follow mode, the control is triggered by time and resets the kWTarget value to the present monitored element power. ' +
- 'It then attempts to discharge Storage to keep power in the monitored element below the new kWTarget. See TimeDischargeTrigger.' +
- CRLF + CRLF + 'In Support mode, the control operates oppositely of PeakShave mode: Storage is discharged to keep kW power output up near the target. ' +
- CRLF + CRLF + 'In Loadshape mode, both charging and discharging precisely follows the per unit loadshape. ' +
- 'Storage is discharged when the loadshape value is positive. ' +
- CRLF + CRLF + 'In Time mode, the Storage discharge is turned on at the specified %RatekW at the specified discharge trigger time in fractional hours.' +
- CRLF + CRLF + 'In Schedule mode, the Tup, TFlat, and Tdn properties specify the up ramp duration, flat duration, and down ramp duration for the schedule. ' +
- 'The schedule start time is set by TimeDischargeTrigger and the rate of discharge for the flat part is determined by %RatekW.' +
- CRLF + CRLF + 'In I-PeakShave mode, the control attempts to discharge Storage to keep current in the monitored element below the target given in k-amps ' +
- '(thousands of amps), when this control mode is active, the property kWTarget will be expressed in k-amps. ';
- PropertyHelp[propMODECHARGE] :=
- '{Loadshape | Time* | PeakShaveLow | I-PeakShaveLow} Mode of operation for the CHARGE FUNCTION of this controller. ' +
- CRLF + CRLF + 'In Loadshape mode, both charging and discharging precisely follows the per unit loadshape. ' +
- 'Storage is charged when the loadshape value is negative. ' +
- CRLF + CRLF + 'In Time mode, the Storage charging FUNCTION is triggered at the specified %RateCharge at the specified charge trigger time in fractional hours.' +
- CRLF + CRLF + 'In PeakShaveLow mode, the charging operation will charge the Storage fleet when the power at a' +
- 'monitored element is below a specified KW target (kWTarget_low). The Storage will charge as much power as necessary to keep the power within the deadband around kWTarget_low.' +
- CRLF + CRLF + 'In I-PeakShaveLow mode, the charging operation will charge the Storage fleet when the current (Amps) at a' +
- 'monitored element is below a specified amps target (kWTarget_low). The Storage will charge as much power as necessary to keep the amps within the deadband around kWTarget_low. ' +
- 'When this control mode is active, the property kWTarget_low will be expressed in k-amps and all the other parameters will be adjusted to match the amps (current) control criteria.';
- PropertyHelp[propTIMEDISCHARGETRIGGER] :=
- 'Default time of day (hr) for initiating Discharging of the fleet. During Follow or Time mode discharging is triggered at a fixed time ' +
- 'each day at this hour. If Follow mode, Storage will be discharged to attempt to hold the load at or below the power level at the time of triggering. ' +
- 'In Time mode, the discharge is based on the %RatekW property value. ' +
- 'Set this to a negative value to ignore. Default is 12.0 for Follow mode; otherwise it is -1 (ignored). ';
- PropertyHelp[propTIMECHARGETRIGGER] :=
- 'Default time of day (hr) for initiating charging in Time control mode. Set this to a negative value to ignore. Default is 2.0. (0200).' +
- 'When this value is >0 the Storage fleet is set to charging at this time regardless of other control criteria to make sure Storage is ' +
- 'topped off for the next discharge cycle.';
- PropertyHelp[propRATEKW] :=
- 'Sets the kW discharge rate in % of rated capacity for each element of the fleet. Applies to TIME control mode, SCHEDULE mode, or anytime discharging is triggered ' +
- 'by time.';
-// PropertyHelp[propRATEKVAR] :=
-// 'Sets the kvar discharge rate in % of rated capacity for each element of the fleet. Applies to TIME control mode or anytime discharging is triggered ' +
-// 'by time.' ;
- PropertyHelp[propRATECHARGE] :=
- 'Sets the kW charging rate in % of rated capacity for each element of the fleet. Applies to TIME control mode and anytime charging mode is ' +
- 'entered due to a time trigger.';
- PropertyHelp[propRESERVE] :=
- 'Use this property to change the % reserve for each Storage element under control of this controller. This might be used, for example, to ' +
- 'allow deeper discharges of Storage or in case of emergency operation to use the remainder of the Storage element.';
- PropertyHelp[propKWHTOTAL] :=
- '(Read only). Total rated kWh energy Storage capacity of Storage elements controlled by this controller.';
- PropertyHelp[propKWTOTAL] :=
- '(Read only). Total rated kW power capacity of Storage elements controlled by this controller.';
- PropertyHelp[propKWHACTUAL] :=
- '(Read only). Actual kWh stored of all controlled Storage elements. ';
- PropertyHelp[propKWACTUAL] :=
- '(Read only). Actual kW output of all controlled Storage elements. ';
- PropertyHelp[propKWNEED] :=
- '(Read only). KW needed to meet target.';
-// PropertyHelp[propPARTICIPATION] :=
-// 'Participation factor, %. Default = 100.';
- PropertyHelp[propYEARLY] :=
- 'Dispatch loadshape object, If any, for Yearly solution Mode.';
- PropertyHelp[propDAILY] :=
- 'Dispatch loadshape object, If any, for Daily solution mode.';
- PropertyHelp[propDUTY] :=
- 'Dispatch loadshape object, If any, for Dutycycle solution mode.';
- PropertyHelp[propEVENTLOG] :=
- '{Yes/True | No/False} Default is No. Log control actions to Eventlog.';
-// PropertyHelp[propVARDISPATCH] :=
-// '{Yes/True | No/False} Default is No. Flag to indicate whether or not to disatch vars as well as watts.';
- PropertyHelp[propINHIBITTIME] :=
- 'Hours (integer) to inhibit Discharging after going into Charge mode. Default is 5.';
- PropertyHelp[propTUPRAMP] := 'Duration, hrs, of upramp part for SCHEDULE mode. Default is 0.25.';
- PropertyHelp[propTFLAT] := 'Duration, hrs, of flat part for SCHEDULE mode. Default is 2.0.';
- PropertyHelp[propTDNRAMP] := 'Duration, hrs, of downramp part for SCHEDULE mode. Default is 0.25.';
- PropertyHelp[propKWTHRESHOLD] := 'Threshold, kW, for Follow mode. kW has to be above this value for the Storage element ' +
- 'to be dispatched on. Defaults to 75% of the kWTarget value. Must reset this property after ' +
- 'setting kWTarget if you want a different value.';
- PropertyHelp[propDispFactor] := 'Defaults to 1 (disabled). Set to any value between 0 and 1 to enable this parameter.' + CRLF + CRLF + 'Use this parameter to reduce the amount of power requested by the controller in each control iteration. ' +
- 'It can be useful when maximum control iterations are exceeded due to numerical instability such as ' +
- 'fleet being set to charging and idling in subsequent control iterations (check the Eventlog). ';
- PropertyHelp[propRESETLEVEL] := 'The level of charge required for allowing the storage to discharge again after reaching ' +
- 'the reserve storage level. After reaching this level, the storage control will not allow ' +
- 'the storage device to discharge, forcing the storage to charge. Once the storage reaches this' +
- 'level, the storage will be able to discharge again. This value is a number between 0.2 and 1';
- PropertyHelp[propSEASONS] := 'With this property the user can' +
- ' specify the number of targets to be used by the controller using the list given at "SeasonTargets"/' +
- '"SeasonTargetsLow", which can be used to dynamically adjust the storage controller during a QSTS' +
- ' simulation. The default value is 1. This property needs to be defined before defining SeasonTargets/SeasonTargetsLow.';
- PropertyHelp[propSEASONTARGETS] := 'An array of doubles specifying the targets to be used during a QSTS simulation. These targets will take effect' +
- ' only if SeasonRating=true. The number of targets cannot exceed the number of seasons defined at the SeasonSignal.' +
- 'The difference between the targets defined at SeasonTargets and SeasonTargetsLow is that SeasonTargets' +
- ' applies to discharging modes, while SeasonTargetsLow applies to charging modes.';
- PropertyHelp[propSEASONTARGETSLOW] := 'An array of doubles specifying the targets to be used during a QSTS simulation. These targets will take effect' +
- ' only if SeasonRating=true. The number of targets cannot exceed the number of seasons defined at the SeasonSignal.' +
- 'The difference between the targets defined at SeasonTargets and SeasonTargetsLow is that SeasonTargets' +
- ' applies to discharging modes, while SeasonTargetsLow applies to charging modes.';
-
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enum properties
+ PropertyType[ord(TProp.ModeDischarge)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.ModeDischarge)] := ptruint(@obj.DischargeMode);
+ PropertyOffset2[ord(TProp.ModeDischarge)] := PtrInt(DischargeModeEnum);
+
+ PropertyType[ord(TProp.ModeCharge)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.ModeCharge)] := ptruint(@obj.ChargeMode);
+ PropertyOffset2[ord(TProp.ModeCharge)] := PtrInt(ChargeModeEnum);
+
+ PropertyType[ord(TProp.MonPhase)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.MonPhase)] := ptruint(@obj.FMonPhase);
+ PropertyOffset2[ord(TProp.MonPhase)] := PtrInt(DSS.MonPhaseEnum);
+
+ // boolean properties
+ PropertyType[ord(TProp.EventLog)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.EventLog)] := ptruint(@obj.ShowEventLog);
+
+ // string lists
+ PropertyType[ord(TProp.ElementList)] := TPropertyType.StringListProperty;
+ PropertyOffset[ord(TProp.ElementList)] := ptruint(@obj.FStorageNameList);
+
+ // objects
+ PropertyType[ord(TProp.yearly)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.daily)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.duty)] := TPropertyType.DSSObjectReferenceProperty;
+
+ PropertyOffset[ord(TProp.yearly)] := ptruint(@obj.YearlyShapeObj);
+ PropertyOffset[ord(TProp.daily)] := ptruint(@obj.DailyShapeObj);
+ PropertyOffset[ord(TProp.duty)] := ptruint(@obj.DutyShapeObj);
+
+ PropertyOffset2[ord(TProp.yearly)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.daily)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.duty)] := ptruint(DSS.LoadShapeClass);
+
+ PropertyType[ord(TProp.Element)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.Element)] := ptruint(@obj.FMonitoredElement);
+ PropertyWriteFunction[ord(TProp.Element)] := @SetMonitoredElement;
+ PropertyFlags[ord(TProp.Element)] := [TPropertyFlag.WriteByFunction];//[TPropertyFlag.CheckForVar]; // not required for general cktelements
+
+ // integer properties
+ PropertyType[ord(TProp.Terminal)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Terminal)] := ptruint(@obj.ElementTerminal);
+
+ PropertyType[ord(TProp.Seasons)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Seasons)] := ptruint(@obj.Seasons);
+
+ PropertyType[ord(TProp.InhibitTime)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.InhibitTime)] := ptruint(@obj.Inhibithrs); // >=1, silent, handled as side effect
+
+ // double arrays
+ PropertyType[ord(TProp.SeasonTargets)] := TPropertyType.DoubleDArrayProperty;
+ PropertyOffset[ord(TProp.SeasonTargets)] := ptruint(@obj.SeasonTargets);
+ PropertyOffset2[ord(TProp.SeasonTargets)] := ptruint(@obj.Seasons);
+
+ PropertyType[ord(TProp.SeasonTargetsLow)] := TPropertyType.DoubleDArrayProperty;
+ PropertyOffset[ord(TProp.SeasonTargetsLow)] := ptruint(@obj.SeasonTargetsLow);
+ PropertyOffset2[ord(TProp.SeasonTargetsLow)] := ptruint(@obj.Seasons);
+
+ PropertyType[ord(TProp.Weights)] := TPropertyType.DoubleDArrayProperty;
+ PropertyOffset[ord(TProp.Weights)] := ptruint(@obj.FWeights);
+ PropertyOffset2[ord(TProp.Weights)] := ptruint(@obj.FleetSize);
+
+ // double functions
+ PropertyFlags[ord(TProp.kWhTotal)] := [TPropertyFlag.SilentReadOnly, TPropertyFlag.ReadByFunction];
+ PropertyFlags[ord(TProp.kWTotal)] := [TPropertyFlag.SilentReadOnly, TPropertyFlag.ReadByFunction];
+ PropertyFlags[ord(TProp.kWhActual)] := [TPropertyFlag.SilentReadOnly, TPropertyFlag.ReadByFunction];
+ PropertyFlags[ord(TProp.kWActual)] := [TPropertyFlag.SilentReadOnly, TPropertyFlag.ReadByFunction];
+ PropertyReadFunction[ord(TProp.kWhTotal)] := @GetkWhTotal;
+ PropertyReadFunction[ord(TProp.kWTotal)] := @GetkWTotal;
+ PropertyReadFunction[ord(TProp.kWhActual)] := @GetkWhActual;
+ PropertyReadFunction[ord(TProp.kWActual)] := @GetkWActual;
+
+ // double read-only
+ PropertyFlags[ord(TProp.kWneed)] := [TPropertyFlag.SilentReadOnly];
+ PropertyOffset[ord(TProp.kWneed)] := ptruint(@obj.kWNeeded);
+
+ // double properties
+ PropertyOffset[ord(TProp.DispFactor)] := ptruint(@obj.DispFactor);
+ //PropertyFlags[ord(TProp.DispFactor)] := [TPropertyFlag.NotNegative, TPropertyFlag.NotZero];
+ // >0,<=1
+
+ PropertyOffset[ord(TProp.kWTarget)] := ptruint(@obj.FkWTarget);
+ PropertyOffset[ord(TProp.kWTargetLow)] := ptruint(@obj.FkWTargetLow);
+ PropertyOffset[ord(TProp.kWBand)] := ptruint(@obj.FkWBand);
+ PropertyOffset[ord(TProp.pctkWBand)] := ptruint(@obj.FpctkWBand);
+ PropertyOffset[ord(TProp.pctkWBandLow)] := ptruint(@obj.FpctkWBandLow);
+ PropertyOffset[ord(TProp.kWBandLow)] := ptruint(@obj.FkWBandLow);
+ PropertyOffset[ord(TProp.TimeDischargeTrigger)] := ptruint(@obj.DischargeTriggerTime);
+ PropertyOffset[ord(TProp.TimeChargeTrigger)] := ptruint(@obj.ChargeTriggerTime);
+ PropertyOffset[ord(TProp.pctRatekW)] := ptruint(@obj.pctkWRate);
+ PropertyOffset[ord(TProp.pctRateCharge)] := ptruint(@obj.pctChargeRate);
+ PropertyOffset[ord(TProp.pctReserve)] := ptruint(@obj.pctFleetReserve);
+ PropertyOffset[ord(TProp.Tup)] := ptruint(@obj.UpRamptime);
+ PropertyOffset[ord(TProp.TFlat)] := ptruint(@obj.FlatTime);
+ PropertyOffset[ord(TProp.Tdn)] := ptruint(@obj.DnrampTime);
+ PropertyOffset[ord(TProp.kWThreshold)] := ptruint(@obj.FkWThreshold);
+ PropertyOffset[ord(TProp.ResetLevel)] := ptruint(@obj.ResetLevel);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ inherited DefineProperties;
end;
-{--------------------------------------------------------------------------}
-function TStorageController2.NewObject(const ObjName: String): Integer;
+function TStorageController2.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new StorageController and add it to StorageController class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TStorageController2Obj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-{--------------------------------------------------------------------------}
-function TStorageController2.Edit(): Integer;
+procedure TStorageController2Obj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
i: Integer;
casemult: Double;
-
begin
-
- // continue parsing with contents of Parser
- DSS.ActiveStorageController2Obj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveStorageController2Obj;
-
- Result := 0;
-
- with DSS.ActiveStorageController2Obj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
+ case Idx of
+ ord(TProp.kWTarget):
begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 14407);
- propELEMENT:
- ElementName := lowercase(param);
- propTERMINAL:
- ElementTerminal := Parser.IntValue;
- propMONPHASE:
- if CompareTextShortest(param, 'avg') = 0 then
- FMonPhase := AVG
- else
- if CompareTextShortest(param, 'max') = 0 then
- FMonPhase := MAXPHASE
- else
- if CompareTextShortest(param, 'min') = 0 then
- FMonPhase := MINPHASE
- else
- FMonPhase := max(1, Parser.IntValue);
- propKWTARGET:
- FkWTarget := Parser.DblValue;
- propKWTARGETLOW:
- FkWTargetLow := Parser.DblValue;
- propPCTKWBAND:
- FpctkWBand := Parser.DblValue;
- propKWBAND:
- FkWBand := Parser.DblValue;
- propPCTKWBANDLOW:
- FpctkWBandLow := Parser.DblValue;
- propKWBANDLOW:
- FkWBandLow := Parser.DblValue;
-// propPFTARGET: FPFTarget := ConvertPFToPFRange2(Parser.DblValue);
-// propPFBAND: FPFBand := Parser.DblValue;
- propELEMENTLIST:
- InterpretTStringListArray(Param, FStorageNameList);
- propWEIGHTS:
- begin
- FleetSize := FStorageNameList.count;
- if FleetSize > 0 then
- begin
- Reallocmem(FWeights, Sizeof(FWeights^[1]) * FleetSize);
- FleetSize := InterpretDblArray(Param, FleetSize, FWeights);
- end;
- end;
- propMODEDISCHARGE:
- DisChargeMode := InterpretMode(propMODEDISCHARGE, Param);
- propMODECHARGE:
- ChargeMode := InterpretMode(propMODECHARGE, Param);
- propTIMEDISCHARGETRIGGER:
- DischargeTriggerTime := Parser.DblValue;
- propTIMECHARGETRIGGER:
- ChargeTriggerTime := Parser.DblValue;
- propRATEKW:
- pctkWRate := Parser.DblValue;
-// propRATEKVAR: pctkvarRate := Parser.DblValue;
- propRATECHARGE:
- pctChargeRate := Parser.DblValue;
- propRESERVE:
- pctFleetReserve := Parser.DblValue;
- propKWHTOTAL: ; // Do nothing (Read ONly)
- propKWTOTAL: ; // Do nothing (Read ONly)
- propKWHACTUAL: ; // Do nothing (Read ONly)
- propKWACTUAL: ; // Do nothing (Read ONly)
- propKWNEED: ; // Do nothing (Read ONly)
-// propPARTICIPATION: ;
- propYEARLY:
- YearlyShape := Param;
- propDAILY:
- DailyShape := Param;
- propDUTY:
- DutyShape := Param;
- propEVENTLOG:
- ShowEventLog := InterpretYesNo(Param);
-// propVARDISPATCH: DispatchVars := InterpretYesNo(Param);
- propINHIBITTIME:
- Inhibithrs := Max(1, Parser.IntValue); // >=1
- propTUPRAMP:
- UpRamptime := Parser.DblValue;
- propTFLAT:
- FlatTime := Parser.DblValue;
- propTDNRAMP:
- DnrampTime := Parser.DblValue;
- propKWTHRESHOLD:
- FkWThreshold := Parser.DblValue;
- propDispFactor:
- if (Parser.DblValue > 1.0) or (Parser.DblValue <= 0.0) then
- DispFactor := 1.0
- else
- DispFactor := Parser.DblValue;
- propRESETLEVEL:
- ResetLevel := Parser.DblValue;
- propSEASONS:
- Seasons := Parser.IntValue;
- propSEASONTARGETS:
- begin
- if Seasons > 1 then
- begin
- setlength(SeasonTargets, Seasons);
- Seasons := InterpretDblArray(Param, Seasons, Pointer(SeasonTargets));
- end;
- end;
- propSEASONTARGETSLOW:
- begin
- if Seasons > 1 then
- begin
- setlength(SeasonTargetsLow, Seasons);
- Seasons := InterpretDblArray(Param, Seasons, Pointer(SeasonTargetsLow));
- end;
- end;
-
+ if DischargeMode = CURRENTPEAKSHAVE then // evaluates the discharging mode to apply
+ Casemult := 1000.0 // a compensation value (for kamps)
else
- // Inherited parameters
- ClassEdit(DSS.ActiveStorageController2Obj, ParamPointer - NumPropsthisClass)
- end;
-
- // Side effects of setting properties above
-
- case ParamPointer of
- propKWTARGET:
- begin
- if DischargeMode = CURRENTPEAKSHAVE then // evaluates the discharging mode to apply
- Casemult := 1000.0 // a compensation value (for kamps)
- else
- Casemult := 1.0;
-
- FkWThreshold := FkWTarget * 0.75 * Casemult;
-
- HalfkWBand := FpctkWBand / 200.0 * FkWTarget * Casemult;
- FkWBand := 2.0 * HalfkWBand;
- FpctkWBand := FkWBand / FkWTarget * 100.0; // sync FpctkWBand
-
- end;
- propPCTKWBAND:
- begin
- if DischargeMode = CURRENTPEAKSHAVE then // evaluates the discharging mode to apply
- Casemult := 1000.0 // a compensation value (for kamps)
- else
- Casemult := 1.0;
-
- HalfkWBand := FpctkWBand / 200.0 * FkWTarget * Casemult;
- FkWBand := 2.0 * HalfkWBand;
- FkWBandSpecified := FALSE;
+ Casemult := 1.0;
- end;
- propKWBAND:
- begin
- if DischargeMode = CURRENTPEAKSHAVE then // evaluates the discharging mode to apply
- Casemult := 1000.0 // a compensation value (for kamps)
- else
- Casemult := 1.0;
+ FkWThreshold := FkWTarget * 0.75 * Casemult;
- HalfkWBand := FkWBand / 2.0 * Casemult;
- FpctkWBand := FkWBand / FkWTarget * 100.0; // sync FpctkWBand
- FkWBandSpecified := TRUE;
- end;
- propKWTARGETLOW,
- propPCTKWBANDLOW:
- begin
- if ChargeMode = CURRENTPEAKSHAVELOW then // evaluates the charging mode to apply
- Casemult := 1000.0 // a compensation value (for kamps)
- else
- Casemult := 1.0;
+ HalfkWBand := FpctkWBand / 200.0 * FkWTarget * Casemult;
+ FkWBand := 2.0 * HalfkWBand;
+ FpctkWBand := FkWBand / FkWTarget * 100.0; // sync FpctkWBand
- HalfkWBandLow := FpctkWBandLow / 200.0 * FkWTargetLow * Casemult;
- FkWBandLow := HalfkWBandLow * 2.0;
- end;
- propKWBANDLOW:
- begin
- if ChargeMode = CURRENTPEAKSHAVELOW then // evaluates the charging mode to apply
- Casemult := 1000.0 // a compensation value (for kamps)
- else
- Casemult := 1.0;
+ end;
+ ord(TProp.pctkWBand):
+ begin
+ if DischargeMode = CURRENTPEAKSHAVE then // evaluates the discharging mode to apply
+ Casemult := 1000.0 // a compensation value (for kamps)
+ else
+ Casemult := 1.0;
- HalfkWBandLow := FkWBandLow / 2.0 * Casemult;
- FpctkWBand := FkWBandLow / FkWTarget * 100.0; // sync FpctkWBandLow
- end;
-// propPFBAND: HalfPFBand := FPFBand / 2.0;
- propMODEDISCHARGE:
- if DischargeMode = MODEFOLLOW then
- DischargeTriggerTime := 12.0; // Noon
+ HalfkWBand := FpctkWBand / 200.0 * FkWTarget * Casemult;
+ FkWBand := 2.0 * HalfkWBand;
+ FkWBandSpecified := FALSE;
- propMONPHASE:
- if FMonPhase > FNphases then
- begin
- DoSimpleMsg(Format('Error: Monitored phase(%d) must be less than or equal to number of phases(%d). ', [FMonPhase, FNphases]), 35302);
- FMonPhase := 1;
- end;
+ end;
+ ord(TProp.kWBand):
+ begin
+ if DischargeMode = CURRENTPEAKSHAVE then // evaluates the discharging mode to apply
+ Casemult := 1000.0 // a compensation value (for kamps)
+ else
+ Casemult := 1.0;
- propELEMENTLIST:
- begin // levelize the list
- FleetPointerList.Clear; // clear this for resetting on first sample
- FleetListChanged := TRUE;
- FElementListSpecified := TRUE;
- FleetSize := FStorageNameList.count;
- // Realloc weights to be same size as possible number of storage elements
- Reallocmem(FWeights, Sizeof(FWeights^[1]) * FleetSize);
- for i := 1 to FleetSize do
- FWeights^[i] := 1.0;
- end;
- propYEARLY:
- begin
- YearlyShapeObj := DSS.LoadShapeClass.Find(YearlyShape);
- if YearlyShapeObj = NIL then
- DoSimpleMsg('Yearly loadshape "' + YearlyShape + '" not found.', 14404);
- end;
- propDAILY:
- begin
- DailyShapeObj := DSS.LoadShapeClass.Find(DailyShape);
- if DailyShapeObj = NIL then
- DoSimpleMsg('Daily loadshape "' + DailyShape + '" not found.', 14405);
- end;
- propDUTY:
- begin
- DutyShapeObj := DSS.LoadShapeClass.Find(DutyShape);
- if DutyShapeObj = NIL then
- DoSimpleMsg('Dutycycle loadshape "' + DutyShape + '" not found.', 14406);
- end
+ HalfkWBand := FkWBand / 2.0 * Casemult;
+ FpctkWBand := FkWBand / FkWTarget * 100.0; // sync FpctkWBand
+ FkWBandSpecified := TRUE;
+ end;
+ ord(TProp.kWTargetLow),
+ ord(TProp.pctkWBandLow):
+ begin
+ if ChargeMode = CURRENTPEAKSHAVELOW then // evaluates the charging mode to apply
+ Casemult := 1000.0 // a compensation value (for kamps)
+ else
+ Casemult := 1.0;
+ HalfkWBandLow := FpctkWBandLow / 200.0 * FkWTargetLow * Casemult;
+ FkWBandLow := HalfkWBandLow * 2.0;
+ end;
+ ord(TProp.kWBandLow):
+ begin
+ if ChargeMode = CURRENTPEAKSHAVELOW then // evaluates the charging mode to apply
+ Casemult := 1000.0 // a compensation value (for kamps)
else
+ Casemult := 1.0;
+
+ HalfkWBandLow := FkWBandLow / 2.0 * Casemult;
+ FpctkWBand := FkWBandLow / FkWTarget * 100.0; // sync FpctkWBandLow
+ end;
+ ord(TProp.ModeDischarge):
+ if DischargeMode = MODEFOLLOW then
+ DischargeTriggerTime := 12.0; // Noon
+ ord(TProp.MonPhase):
+ if FMonPhase > FNphases then
+ begin
+ DoSimpleMsg('Error: Monitored phase (%d) must be less than or equal to number of phases (%d). ', [FMonPhase, FNphases], 35302);
+ FMonPhase := 1;
end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ ord(TProp.ElementList):
+ begin // levelize the list
+ FleetPointerList.Clear; // clear this for resetting on first sample
+ FleetListChanged := TRUE;
+ FElementListSpecified := TRUE;
+ FleetSize := FStorageNameList.count;
+ // Realloc weights to be same size as possible number of storage elements
+ Reallocmem(FWeights, Sizeof(FWeights^[1]) * FleetSize);
+ for i := 1 to FleetSize do
+ FWeights^[i] := 1.0;
end;
-
- RecalcElementData();
+ ord(TProp.Seasons):
+ begin
+ setlength(SeasonTargets, Seasons);
+ setlength(SeasonTargetsLow, Seasons);
+ end;
+ ord(TProp.DispFactor):
+ if (DispFactor <= 0) or (DispFactor > 1) then
+ DispFactor := 1;
+ ord(TProp.InhibitTime):
+ Inhibithrs := Max(1, Inhibithrs); // >=1, silent
end;
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-{--------------------------------------------------------------------------}
-function TStorageController2.MakeLike(const StorageController2Name: String): Integer;
+procedure TStorageController2Obj.MakeLike(OtherPtr: Pointer);
var
- OtherStorageController: TStorageController2Obj;
+ Other: TObj;
i: Integer;
begin
- Result := 0;
- {See If we can find this StorageController name in the present collection}
- OtherStorageController := Find(StorageController2Name);
- if OtherStorageController <> NIL then
- with DSS.ActiveStorageController2Obj do
- begin
-
- NPhases := OtherStorageController.Fnphases;
- NConds := OtherStorageController.Fnconds; // Force Reallocation of terminal stuff
-
- ElementName := OtherStorageController.ElementName;
- ControlledElement := OtherStorageController.ControlledElement; // Pointer to target circuit element
- MonitoredElement := OtherStorageController.MonitoredElement; // Pointer to target circuit element
- ElementTerminal := OtherStorageController.ElementTerminal;
- FMonPhase := OtherStorageController.FMonPhase;
- CondOffset := OtherStorageController.CondOffset;
-
- FkWTarget := OtherStorageController.FkWTarget;
- FkWTargetLow := OtherStorageController.FkWTargetLow;
- FkWThreshold := OtherStorageController.FkWThreshold;
-
- DispFactor := OtherStorageController.DispFactor;
-
- FpctkWBand := OtherStorageController.FpctkWBand;
- FkWBand := OtherStorageController.FkWBand;
- FpctkWBandLow := OtherStorageController.FpctkWBandLow;
- FkWBandLow := OtherStorageController.FkWBandLow;
-// FPFTarget := OtherStorageController.FPFTarget;
-// FPFBand := OtherStorageController.FPFBand;
-// HalfPFBand := OtherStorageController.HalfPFBand;
- ResetLevel := OtherStorageController.ResetLevel;
-
- FkWBandSpecified := OtherStorageController.FkWBandSpecified;
- FStorageNameList.Clear;
- for i := 1 to OtherStorageController.FStorageNameList.Count do
- FStorageNameList.Add(OtherStorageController.FStorageNameList.Strings[i - 1]);
-
- FleetSize := FStorageNameList.count;
- if FleetSize > 0 then
- begin
- Reallocmem(FWeights, Sizeof(FWeights^[1]) * FleetSize);
- for i := 1 to FleetSize do
- FWeights^[i] := OtherStorageController.FWeights^[i];
- end;
-
- DisChargeMode := OtherStorageController.DisChargeMode;
- ChargeMode := OtherStorageController.ChargeMode;
- DischargeTriggerTime := OtherStorageController.DischargeTriggerTime;
- ChargeTriggerTime := OtherStorageController.ChargeTriggerTime;
- pctkWRate := OtherStorageController.pctkWRate;
-// pctkvarRate := OtherStorageController.pctkvarRate;
- pctChargeRate := OtherStorageController.pctChargeRate;
- pctFleetReserve := OtherStorageController.pctFleetReserve;
- YearlyShape := OtherStorageController.YearlyShape;
- DailyShape := OtherStorageController.DailyShape;
- DutyShape := OtherStorageController.DutyShape;
-// DispatchVars := OtherStorageController.DispatchVars;
- ShowEventLog := OtherStorageController.ShowEventLog;
- Inhibithrs := OtherStorageController.Inhibithrs;
-
- UpRamptime := OtherStorageController.UpRamptime;
- FlatTime := OtherStorageController.FlatTime;
- DnrampTime := OtherStorageController.DnrampTime;
-
- Seasons := OtherStorageController.Seasons;
- if Seasons > 1 then
- begin
- setlength(SeasonTargets, Seasons);
- setlength(SeasonTargetsLow, Seasons);
- for i := 0 to (Seasons - 1) do
- begin
- SeasonTargets[i] := OtherStorageController.SeasonTargets[i];
- SeasonTargetsLow[i] := OtherStorageController.SeasonTargetsLow[i];
- end;
- end;
-
-
- //**** fill in private properties
-
- for i := 1 to ParentClass.NumProperties do
- // Skip Read only properties
- case i of
- propKWHTOTAL: ; {Do Nothing}
- propKWTOTAL: ; {Do Nothing}
- propKWHACTUAL: ; {Do Nothing}
- propKWACTUAL: ; {Do Nothing}
- propKWNEED: ; {Do Nothing}
- else
- PropertyValue[i] := OtherStorageController.PropertyValue[i];
- end;
-
-
- end
- else
- DoSimpleMsg('Error in StorageController2 MakeLike: "' + StorageController2Name + '" Not Found.', 370);
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
+
+ // ControlledElement := Other.ControlledElement; // Pointer to target circuit element
+ MonitoredElement := Other.MonitoredElement; // Pointer to target circuit element
+ ElementTerminal := Other.ElementTerminal;
+ FMonPhase := Other.FMonPhase;
+ CondOffset := Other.CondOffset;
+
+ FkWTarget := Other.FkWTarget;
+ FkWTargetLow := Other.FkWTargetLow;
+ FkWThreshold := Other.FkWThreshold;
+
+ DispFactor := Other.DispFactor;
+
+ FpctkWBand := Other.FpctkWBand;
+ FkWBand := Other.FkWBand;
+ FpctkWBandLow := Other.FpctkWBandLow;
+ FkWBandLow := Other.FkWBandLow;
+ ResetLevel := Other.ResetLevel;
+
+ FkWBandSpecified := Other.FkWBandSpecified;
+ FStorageNameList.Clear;
+ for i := 1 to Other.FStorageNameList.Count do
+ FStorageNameList.Add(Other.FStorageNameList.Strings[i - 1]);
+
+ FleetSize := FStorageNameList.count;
+ if FleetSize > 0 then
+ begin
+ Reallocmem(FWeights, Sizeof(FWeights^[1]) * FleetSize);
+ for i := 1 to FleetSize do
+ FWeights^[i] := Other.FWeights^[i];
+ end;
+ DisChargeMode := Other.DisChargeMode;
+ ChargeMode := Other.ChargeMode;
+ DischargeTriggerTime := Other.DischargeTriggerTime;
+ ChargeTriggerTime := Other.ChargeTriggerTime;
+ pctkWRate := Other.pctkWRate;
+ pctChargeRate := Other.pctChargeRate;
+ pctFleetReserve := Other.pctFleetReserve;
+ YearlyShapeObj := Other.YearlyShapeObj;
+ DailyShapeObj := Other.DailyShapeObj;
+ DutyShapeObj := Other.DutyShapeObj;
+ ShowEventLog := Other.ShowEventLog;
+ Inhibithrs := Other.Inhibithrs;
+
+ UpRamptime := Other.UpRamptime;
+ FlatTime := Other.FlatTime;
+ DnrampTime := Other.DnrampTime;
+
+ Seasons := Other.Seasons;
+ if Seasons > 1 then
+ begin
+ setlength(SeasonTargets, Seasons);
+ setlength(SeasonTargetsLow, Seasons);
+ for i := 0 to (Seasons - 1) do
+ begin
+ SeasonTargets[i] := Other.SeasonTargets[i];
+ SeasonTargetsLow[i] := Other.SeasonTargetsLow[i];
+ end;
+ end;
end;
-
-{==========================================================================}
-{ TStorageControllerObj }
-{==========================================================================}
-
-
-{--------------------------------------------------------------------------}
constructor TStorageController2Obj.Create(ParClass: TDSSClass; const StorageController2Name: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(StorageController2Name);
+ Name := AnsiLowerCase(StorageController2Name);
DSSObjType := ParClass.DSSClassType;
- NPhases := 3; // Directly set conds and phases
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 1; // this forces allocation of terminals and conductors
- ElementName := '';
ControlledElement := NIL; // not used in this control
ElementTerminal := 1;
MonitoredElement := NIL;
@@ -961,180 +665,19 @@ constructor TStorageController2Obj.Create(ParClass: TDSSClass; const StorageCont
SeasonTargets[0] := FkWTarget;
setlength(SeasonTargetsLow, 1);
SeasonTargetsLow[0] := FkWTargetLow;
-
-
- InitPropertyValues(0);
-
end;
destructor TStorageController2Obj.Destroy;
begin
- ElementName := '';
- YearlyShape := '';
- DailyShape := '';
- DutyShape := '';
-
if Assigned(cBuffer) then
ReallocMem(cBuffer, 0);
-(* Don't Do this here!! Disposes of actual object;
- YearlyShapeObj.Free;
- DailyShapeObj.Free;
- DutyShapeObj.Free;
-*)
-
-
FleetPointerList.Free;
FStorageNameList.Free;
inherited Destroy;
end;
-//----------------------------------------------------------------------------
-procedure TStorageController2Obj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
-
- PropertyValue[propELEMENT] := '';
- PropertyValue[propTERMINAL] := '1';
- PropertyValue[propMONPHASE] := 'MAX';
- PropertyValue[propKWTARGET] := '8000';
- PropertyValue[propKWTARGETLOW] := '4000';
- PropertyValue[propPCTKWBAND] := '2';
- PropertyValue[propPCTKWBANDLOW] := '2';
-// PropertyValue[propPFTARGET] :='.96';
-// PropertyValue[propPFBAND] :='.04';
- PropertyValue[propELEMENTLIST] := '';
- PropertyValue[propWEIGHTS] := '';
- PropertyValue[propMODEDISCHARGE] := 'Follow';
- PropertyValue[propMODECHARGE] := 'Time';
- PropertyValue[propTIMEDISCHARGETRIGGER] := '-1';
- PropertyValue[propTIMECHARGETRIGGER] := '2';
- PropertyValue[propRATEKW] := '20';
-// PropertyValue[propRATEKVAR] :='20';
- PropertyValue[propRATECHARGE] := '20';
- PropertyValue[propRESERVE] := '25';
- PropertyValue[propKWHTOTAL] := '';
- PropertyValue[propKWTOTAL] := '';
- PropertyValue[propKWACTUAL] := '';
- PropertyValue[propKWNEED] := '';
-// PropertyValue[propPARTICIPATION] :='';
- PropertyValue[propYEARLY] := '';
- PropertyValue[propDAILY] := '';
- PropertyValue[propDUTY] := '';
- PropertyValue[propEVENTLOG] := 'No';
- PropertyValue[propINHIBITTIME] := '5';
- PropertyValue[propTUPRAMP] := '0.25';
- PropertyValue[propTFLAT] := '2.0';
- PropertyValue[propTDNRAMP] := '0.25';
- PropertyValue[propKWTHRESHOLD] := '4000';
- PropertyValue[propDispFactor] := '1.0';
- PropertyValue[propRESETLEVEL] := '0.8';
- PropertyValue[propSEASONS] := '1';
- PropertyValue[propSEASONTARGETS] := '[8000,]';
- PropertyValue[propSEASONTARGETSLOW] := '[4000,]';
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-function TStorageController2Obj.GetPropertyValue(Index: Integer): String;
-begin
- Result := '';
- case Index of
-
- propMONPHASE:
- if FMonPhase = AVG then
- Result := 'AVG'
- else
- if FMonPhase = MAXPHASE then
- Result := 'MAX'
- else
- if FMonPhase = MINPHASE then
- Result := 'MIN'
- else
- Result := Format('%d', [FMonPhase]);
- propKWTARGET:
- Result := Format('%-.6g', [FkWTarget]);
- propKWTARGETLOW:
- Result := Format('%-.6g', [FkWTargetLow]);
- propPCTKWBAND:
- Result := Format('%-.6g', [FpctkWBand]);
- propKWBAND:
- Result := Format('%-.6g', [FkWBand]);
- propPCTKWBANDLOW:
- Result := Format('%-.6g', [FpctkWBandLow]);
- propKWBANDLOW:
- Result := Format('%-.6g', [FkWBandLow]);
-// propPFTARGET : Result := Format('%-.6g',[ConvertPFRange2ToPF(FPFTarget)]);
-// propPFBAND : Result := Format('%-.6g',[FPFBand]);
- propELEMENTLIST:
- Result := ReturnElementsList;
- propWEIGHTS:
- Result := ReturnWeightsList;
- propMODEDISCHARGE:
- Result := GetModeString(propMODEDISCHARGE, DischargeMode);
- propMODECHARGE:
- Result := GetModeString(propMODECHARGE, ChargeMode);
- propTIMEDISCHARGETRIGGER:
- Result := Format('%.6g', [DisChargeTriggerTime]);
- propTIMECHARGETRIGGER:
- Result := Format('%.6g', [ChargeTriggerTime]);
- propRATEKW:
- Result := Format('%-.8g', [pctkWRate]);
-// propRATEKVAR : Result := Format('%-.8g',[pctkvarRate]);
- propRATECHARGE:
- Result := Format('%-.8g', [pctChargeRate]);
- propRESERVE:
- Result := Format('%-.8g', [pctFleetReserve]);
- propKWHTOTAL:
- Result := GetkWhTotal(TotalkWhCapacity);
- propKWTOTAL:
- Result := GetkWTotal(TotalkWCapacity);
- propKWHACTUAL:
- Result := GetkWhActual;
- propKWACTUAL:
- Result := GetkWActual;
- propKWNEED:
- Result := Format('%-.6g', [kWNeeded]);
- {propPARTICIPATION : Result := PropertyValue[Index]; }
- propYEARLY:
- Result := YearlyShape;
- propDAILY:
- Result := DailyShape;
- propDUTY:
- Result := DutyShape;
- propEVENTLOG:
- Result := StrYorN(ShowEventLog);
-// propVARDISPATCH : If DispatchVars Then Result := 'Yes' Else Result := 'No';
- propINHIBITTIME:
- Result := Format('%d', [InhibitHrs]);
- propTUPRAMP:
- Result := Format('%.6g', [UpRamptime]);
- propTFLAT:
- Result := Format('%.6g', [FlatTime]);
- propTDNRAMP:
- Result := Format('%.6g', [DnrampTime]);
- propKWTHRESHOLD:
- Result := Format('%.6g', [FkWThreshold]);
- propDispFactor:
- Result := Format('%.6g', [DispFactor]);
- propRESETLEVEL:
- Result := Format('%.6g', [ResetLevel]);
- propSEASONS:
- Result := Format('%d', [seasons]);
- propSEASONTARGETS:
- Result := ReturnSeasonTarget(1);
- propSEASONTARGETSLOW:
- Result := ReturnSeasonTarget(0);
-
- else // take the generic handler
- Result := inherited GetPropertyValue(index);
-
- end;
-end;
-
function TStorageController2Obj.Get_FleetkW: Double;
var
@@ -1186,53 +729,43 @@ function TStorageController2Obj.Get_FleetReservekWh: Double;
pStorage := FleetPointerList.Get(i);
Result := Result + pStorage.StorageVars.kWhReserve;
end;
-
end;
-{--------------------------------------------------------------------------}
procedure TStorageController2Obj.RecalcElementData();
-
// Recalculate critical element values after changes have been made
-
-var
- DevIndex: Integer;
-
begin
+ {Check for existence of monitored element}
- {Check for existence of monitored element}
-
- Devindex := GetCktElementIndex(ElementName); // Global FUNCTION
- if DevIndex > 0 then
+ if MonitoredElement <> NIL then
begin
- MonitoredElement := ActiveCircuit.CktElements.Get(DevIndex);
if ElementTerminal > MonitoredElement.Nterms then
begin
- DoErrorMsg('StorageController2: "' + Name + '"',
- 'Terminal no. "' + '" Does not exist.',
+ DoErrorMsg(Format('StorageController: "%s"', [Name]),
+ Format('Terminal no. "%d" Does not exist.', [ElementTerminal]),
'Re-specify terminal no.', 371);
end
else
begin
- Nphases := MonitoredElement.Nphases;
+ FNphases := MonitoredElement.Nphases;
NConds := FNphases;
- // Sets name of i-th terminal's connected bus in StorageController's buslist
+ // Sets name of i-th terminal's connected bus in StorageController's buslist
Setbus(1, MonitoredElement.GetBus(ElementTerminal));
- // Allocate a buffer bigenough to hold everything from the monitored element
+ // Allocate a buffer bigenough to hold everything from the monitored element
ReAllocMem(cBuffer, SizeOF(cBuffer^[1]) * MonitoredElement.Yorder);
CondOffset := (ElementTerminal - 1) * MonitoredElement.NConds; // for speedy sampling
end;
end
else
- DoSimpleMsg('Monitored Element in StorageController2.' + Name + ' Does not exist:"' + ElementName + '"', 372);
+ DoSimpleMsg('Monitored Element in %s is not set', [FullName], 372);
if FleetListChanged then
if not MakeFleetList then
- DoSimpleMsg('No unassigned Storage Elements found to assign to StorageController.' + Name, 37201);
+ DoSimpleMsg('No unassigned Storage Elements found to assign to %s', [FullName], 37201);
- GetkWTotal(TotalkWCapacity);
- GetkWhTotal(TotalkWhCapacity);
+ // TotalkWCapacity := GetkWTotal(self);
+ // TotalkWhCapacity := GetkWhTotal(self);
if FleetSize > 0 then
begin
@@ -1242,160 +775,29 @@ procedure TStorageController2Obj.RecalcElementData();
UpPlusFlat := UpRampTime + FlatTime;
UpPlusFlatPlusDn := UpPlusFlat + DnRampTime;
-
end;
procedure TStorageController2Obj.MakePosSequence();
begin
if MonitoredElement <> NIL then
begin
- Nphases := MonitoredElement.NPhases;
+ FNphases := MonitoredElement.NPhases;
Nconds := FNphases;
Setbus(1, MonitoredElement.GetBus(ElementTerminal));
- // Allocate a buffer bigenough to hold everything from the monitored element
+ // Allocate a buffer big enough to hold everything from the monitored element
ReAllocMem(cBuffer, SizeOF(cbuffer^[1]) * MonitoredElement.Yorder);
CondOffset := (ElementTerminal - 1) * MonitoredElement.NConds; // for speedy sampling
end;
inherited;
end;
-{--------------------------------------------------------------------------}
-procedure TStorageController2Obj.CalcYPrim();
-begin
- // leave YPrims as nil and they will be ignored
- // Yprim is zeroed when created. Leave it as is.
- // IF YPrim=nil THEN YPrim := TcMatrix.CreateMatrix(Yorder);
-
-
-end;
-
-
-{--------------------------------------------------------------------------}
-procedure TStorageController2Obj.GetCurrents(Curr: pComplexArray);
-var
- i: Integer;
-begin
-
- for i := 1 to Fnconds do
- Curr^[i] := CZERO;
-
-end;
-
-function TStorageController2Obj.GetkWActual: String;
-begin
- Result := Format('%-.8g', [FleetkW]);
-end;
-
-function TStorageController2Obj.GetkWhActual: String;
-begin
- Result := Format('%-.8g', [FleetkWh]);
-end;
-
-function TStorageController2Obj.GetkWhTotal(var Sum: Double): String;
-var
- pStorage: TStorageObj;
- i: Integer;
-
-begin
- Sum := 0.0;
- for i := 1 to FleetPointerList.Count do
- begin
- pStorage := FleetPointerList.Get(i);
- sum := sum + pStorage.StorageVars.kWhRating;
- end;
- Result := Format('%-.8g', [sum]);
-end;
-
-function TStorageController2Obj.GetkWTotal(var Sum: Double): String;
-var
- pStorage: TStorageObj;
- i: Integer;
-
-begin
- Sum := 0.0;
- for i := 1 to FleetPointerList.Count do
- begin
- pStorage := FleetPointerList.Get(i);
- sum := sum + pStorage.StorageVars.kWRating;
- end;
- Result := Format('%-.8g', [sum]);
-end;
-
-function TStorageController2Obj.GetModeString(Opt, Mode: Integer): String;
-begin
- Result := '';
- case Opt of
- propMODEDISCHARGE:
- case Mode of
- MODEFOLLOW:
- Result := 'Follow';
- MODELOADSHAPE:
- Result := 'Loadshape';
- MODESUPPORT:
- Result := 'Support';
- MODETIME:
- Result := 'Time';
- MODEPEAKSHAVE:
- Result := 'Peakshave';
- CURRENTPEAKSHAVE:
- Result := 'I-Peakshave';
- else
- Result := 'UNKNOWN'
- end;
- propMODECHARGE:
- case Mode of
- // 1: Result := 'Follow';
- MODELOADSHAPE:
- Result := 'Loadshape';
- // 3: Result := 'Support';
- MODETIME:
- Result := 'Time';
- MODEPEAKSHAVELOW:
- Result := 'PeakshaveLow';
- CURRENTPEAKSHAVELOW:
- Result := 'I-PeakShaveLow';
- else
- Result := 'UNKNOWN'
- end;
- else
- DoSimpleMsg('Unknown Charge/Discharge designation', 14401);
- end;
-end;
-
-{--------------------------------------------------------------------------}
-procedure TStorageController2Obj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
- if Complete then
- begin
- FSWriteln(F);
- end;
-
-end;
-
-{--------------------------------------------------------------------------}
procedure TStorageController2Obj.DoPendingAction(const Code, ProxyHdl: Integer);
begin
-
- {
- Release the discharge inhibit .
- Do nothing for other codes
- }
+ // Release the discharge inhibit .
+ // Do nothing for other codes
if (Code = RELEASE_INHIBIT) and (DischargeMode <> MODEFOLLOW) then
DischargeInhibited := FALSE;
-
end;
procedure TStorageController2Obj.DoScheduleMode();
@@ -1439,7 +841,6 @@ procedure TStorageController2Obj.DoScheduleMode();
TDiff := NormalizeToTOD(DynaVars.intHour, DynaVars.t) - DisChargeTriggerTime;
if TDiff < UpRampTime then
begin
-
pctDischargeRate := min(pctkWRate, max(pctKWRate * Tdiff / UpRampTime, 0.0));
SetFleetDesiredState(STORE_DISCHARGING);
@@ -1452,10 +853,8 @@ procedure TStorageController2Obj.DoScheduleMode();
end
else
begin
-
if TDiff < UpPlusFlat then
begin
-
pctDischargeRate := pctkWRate;
SetFleetDesiredState(STORE_DISCHARGING);
if PctDischargeRate <> LastpctDischargeRate then
@@ -1465,7 +864,6 @@ procedure TStorageController2Obj.DoScheduleMode();
else
if TDiff > UpPlusFlatPlusDn then
begin
-
SetFleetToIdle;
ChargingAllowed := TRUE;
pctDischargeRate := 0.0;
@@ -1504,9 +902,8 @@ procedure TStorageController2Obj.DoTimeMode(Opt: Integer);
ReservekWh,
TotalRatingkWh: Double;
begin
-
TotalRatingkWh := FleetkWhRating;
- RemainingkWh := FleetkWh;
+ RemainingkWh := Get_FleetkWh();
ReservekWh := FleetReservekWh;
@@ -1567,10 +964,8 @@ procedure TStorageController2Obj.DoTimeMode(Opt: Integer);
end;
end; //Charge mode
end;
-
end;
-//----------------------------------------------------------------------------
function TStorageController2Obj.NormalizeToTOD(h: Integer; sec: Double): Double;
// Normalize time to a floating point number representing time of day If Hour > 24
// time should be 0 to 23.999999....
@@ -1578,7 +973,6 @@ function TStorageController2Obj.NormalizeToTOD(h: Integer; sec: Double): Double;
HourOfDay: Integer;
begin
-
if h > 23 then
HourOfDay := (h - (h div 24) * 24)
else
@@ -1602,14 +996,12 @@ procedure TStorageController2Obj.PushTimeOntoControlQueue(Code: Integer);
LoadsNeedUpdating := TRUE; // Force recalc of power parms
ControlQueue.Push(DynaVars.intHour, DynaVars.t, Code, 0, Self);
end;
-
end;
-{--------------------------------------------------------------------------}
function TStorageController2Obj.Get_DynamicTarget(THigh: Integer): Double;
var
// Temp, temp2: Double;
- RatingIdx: Integer;
+ RatingIdx: Integer = 0;
RSignal: TXYCurveObj;
begin
if DSS.SeasonSignal <> '' then
@@ -1636,7 +1028,6 @@ function TStorageController2Obj.Get_DynamicTarget(THigh: Integer): Double;
end;
-{--------------------------------------------------------------------------}
procedure TStorageController2Obj.DoLoadFollowMode();
var
@@ -1661,14 +1052,12 @@ procedure TStorageController2Obj.DoLoadFollowMode();
ActualkWDispatch: Double;
begin
-
// If list is not defined, go make one from all storage elements in circuit
if FleetPointerList.Count = 0 then
MakeFleetList;
if FleetSize > 0 then
begin
-
StorekWChanged := FALSE;
StorekvarChanged := FALSE;
SkipkWDispatch := FALSE;
@@ -1676,7 +1065,6 @@ procedure TStorageController2Obj.DoLoadFollowMode();
//----MonitoredElement.ActiveTerminalIdx := ElementTerminal;
if DischargeMode = CURRENTPEAKSHAVE then
begin
-
MonitoredElement.GetCurrents(cBuffer);
GetControlCurrent(Amps);
@@ -1782,14 +1170,14 @@ procedure TStorageController2Obj.DoLoadFollowMode();
if (FleetState = STORE_CHARGING) then
begin
if not (DischargeMode = CURRENTPEAKSHAVE) then
- Pdiff := Pdiff + FleetkW
+ Pdiff := Pdiff + Get_FleetkW()
else
begin
MonitoredElement.ComputeVterminal();
VoltsArr := MonitoredElement.Vterminal;
ElemVolts := cabs(VoltsArr^[1]);
- Pdiff := Pdiff + (FleetkW * 1000 / (ElemVolts * MonitoredElement.NPhases));
-// Pdiff := Pdiff + (FleetkW * 1000 / (ElemVolts ));
+ Pdiff := Pdiff + (Get_FleetkW() * 1000 / (ElemVolts * MonitoredElement.NPhases));
+// Pdiff := Pdiff + (Get_FleetkW() * 1000 / (ElemVolts ));
end;
end;
@@ -1813,11 +1201,11 @@ procedure TStorageController2Obj.DoLoadFollowMode();
end; // -----------------------------------------------------------------------
end;
-// STORE_DISCHARGING: If ((PDiff + FleetkW) < 0.0) or OutOfOomph Then
-// STORE_DISCHARGING: If (((PDiff + FleetkW) < 0.0) and (abs(PDiff) > HalfkWBand)) or OutOfOomph Then // CR: set to idle only if out of band
+// STORE_DISCHARGING: If ((PDiff + Get_FleetkW()) < 0.0) or OutOfOomph Then
+// STORE_DISCHARGING: If (((PDiff + Get_FleetkW()) < 0.0) and (abs(PDiff) > HalfkWBand)) or OutOfOomph Then // CR: set to idle only if out of band
// Begin // desired decrease is greater then present output; just cancel
// If ShowEventLog Then AppendToEventLog('StorageController.' + Self.Name,
-// Format('Desired decrease is greater than present output. Pdiff = %-.6g, FleetkW = %-.6g. Setting Fleet to Idle', [PDiff, FleetkW]));
+// Format('Desired decrease is greater than present output. Pdiff = %-.6g, FleetkW = %-.6g. Setting Fleet to Idle', [PDiff, Get_FleetkW()]));
// SetFleetToIdle; // also sets presentkW = 0
// For i := 1 to FleetSize Do Begin TStorageObj(FleetPointerList.Get(i)).SetNominalStorageOutput() End; // To Update Current kvarLimit
// PushTimeOntoControlQueue(STORE_IDLING); // force a new power flow solution
@@ -1832,7 +1220,7 @@ procedure TStorageController2Obj.DoLoadFollowMode();
if not SkipkWDispatch then
begin
- RemainingkWh := FleetkWh;
+ RemainingkWh := Get_FleetkWh();
ReservekWh := FleetReservekWh;
if (RemainingkWh > ReservekWh) then
begin
@@ -1866,7 +1254,6 @@ procedure TStorageController2Obj.DoLoadFollowMode();
if DispatchkW <= 0.0 then // if kWNeeded is too low, DispatchkW may be negative depending on idling losses. In this case, just set it to idling
begin
-
StorageState := STORE_IDLING; // overrides SetFleetToDischarge
if (abs(PresentkW) - StorageObj.kWOutIdling > EPSILON) then // if not already idling
@@ -1877,7 +1264,7 @@ procedure TStorageController2Obj.DoLoadFollowMode();
if ShowEventLog then
AppendToEventLog('StorageController.' + Self.Name,
- Format('Requesting ' + StorageObj.QualifiedName + ' to dispatch %-.6g kW. Setting ' + StorageObj.QualifiedName + ' to idling state. Final kWOut is %-.6g kW', [DispatchkW, ActualkWDispatch]));
+ Format('Requesting ' + StorageObj.FullName + ' to dispatch %-.6g kW. Setting ' + StorageObj.FullName + ' to idling state. Final kWOut is %-.6g kW', [DispatchkW, ActualkWDispatch]));
end
// DispatchkW := 0.0;
@@ -1893,7 +1280,6 @@ procedure TStorageController2Obj.DoLoadFollowMode();
// Next time, the inverter will be OFF and the control won't dispatch a new power
if StorageVars.kWhStored > StorageVars.kWhReserve then
begin
-
kW := DispatchkW;
SetNominalStorageOutput();
ActualkWDispatch := PresentkW;
@@ -1901,12 +1287,11 @@ procedure TStorageController2Obj.DoLoadFollowMode();
if ShowEventLog then
AppendToEventLog('StorageController.' + Self.Name,
- Format('Requesting ' + StorageObj.QualifiedName + ' to dispatch %-.6g kW, less than CutIn/CutOut.' + ' Final kWOut is %-.6g kW', [DispatchkW, ActualkWDispatch]));
+ Format('Requesting ' + StorageObj.FullName + ' to dispatch %-.6g kW, less than CutIn/CutOut.' + ' Final kWOut is %-.6g kW', [DispatchkW, ActualkWDispatch]));
end;
end
else
begin
-
// if inverter is already off, just override discharging state to
// idling and update current kvarlimit for usage by InvControl
@@ -1915,7 +1300,7 @@ procedure TStorageController2Obj.DoLoadFollowMode();
ActualkWDispatch := PresentkW;
if ShowEventLog then
AppendToEventLog('StorageController.' + Self.Name,
- Format('Requesting ' + StorageObj.QualifiedName + ' to dispatch %-.6g kW, less than CutIn/CutOut.' + ' Inverter is OFF. Final kWOut is %-.6g kW', [DispatchkW, ActualkWDispatch]));
+ Format('Requesting ' + StorageObj.FullName + ' to dispatch %-.6g kW, less than CutIn/CutOut.' + ' Inverter is OFF. Final kWOut is %-.6g kW', [DispatchkW, ActualkWDispatch]));
end
end
else
@@ -1929,7 +1314,7 @@ procedure TStorageController2Obj.DoLoadFollowMode();
if ShowEventLog then
AppendToEventLog('StorageController.' + Self.Name,
- Format('Requesting ' + StorageObj.QualifiedName + ' to dispatch %-.6g kW. Final kWOut is %-.6g kW',
+ Format('Requesting ' + StorageObj.FullName + ' to dispatch %-.6g kW. Final kWOut is %-.6g kW',
[DispatchkW, ActualkWDispatch]));
end;
@@ -1961,11 +1346,8 @@ procedure TStorageController2Obj.DoLoadFollowMode();
{Else just continue}
end;
-
-
end;
-{--------------------------------------------------------------------------}
procedure TStorageController2Obj.DoPeakShaveModeLow();
// This is the peakShaving mode for controlling the charging operation of the storage fleet
// The objective is to charge the storage fleet when the power at a monitored element is bellow a specified KW target (kWTarget_low)
@@ -2013,7 +1395,6 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
//----MonitoredElement.ActiveTerminalIdx := ElementTerminal;
if Chargemode = CURRENTPEAKSHAVELOW then
begin
-
MonitoredElement.GetCurrents(cBuffer);
GetControlCurrent(Amps);
// Amps := MonitoredElement.MaxCurrent[ElementTerminal]; // Max current in active terminal
@@ -2025,8 +1406,8 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
PDiff := S.re * 0.001 - CtrlTarget; // Assume S.re is normally positive
end;
- // ActualkW := FleetkW;
- ActualkWh := FleetkWh;
+ // ActualkW := Get_FleetkW();
+ ActualkWh := Get_FleetkWh();
TotalRatingkWh := FleetkWhRating;
if Chargemode = CURRENTPEAKSHAVELOW then // convert Pdiff from Amps to kW
@@ -2039,7 +1420,7 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
AmpsDiff := PDiff;
end
else
-// kWNeeded := Pdiff + FleetkW;
+// kWNeeded := Pdiff + Get_FleetkW();
kWNeeded := Pdiff;
@@ -2071,14 +1452,14 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
if (FleetState = STORE_DISCHARGING) then
begin
if not (ChargeMode = CURRENTPEAKSHAVELOW) then
- Pdiff := Pdiff + FleetkW
+ Pdiff := Pdiff + Get_FleetkW()
else
begin
MonitoredElement.ComputeVterminal();
VoltsArr := MonitoredElement.Vterminal;
ElemVolts := cabs(VoltsArr^[1]);
- Pdiff := Pdiff + (FleetkW * 1000 / (ElemVolts * MonitoredElement.NPhases)); // get actual Pdiff in Currents (discount FleetkW) (assuming same number of phases of Fleet and Monitored Element)
-// Pdiff := Pdiff + (FleetkW * 1000 / (ElemVolts ));
+ Pdiff := Pdiff + (Get_FleetkW() * 1000 / (ElemVolts * MonitoredElement.NPhases)); // get actual Pdiff in Currents (discount FleetkW) (assuming same number of phases of Fleet and Monitored Element)
+// Pdiff := Pdiff + (Get_FleetkW() * 1000 / (ElemVolts ));
end;
end;
@@ -2094,7 +1475,7 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
end
// End;
// STORE_CHARGING: If (kWNeeded > 0.0) or (ActualkWh>=TotalRatingkWh) // old approach
-// STORE_CHARGING: If (Pdiff + FleetkW > 0.0) or (ActualkWh >= TotalRatingkWh) Then
+// STORE_CHARGING: If (Pdiff + Get_FleetkW() > 0.0) or (ActualkWh >= TotalRatingkWh) Then
// Begin // desired decrease (in absolute value) is greater than present output; just cancel
// SetFleetToIdle; // also sets presentkW = 0
// PushTimeOntoControlQueue(STORE_IDLING); // force a new power flow solution
@@ -2123,7 +1504,6 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
StorageObj := FleetPointerList.Get(i);
with StorageObj do
begin
-
// Checks if PDiff needs to be adjusted considering the charging mode
if Chargemode = CURRENTPEAKSHAVELOW then
begin
@@ -2144,7 +1524,6 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
if ChargekW >= 0 then // chargekW may be positive if increase in demand is too high.
begin
-
StorageState := STORE_IDLING; // overrides SetFleetToDischarge
if (abs(PresentkW) - StorageObj.kWOutIdling > EPSILON) then // if not already idling
@@ -2155,7 +1534,7 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
if ShowEventLog then
AppendToEventLog('StorageController.' + Self.Name,
- Format('Requesting ' + StorageObj.QualifiedName + ' to dispatch %-.6g kW. Setting ' + StorageObj.QualifiedName + ' to idling state. Final kWOut is %-.6g kW', [ChargekW, ActualkWDispatch]));
+ Format('Requesting ' + StorageObj.FullName + ' to dispatch %-.6g kW. Setting ' + StorageObj.FullName + ' to idling state. Final kWOut is %-.6g kW', [ChargekW, ActualkWDispatch]));
end
end
@@ -2164,7 +1543,6 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
// If ChargekW <> PresentkW Then // do only if change requested
if abs(StorageObj.kW - ChargekW) / abs(ChargekW) > 0.0001 then // do only if change requested
begin
-
if abs(ChargekW) < Max(CutInkWAC, CutOutkWAC) then // Necessary check to avoid the control to go into an infinite loop when ChargekW is less than CutOutkWAC
begin
if InverterON = TRUE then // request Dispatch only if the inverter is on (only once).
@@ -2172,7 +1550,6 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
// Next time the inverter will be OFF and the control won't dispatch a new power
if StorageVars.kWhStored > StorageVars.kWhReserve then
begin
-
kW := ChargekW;
SetNominalStorageOutput();
ActualkWDispatch := PresentkW;
@@ -2180,12 +1557,11 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
if ShowEventLog then
AppendToEventLog('StorageController.' + Self.Name,
- Format('Requesting ' + StorageObj.QualifiedName + ' to dispatch %-.6g kW, less than CutIn/CutOut.' + ' Final kWOut is %-.6g kW', [ChargekW, ActualkWDispatch]));
+ Format('Requesting ' + StorageObj.FullName + ' to dispatch %-.6g kW, less than CutIn/CutOut.' + ' Final kWOut is %-.6g kW', [ChargekW, ActualkWDispatch]));
end;
end
else
begin
-
// if inverter is already off, just override discharging state to
// idling and update current kvarlimit for usage by InvControl
@@ -2194,7 +1570,7 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
ActualkWDispatch := PresentkW;
if ShowEventLog then
AppendToEventLog('StorageController.' + Self.Name,
- Format('Requesting ' + StorageObj.QualifiedName + ' to dispatch %-.6g kW, less than CutIn/CutOut.' + ' Inverter is OFF. Final kWOut is %-.6g kW', [ChargekW, ActualkWDispatch]));
+ Format('Requesting ' + StorageObj.FullName + ' to dispatch %-.6g kW, less than CutIn/CutOut.' + ' Inverter is OFF. Final kWOut is %-.6g kW', [ChargekW, ActualkWDispatch]));
end
end
else
@@ -2210,7 +1586,7 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
if ShowEventLog then
AppendToEventLog('StorageController.' + Self.Name,
- Format('Requesting ' + StorageObj.QualifiedName + ' to dispatch %-.6g kW. Final kWOut is %-.6g kW',
+ Format('Requesting ' + StorageObj.FullName + ' to dispatch %-.6g kW. Final kWOut is %-.6g kW',
[ChargekW, ActualkWDispatch]));
end;
@@ -2241,7 +1617,6 @@ procedure TStorageController2Obj.DoPeakShaveModeLow();
end;
end;
-{--------------------------------------------------------------------------}
procedure TStorageController2Obj.Sample();
begin
@@ -2270,7 +1645,7 @@ procedure TStorageController2Obj.Sample();
MODESCHEDULE:
DoScheduleMode();
else
- DoSimpleMsg(Format('Invalid DisCharging Mode: %d', [DisChargeMode]), 14408);
+ DoSimpleMsg('Invalid DisCharging Mode: %d', [DisChargeMode], 14408);
end;
if ChargingAllowed then
@@ -2283,14 +1658,11 @@ procedure TStorageController2Obj.Sample();
CURRENTPEAKSHAVELOW:
DoPeakShaveModeLow()
else
- DoSimpleMsg(Format('Invalid Charging Mode: %d', [ChargeMode]), 14409);
+ DoSimpleMsg('Invalid Charging Mode: %d', [ChargeMode], 14409);
end;
-
-
end;
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.CalcDailyMult(Hr: Double);
begin
@@ -2303,7 +1675,6 @@ procedure TStorageController2Obj.CalcDailyMult(Hr: Double);
end;
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.CalcDutyMult(Hr: Double);
begin
@@ -2315,7 +1686,6 @@ procedure TStorageController2Obj.CalcDutyMult(Hr: Double);
CalcDailyMult(Hr); // Default to Daily Mult If no duty curve specified
end;
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.CalcYearlyMult(Hr: Double);
begin
@@ -2327,7 +1697,6 @@ procedure TStorageController2Obj.CalcYearlyMult(Hr: Double);
CalcDailyMult(Hr); // Defaults to Daily curve
end;
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.DoLoadShapeMode();
var
FleetStateSaved: Integer;
@@ -2336,7 +1705,6 @@ procedure TStorageController2Obj.DoLoadShapeMode();
NewkWRate: Double;
//NewkvarRate: Double;
begin
-
FleetStateSaved := FleetState;
RateChanged := FALSE;
@@ -2398,11 +1766,8 @@ procedure TStorageController2Obj.DoLoadShapeMode();
{Force a new power flow solution if fleet state has changed}
if (FleetState <> FleetStateSaved) or RateChanged then
PushTimeOntoControlQueue(0);
-
-
end;
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.SetAllFleetValues;
var
i: Integer;
@@ -2417,7 +1782,6 @@ procedure TStorageController2Obj.SetAllFleetValues;
end;
end;
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.SetFleetChargeRate;
var
i: Integer;
@@ -2426,7 +1790,6 @@ procedure TStorageController2Obj.SetFleetChargeRate;
TStorageObj(FleetPointerList.Get(i)).pctkWin := pctChargeRate;
end;
-//----------------------------------------------------------------------------
//PROCEDURE TStorageController2Obj.SetFleetkvarRate;
//VAR
// i :Integer;
@@ -2436,7 +1799,6 @@ procedure TStorageController2Obj.SetFleetChargeRate;
//// TStorageObj(FleetPointerList.Get(i)).pctkvarout := pctkvarRate;
//End;
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.SetFleetkWRate(pctkw: Double);
var
i: Integer;
@@ -2445,7 +1807,6 @@ procedure TStorageController2Obj.SetFleetkWRate(pctkw: Double);
TStorageObj(FleetPointerList.Get(i)).pctkWout := pctkw;
end;
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.SetFleetToCharge;
var
i: Integer;
@@ -2455,18 +1816,15 @@ procedure TStorageController2Obj.SetFleetToCharge;
FleetState := STORE_CHARGING;
end;
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.SetFleetToDisCharge;
var
i: Integer;
begin
-
for i := 1 to FleetPointerList.Count do
TStorageObj(FleetPointerList.Get(i)).StorageState := STORE_DISCHARGING;
FleetState := STORE_DISCHARGING;
end;
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.SetFleetToIdle;
var
i: Integer;
@@ -2490,16 +1848,6 @@ procedure TStorageController2Obj.SetFleetDesiredState(state: Integer);
for i := 1 to FleetPointerList.Count do
TStorageObj(FleetPointerList.Get(i)).StateDesired := state;
end;
-
-//-----------------------------------------------------------------------------
-//
-//procedure TStorageController2Obj.Set_PFBand(const Value: Double);
-//begin
-// FPFBand := Value;
-// HalfPFBand := FPFBand / 2.0;
-//end;
-
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.SetFleetToExternal;
var
i: Integer;
@@ -2508,73 +1856,12 @@ procedure TStorageController2Obj.SetFleetToExternal;
TStorageObj(FleetPointerList.Get(i)).DispatchMode := STORE_EXTERNALMODE;
end;
-//----------------------------------------------------------------------------
-(*
- PROCEDURE TStorageController2Obj.SetPctReserve;
- VAR
- i :Integer;
- Begin
- For i := 1 to FleetPointerList.Count Do
- TStorageObj(FleetPointerList.Get(i)).pctReserve := pctFleetReserve;
- End;
-*)
-
-
-//----------------------------------------------------------------------------
-function TStorageController2Obj.InterpretMode(Opt: Integer;
- const S: String): Integer;
-begin
-
- Result := -1; // Unknown: error
- case Opt of
- propMODEDISCHARGE:
- case LowerCase(S)[1] of
- 'f':
- Result := MODEFOLLOW;
- 'l':
- Result := MODELOADSHAPE;
- 'p':
- Result := MODEPEAKSHAVE;
- 's':
- if LowerCase(S)[2] = 'c' then
- Result := MODESCHEDULE
- else
- Result := MODESUPPORT;
- 't':
- Result := MODETIME;
- 'i':
- Result := CURRENTPEAKSHAVE;
- else
- DoSimpleMsg('Discharge Mode "' + S + '" not recognized.', 14402);
- end;
- propMODECHARGE:
- case LowerCase(S)[1] of
- // 'f': Result := MODEFOLLOW;
- 'l':
- Result := MODELOADSHAPE;
- // 's': Result := MODESUPPORT;
- 't':
- Result := MODETIME;
- 'p':
- Result := MODEPEAKSHAVELOW;
- 'i':
- Result := CURRENTPEAKSHAVELOW;
- else
- DoSimpleMsg('Charge Mode "' + S + '" not recognized.', 14402);
- end;
- else
- end;
-end;
-
-//----------------------------------------------------------------------------
function TStorageController2Obj.MakeFleetList: Boolean;
-
var
StorageObj: TStorageObj;
i: Integer;
begin
-
Result := FALSE;
if FElementListSpecified then
@@ -2587,11 +1874,11 @@ function TStorageController2Obj.MakeFleetList: Boolean;
if Assigned(StorageObj) then
begin
if StorageObj.Enabled then
- FleetPointerList.New := StorageObj;
+ FleetPointerList.Add(StorageObj);
end
else
begin
- DoSimpleMsg('Error: Storage Element "' + FStorageNameList.Strings[i - 1] + '" not found.', 14403);
+ DoSimpleMsg('Error: Storage Element "%s" not found.', [FStorageNameList.Strings[i - 1]], 14403);
Exit;
end;
end;
@@ -2600,22 +1887,21 @@ function TStorageController2Obj.MakeFleetList: Boolean;
else
begin
-
- {Search through the entire circuit for enabled Storage Elements and add them to the list}
+ // Search through the entire circuit for enabled Storage Elements and add them to the list
FStorageNameList.Clear;
FleetPointerList.Clear;
for i := 1 to DSS.Storage2Class.ElementCount do
begin
StorageObj := DSS.Storage2Class.ElementList.Get(i);
- // Look for a storage element not already assigned
+ // Look for a storage element not already assigned
if StorageObj.Enabled and (StorageObj.DispatchMode <> STORE_EXTERNALMODE) then
begin
FStorageNameList.Add(StorageObj.Name); // Add to list of names
- FleetPointerList.New := StorageObj;
+ FleetPointerList.Add(StorageObj);
end;
end;
- {Allocate uniform weights}
+ // Allocate uniform weights
FleetSize := FleetPointerList.Count;
Reallocmem(FWeights, Sizeof(FWeights^[1]) * FleetSize);
for i := 1 to FleetSize do
@@ -2632,11 +1918,9 @@ function TStorageController2Obj.MakeFleetList: Boolean;
Result := TRUE;
FleetListChanged := FALSE;
-
end;
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.Reset;
begin
// inherited;
@@ -2645,65 +1929,6 @@ procedure TStorageController2Obj.Reset;
// do we want to set fleet to 100% charged storage?
end;
-
-//----------------------------------------------------------------------------
-function TStorageController2Obj.ReturnElementsList: String;
-var
- i: Integer;
-begin
- if FleetSize = 0 then
- begin
- Result := '';
- Exit;
- end;
-
- Result := '[' + FStorageNameList.Strings[0];
- for i := 1 to FleetSize - 1 do
- begin
- Result := Result + ', ' + FStorageNameList.Strings[i];
- end;
- Result := Result + ']'; // terminate the array
-
-end;
-
-//----------------------------------------------------------------------------
-function TStorageController2Obj.ReturnSeasonTarget(THigh: Integer): String;
-var
- i: Integer;
-begin
- if Seasons = 1 then
- begin
- Result := '';
- Exit;
- end;
-
- Result := '[';
- for i := 0 to (Seasons - 1) do
- begin
- if THigh = 1 then
- Result := Result + format('%.6g', [SeasonTargets[i]]) + ', '
- else
- Result := Result + format('%.6g', [SeasonTargetsLow[i]]) + ', ';
- end;
- Result := Result + ']'; // terminate the array
-
-end;
-
-
-//----------------------------------------------------------------------------
-function TStorageController2Obj.ReturnWeightsList: String;
-begin
- if FleetSize = 0 then
- begin
- Result := '';
- Exit;
- end;
-
- Result := GetDSSArray_Real(FleetSize, FWeights);
-
-end;
-
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.GetControlPower(var ControlPower: Complex);
// Get power to control based on active power
var
@@ -2712,7 +1937,6 @@ procedure TStorageController2Obj.GetControlPower(var ControlPower: Complex);
TempPower: Double;
begin
-
if MonitoredElement.NPhases = 1 then
begin
ControlPower := MonitoredElement.Power[ElementTerminal]; // just take the total power (works also for 1ph elements with 2 conductors)
@@ -2726,7 +1950,7 @@ procedure TStorageController2Obj.GetControlPower(var ControlPower: Complex);
begin // Get avg of all phases
ControlPower := Cmplx(0.0, 0.0);
for i := (1 + CondOffset) to (MonitoredElement.NConds + CondOffset) do
- ControlPower := Cadd(ControlPower, cBuffer^[i]);
+ ControlPower := ControlPower + cBuffer^[i];
end;
MAXPHASE:
begin // Get abs max of all phases
@@ -2739,7 +1963,7 @@ procedure TStorageController2Obj.GetControlPower(var ControlPower: Complex);
// ControlPowerPhase := i;
end;
// Compute equivalent total power of all phases assuming equal to max power in all phases
- ControlPower := cMulReal(ControlPower, Fnphases);
+ ControlPower := ControlPower * Fnphases;
end;
MINPHASE:
begin // Get abs min of all phases
@@ -2752,29 +1976,25 @@ procedure TStorageController2Obj.GetControlPower(var ControlPower: Complex);
// ControlPowerPhase := i;
end;
// Compute equivalent total power of all phases assuming equal to min power in all phases
- ControlPower := cMulReal(ControlPower, Fnphases); // sign according to phase with min abs value
+ ControlPower := ControlPower * Fnphases; // sign according to phase with min abs value
end;
else
// Compute equivalent total power of all phases assuming equal to power in selected phases
- ControlPower := cMulReal(Cbuffer^[FMonPhase], Fnphases); // monitored phase only
+ ControlPower := Cbuffer^[FMonPhase] * Fnphases; // monitored phase only
end;
end;
{If this is a positive sequence circuit (Fnphases=1),
then we need to multiply by 3 to get the 3-phase power}
if ActiveCircuit.PositiveSequence then
- ControlPower := cMulReal(ControlPower, 3.0);
-
+ ControlPower := ControlPower * 3.0;
end;
-//----------------------------------------------------------------------------
procedure TStorageController2Obj.GetControlCurrent(var ControlCurrent: Double);
// Get current to control
var
i: Integer;
-
begin
-
case FMonPhase of
AVG:
begin
@@ -2801,14 +2021,8 @@ procedure TStorageController2Obj.GetControlCurrent(var ControlCurrent: Double);
{Just use one phase because that's what most controls do.}
ControlCurrent := Cabs(Cbuffer^[FMonPhase]); // monitored phase only
end;
-
end;
-
-{--------------------------------------------------------------------------}
-
-initialization
-
- CDoubleOne := Cmplx(1.0, 1.0);
-
+finalization ChargeModeEnum.Free;
+ DischargeModeEnum.Free;
end.
diff --git a/src/Controls/SwtControl.pas b/src/Controls/SwtControl.pas
index 0d78f842a..e67637389 100644
--- a/src/Controls/SwtControl.pas
+++ b/src/Controls/SwtControl.pas
@@ -16,71 +16,60 @@ interface
CktElement,
DSSClass,
Arraydef,
- ucomplex;
+ UComplex, DSSUcomplex;
type
+{$SCOPEDENUMS ON}
+ TSwtControlProp = (
+ INVALID = 0,
+ SwitchedObj = 1,
+ SwitchedTerm = 2,
+ Action = 3,
+ Lock = 4,
+ Delay = 5,
+ Normal = 6,
+ State = 7,
+ Reset = 8
+ );
+{$SCOPEDENUMS OFF}
TSwtControl = class(TControlClass)
PROTECTED
- procedure DefineProperties;
- function MakeLike(const SwtControlName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TSwtControlObj = class(TControlElem)
-{$IFDEF DSS_CAPI}
PUBLIC
-{$ELSE}
- PROTECTED
-{$ENDIF}
- procedure Set_Enabled(Value: Boolean); OVERRIDE;
-
- PRIVATE
- FPresentState: EControlAction;
- FNormalState: EControlAction;
- ActionCommand: EControlAction;
+ PresentState: EControlAction;
+ NormalState: EControlAction;
+ CurrentAction: EControlAction; // previously ActionCommand
LockCommand: EControlAction;
- FLocked: Boolean;
+ Locked: LongBool;
Armed: Boolean;
- procedure InterpretSwitchAction(const Action: String);
- procedure Set_NormalState(const Value: EControlAction);
- procedure set_Flocked(const Value: Boolean);
- procedure Set_LastAction(const Value: EControlAction);
- procedure Set_PresentState(const Value: EControlAction);
- PUBLIC
constructor Create(ParClass: TDSSClass; const SwtControlName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure Set_Enabled(Value: Boolean); OVERRIDE;
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
procedure RecalcElementData; OVERRIDE;
- procedure CalcYPrim; OVERRIDE; // Always Zero for a SwtControl
procedure Sample; OVERRIDE; // Sample control quantities and set action times in Control Queue
procedure DoPendingAction(const Code, ProxyHdl: Integer); OVERRIDE; // Do the action that is pending from last sample
procedure Reset; OVERRIDE; // Reset to initial defined state
- procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present value of terminal Curr
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- property NormalState: EControlAction READ FNormalState WRITE Set_NormalState;
- property PresentState: EControlAction READ FPresentState WRITE Set_PresentState;
- property IsLocked: Boolean READ FLocked;
- property Locked: Boolean READ Flocked WRITE set_Flocked;
- property CurrentAction: EControlAction READ ActionCommand WRITE Set_LastAction;
end;
-{--------------------------------------------------------------------------}
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Circuit,
@@ -91,248 +80,195 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TSwtControlObj;
+ TProp = TSwtControlProp;
const
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
+ ActionEnum, StateEnum: TDSSEnum;
- NumPropsThisClass = 8;
-
-constructor TSwtControl.Create(dssContext: TDSSContext); // Creates superstructure for all SwtControl objects
+constructor TSwtControl.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
-
- Class_name := 'SwtControl';
- DSSClassType := DSSClassType + SWT_CONTROL;
-
- DefineProperties;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ ActionEnum := TDSSEnum.Create('SwtControl: Action', False, 1, 1,
+ ['close', 'open'],
+ [ord(CTRL_CLOSE), ord(CTRL_OPEN)]);
+ ActionEnum.DefaultValue := ord(CTRL_CLOSE);
+ StateEnum := TDSSEnum.Create('SwtControl: State', False, 1, 1,
+ ['closed', 'open'],
+ [ord(CTRL_CLOSE), ord(CTRL_OPEN)]);
+ StateEnum.DefaultValue := ord(CTRL_CLOSE);
+ end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, SWT_CONTROL, 'SwtControl');
end;
destructor TSwtControl.Destroy;
-
begin
inherited Destroy;
end;
-procedure TSwtControl.DefineProperties;
+procedure DoReset(Obj: TObj);
begin
-
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
-
- AllocatePropertyArrays; {see DSSClass}
-
- PropertyName[1] := 'SwitchedObj';
- PropertyName[2] := 'SwitchedTerm';
- PropertyName[3] := 'Action';
- PropertyName[4] := 'Lock';
- PropertyName[5] := 'Delay';
- PropertyName[6] := 'Normal';
- PropertyName[7] := 'State';
- PropertyName[8] := 'Reset';
-
- PropertyHelp[1] := 'Name of circuit element switch that the SwtControl operates. ' +
- 'Specify the full object class and name.';
- PropertyHelp[2] := 'Terminal number of the controlled element switch. ' +
- '1 or 2, typically. Default is 1.';
- PropertyHelp[3] := '{Open | Close} After specified delay time, and if not locked, causes the controlled switch to open or close. ';
- PropertyHelp[4] := '{Yes | No} Delayed action. Sends CTRL_LOCK or CTRL_UNLOCK message to control queue. ' +
- 'After delay time, controlled switch is locked in its present open / close state or unlocked. ' +
- 'Switch will not respond to either manual (Action) or automatic (COM interface) control or internal OpenDSS Reset when locked.';
- PropertyHelp[5] := 'Operating time delay (sec) of the switch. Defaults to 120.';
- PropertyHelp[6] := '{Open | Closed] Normal state of the switch. If not Locked, the switch reverts to this state for reset, change of mode, etc.' +
- ' Defaults to first Action or State specified if not specifically declared.';
- PropertyHelp[7] := '{Open | Closed] Present state of the switch. Upon setting, immediately forces state of switch.';
- PropertyHelp[8] := '{Yes | No} If Yes, forces Reset of switch to Normal state and removes Lock independently of any internal ' +
- 'reset command for mode change, etc.';
-
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ // force a reset
+ Obj.Locked := FALSE;
+ Obj.Reset;
end;
-function TSwtControl.NewObject(const ObjName: String): Integer;
+function GetState(Obj: TObj): Integer;
begin
- // Make a new SwtControl and add it to SwtControl class list
- with ActiveCircuit do
+ with Obj do
begin
- ActiveCktElement := TSwtControlObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
+ ControlledElement.ActiveTerminalIdx := ElementTerminal;
+ if ControlledElement.Closed[0] then
+ Result := ord(CTRL_CLOSE)
+ else
+ Result := ord(CTRL_OPEN);
end;
end;
-function TSwtControl.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
- Devindex: Integer;
-
+procedure TSwtControl.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
+ Numproperties := NumPropsThisClass;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enum properties
+ PropertyType[ord(TProp.Action)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.Action)] := ptruint(@obj.CurrentAction);
+ PropertyOffset2[ord(TProp.Action)] := PtrInt(ActionEnum);
+ PropertyOffset3[ord(TProp.Action)] := ptruint(@obj.Locked);
+ PropertyFlags[ord(TProp.Action)] := [TPropertyFlag.ConditionalReadOnly, TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.Action)] := ord(TProp.State);
+
+ PropertyType[ord(TProp.Normal)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.Normal)] := ptruint(@obj.CurrentAction);
+ PropertyOffset2[ord(TProp.Normal)] := PtrInt(StateEnum);
+ PropertyOffset3[ord(TProp.Normal)] := ptruint(@obj.Locked);
+ PropertyFlags[ord(TProp.Normal)] := [TPropertyFlag.ConditionalReadOnly];
+
+ PropertyType[ord(TProp.State)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.State)] := ptruint(@obj.CurrentAction);
+ PropertyOffset2[ord(TProp.State)] := PtrInt(StateEnum);
+ PropertyOffset3[ord(TProp.State)] := ptruint(@obj.Locked);
+ PropertyReadFunction[ord(TProp.State)] := @GetState;
+ PropertyFlags[ord(TProp.State)] := [TPropertyFlag.ConditionalReadOnly, TPropertyFlag.ReadByFunction];
+
+ // boolean
+ PropertyType[ord(TProp.Lock)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.Lock)] := ptruint(@obj.Locked);
+
+ // object references
+ PropertyType[ord(TProp.SwitchedObj)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.SwitchedObj)] := ptruint(@obj.FControlledElement);
+ PropertyOffset2[ord(TProp.SwitchedObj)] := 0;
+ PropertyWriteFunction[ord(TProp.SwitchedObj)] := @SetControlledElement;
+ PropertyFlags[ord(TProp.SwitchedObj)] := [TPropertyFlag.WriteByFunction]; //[TPropertyFlag.CheckForVar]; // not required for general cktelements
+
+ // integer properties
+ PropertyType[ord(TProp.SwitchedTerm)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.SwitchedTerm)] := ptruint(@obj.ElementTerminal);
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.Delay)] := ptruint(@obj.TimeDelay);
+
+ // boolean action
+ PropertyType[ord(TProp.Reset)] := TPropertyType.BooleanActionProperty;
+ PropertyOffset[ord(TProp.Reset)] := ptruint(@DoReset);
- // continue parsing WITH contents of Parser
- DSS.ActiveSwtControlObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveSwtControlObj;
-
- Result := 0;
-
- with DSS.ActiveSwtControlObj do
- begin
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
+end;
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- {internal SwtControl Property commands}
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 382);
- 1:
- ElementName := lowercase(Param);
- 2:
- ElementTerminal := Parser.IntValue;
- 3:
- InterpretSwitchAction(param);
- 4:
- Locked := InterpretYesNo(Param);
- 5:
- TimeDelay := Parser.DblValue;
- 6:
- begin // set the normal state
- InterpretSwitchAction(param);
- NormalState := ActionCommand;
- end;
- 7:
- begin // set the present state
- InterpretSwitchAction(param);
- PresentState := ActionCommand;
- end;
- 8:
- if InterpretYesNo(Param) then
- begin // force a reset
- Locked := FALSE;
- Reset;
- PropertyValue[8] := 'n';
- end;
+function TSwtControl.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
+begin
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
+end;
+procedure TSwtControlObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
+begin
+ case Idx of
+ // Default to first action specified for legacy scripts
+ ord(TProp.Normal):
+ NormalState := CurrentAction;
+ ord(TProp.Action):
+ if NormalState = CTRL_NONE then
+ NormalState := CurrentAction;
+ ord(TProp.Lock):
+ if Locked then
+ LockCommand := CTRL_LOCK
else
- // Inherited parameters
- ClassEdit(DSS.ActiveSwtControlObj, ParamPointer - NumPropsthisClass)
- end;
-
- {supplemental actions}
- case ParamPointer of
-
- // Default to first action specified for legacy scripts
- 3:
- if NormalState = CTRL_NONE then
- NormalState := ActionCommand;
-
- 4:
- if Locked then
- LockCommand := CTRL_LOCK
- else
- LockCommand := CTRL_UNLOCK;
-
- 7:
- begin
- if NormalState = CTRL_NONE then
- NormalState := PresentState;
- Devindex := GetCktElementIndex(ElementName); // Set Controlled element
- if DevIndex > 0 then
- begin
- ControlledElement := ActiveCircuit.CktElements.Get(DevIndex);
- if ControlledElement <> NIL then
- begin
- ControlledElement.ActiveTerminalIdx := ElementTerminal;
- case PresentState of // Force state
- CTRL_OPEN:
- ControlledElement.Closed[0] := FALSE;
- CTRL_CLOSE:
- ControlledElement.Closed[0] := TRUE;
- end;
- end;
- end;
+ LockCommand := CTRL_UNLOCK;
+ ord(TProp.State):
+ begin
+ PresentState := CurrentAction;
+ if NormalState = CTRL_NONE then
+ NormalState := PresentState;
+ if ControlledElement <> NIL then
+ begin
+ ControlledElement.ActiveTerminalIdx := ElementTerminal;
+ case PresentState of // Force state
+ CTRL_OPEN:
+ ControlledElement.Closed[0] := FALSE;
+ CTRL_CLOSE:
+ ControlledElement.Closed[0] := TRUE;
end;
end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
end;
-
- RecalcElementData;
end;
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-function TSwtControl.MakeLike(const SwtControlName: String): Integer;
+procedure TSwtControlObj.MakeLike(OtherPtr: Pointer);
var
- OtherSwtControl: TSwtControlObj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this SwtControl name in the present collection}
- OtherSwtControl := Find(SwtControlName);
- if OtherSwtControl <> NIL then
- with DSS.ActiveSwtControlObj do
- begin
-
- NPhases := OtherSwtControl.Fnphases;
- NConds := OtherSwtControl.Fnconds; // Force Reallocation of terminal stuff
-
- ElementName := OtherSwtControl.ElementName;
- ElementTerminal := OtherSwtControl.ElementTerminal;
- ControlledElement := OtherSwtControl.ControlledElement; // Pointer to target circuit element
-
- TimeDelay := OtherSwtControl.TimeDelay;
- Locked := OtherSwtControl.Locked;
- PresentState := OtherSwtControl.PresentState;
- NormalState := OtherSwtControl.NormalState;
- ActionCommand := OtherSwtControl.ActionCommand;
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherSwtControl.PropertyValue[i];
-
- end
- else
- DoSimpleMsg('Error in SwtControl MakeLike: "' + SwtControlName + '" Not Found.', 383);
-
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
+
+ ElementTerminal := Other.ElementTerminal;
+ ControlledElement := Other.ControlledElement; // Pointer to target circuit element
+
+ TimeDelay := Other.TimeDelay;
+ Locked := Other.Locked;
+ PresentState := Other.PresentState;
+ NormalState := Other.NormalState;
+ CurrentAction := Other.CurrentAction;
end;
-{==========================================================================}
-{ TSwtControlObj }
-{==========================================================================}
-
constructor TSwtControlObj.Create(ParClass: TDSSClass; const SwtControlName: String);
begin
inherited Create(ParClass);
- Name := LowerCase(SwtControlName);
+ Name := AnsiLowerCase(SwtControlName);
DSSObjType := ParClass.DSSClassType;
- NPhases := 3; // Directly set conds and phases
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 1; // this forces allocation of terminals and conductors in base class
- ElementName := '';
ControlledElement := NIL;
ElementTerminal := 1;
PresentState := CTRL_CLOSE; // default to closed
NormalState := CTRL_NONE; // default to unspecified; set on first setting action or anything
- ActionCommand := PresentState;
+ CurrentAction := PresentState;
Lockcommand := CTRL_NONE;
Locked := FALSE;
Armed := FALSE;
TimeDelay := 120.0; // 2 minutes
-
- InitPropertyValues(0);
end;
destructor TSwtControlObj.Destroy;
@@ -341,62 +277,36 @@ destructor TSwtControlObj.Destroy;
end;
procedure TSwtControlObj.RecalcElementData;
-var
- DevIndex: Integer;
begin
- Devindex := GetCktElementIndex(ElementName);
- if DevIndex > 0 then
+ if ControlledElement = NIL then // element not found
begin
- ControlledElement := ActiveCircuit.CktElements.Get(DevIndex);
- Nphases := ControlledElement.NPhases;
- Nconds := FNphases;
- ControlledElement.ActiveTerminalIdx := ElementTerminal;
+ DoErrorMsg(
+ Format(_('SwtControl: "%s"'), [Self.Name]),
+ _('SwitchedObj is not set.'),
+ _('Element must be defined previously.'), 387);
+ Exit;
+ end;
- ControlledElement.HasSwtControl := TRUE; // For Reliability calcs
-{
- if not Locked then
- Case PresentState of
- CTRL_OPEN: ControlledElement.Closed[0] := FALSE;
- CTRL_CLOSE: ControlledElement.Closed[0] := TRUE;
- End;
+ FNphases := ControlledElement.NPhases;
+ Nconds := FNphases;
+ ControlledElement.ActiveTerminalIdx := ElementTerminal;
-}
+ // Include(ControlledElement.Flags, Flg.HasSwtControl); // For Reliability calcs
// attach controller bus to the switch bus - no space allocated for monitored variables
- Setbus(1, ControlledElement.GetBus(ElementTerminal));
- end
- else
- begin
- ControlledElement := NIL; // element not found
- DoErrorMsg('SwtControl: "' + Self.Name + '"', 'CktElement Element "' + ElementName + '" Not Found.',
- ' Element must be defined previously.', 387);
- end;
+ Setbus(1, ControlledElement.GetBus(ElementTerminal));
end;
-procedure TSwtControlObj.MakePosSequence;
+procedure TSwtControlObj.MakePosSequence();
begin
if ControlledElement <> NIL then
begin
- Nphases := ControlledElement.NPhases;
+ FNphases := ControlledElement.NPhases;
Nconds := FNphases;
Setbus(1, ControlledElement.GetBus(ElementTerminal));
end;
inherited;
end;
-{--------------------------------------------------------------------------}
-procedure TSwtControlObj.CalcYPrim;
-begin
- // leave YPrims as nil
-end;
-
-procedure TSwtControlObj.GetCurrents(Curr: pComplexArray);
-var
- i: Integer;
-begin
- for i := 1 to Fnconds do
- Curr^[i] := CZERO;
-end;
-
procedure TSwtControlObj.DoPendingAction(const Code, ProxyHdl: Integer);
var
ctrl_code: EControlAction;
@@ -428,34 +338,9 @@ procedure TSwtControlObj.DoPendingAction(const Code, ProxyHdl: Integer);
end;
end;
-procedure TSwtControlObj.InterpretSwitchAction(const Action: String);
-begin
- if not Locked then
- begin
- case LowerCase(Action)[1] of
- 'o':
- ActionCommand := CTRL_OPEN;
- else // default is closed
- ActionCommand := CTRL_CLOSE;
- end;
-
- { Changed to delayed action
- if ControlledElement <> nil then begin
- ControlledElement.ActiveTerminalIdx := ElementTerminal;
- Case PresentState of
- CTRL_OPEN: ControlledElement.Closed[0] := FALSE;
- CTRL_CLOSE: ControlledElement.Closed[0] := TRUE;
- End;
- End;
- }
-
- end;
-end;
-
procedure TSwtControlObj.Sample;
begin
-
-// push on the Lock command if any at the present time delay
+ // push on the Lock command if any at the present time delay
if LockCommand <> CTRL_NONE then
with ActiveCircuit, ActiveCircuit.Solution do
begin
@@ -463,91 +348,12 @@ procedure TSwtControlObj.Sample;
LockCommand := CTRL_NONE; // reset the lock command for next time
end;
- if (ActionCommand <> PresentState) and not Armed then // we need to operate this switch
+ if (CurrentAction <> PresentState) and not Armed then // we need to operate this switch
with ActiveCircuit, ActiveCircuit.Solution do
begin
- ControlQueue.Push(DynaVars.intHour, Dynavars.t + TimeDelay, ActionCommand, 0, Self);
+ ControlQueue.Push(DynaVars.intHour, Dynavars.t + TimeDelay, CurrentAction, 0, Self);
Armed := TRUE;
end;
- {ControlledElement.ActiveTerminalIdx := ElementTerminal;
- IF ControlledElement.Closed [0] // Check state of phases of active terminal
- THEN PresentState := CTRL_CLOSE
- ELSE PresentState := CTRL_OPEN; }
-end;
-
-procedure TSwtControlObj.set_Flocked(const Value: Boolean);
-begin
- Flocked := Value;
-end;
-
-procedure TSwtControlObj.Set_LastAction(const Value: EControlAction);
-begin
- ActionCommand := Value;
-
-end;
-
-procedure TSwtControlObj.Set_NormalState(const Value: EControlAction);
-begin
- FNormalState := Value;
-end;
-
-procedure TSwtControlObj.Set_PresentState(const Value: EControlAction);
-begin
- FPresentState := Value;
-end;
-
-procedure TSwtControlObj.DumpProperties(F: TFileStream; Complete: Boolean);
-var
- i: Integer;
-begin
- inherited DumpProperties(F, Complete);
- with ParentClass do
- for i := 1 to NumProperties do
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[PropertyIdxMap[i]]);
- if Complete then
- FSWriteln(F);
-end;
-
-function TSwtControlObj.GetPropertyValue(Index: Integer): String;
-begin
- Result := '';
- case Index of
- 1:
- Result := ElementName;
- 2:
- Result := Format('%d', [ElementTerminal]);
- 3:
- case ActionCommand of
- CTRL_OPEN:
- Result := 'open';
- else
- {CTRL_CLOSE:} Result := 'close';
- end;
- 4:
- Result := StrYorN(Locked);
- 5:
- Result := Format('%-.7g', [TimeDelay]);
- 6:
- case FNormalState of
- CTRL_OPEN:
- Result := 'open';
- else
- {CTRL_CLOSE:} Result := 'closed';
- end;
- 7:
- begin
- ControlledElement.ActiveTerminalIdx := ElementTerminal;
- if ControlledElement.Closed[0] then
- Result := 'Closed'
- else
- Result := 'open';
- end;
- 8:
- Result := 'n'; // Always no; yes is executed immediately
- else
- Result := inherited GetPropertyValue(Index);
- end;
-
end;
procedure TSwtControlObj.Reset;
@@ -555,12 +361,12 @@ procedure TSwtControlObj.Reset;
if not Locked then
begin
PresentState := NormalState;
- ActionCommand := PresentState;
+ CurrentAction := PresentState;
Armed := FALSE;
if ControlledElement <> NIL then
begin
ControlledElement.ActiveTerminalIdx := ElementTerminal; // Set active terminal
- case FNormalState of
+ case NormalState of
CTRL_OPEN:
ControlledElement.Closed[0] := FALSE;
else
@@ -570,19 +376,6 @@ procedure TSwtControlObj.Reset;
end;
end;
-procedure TSwtControlObj.InitPropertyValues(ArrayOffset: Integer);
-begin
- PropertyValue[1] := ''; //'element';
- PropertyValue[2] := '1'; //'terminal';
- PropertyValue[3] := 'c';
- PropertyValue[4] := 'n';
- PropertyValue[5] := '120.0';
- PropertyValue[6] := 'c';
- PropertyValue[7] := 'c';
- PropertyValue[8] := 'n';
- inherited InitPropertyValues(NumPropsThisClass);
-end;
-
procedure TSwtControlObj.Set_Enabled(Value: Boolean);
begin
// Do nothing else besides toggling the flag,
@@ -590,4 +383,6 @@ procedure TSwtControlObj.Set_Enabled(Value: Boolean);
FEnabled := Value;
end;
+finalization ActionEnum.Free;
+ StateEnum.Free;
end.
diff --git a/src/Controls/UPFCControl.pas b/src/Controls/UPFCControl.pas
index c5a87ffda..f2d25e5e4 100644
--- a/src/Controls/UPFCControl.pas
+++ b/src/Controls/UPFCControl.pas
@@ -6,16 +6,13 @@
All rights reserved.
----------------------------------------------------------
}
-{
- A UPFCControl is a control element that is connected to a terminal of another
- circuit element and sends dispatch kW signals to a set of generators it controls
-
- A UPFCControl is defined by a New command:
-
- New UPFCControl.Name=myname Element=devclass.name terminal=[ 1|2|...] CapacitorList = (gen1 gen2 ...)
-
-}
+// A UPFCControl is a control element that is connected to a terminal of another
+// circuit element and sends dispatch kW signals to a set of generators it controls
+//
+// A UPFCControl is defined by a New command:
+//
+// New UPFCControl.Name=myname Element=devclass.name terminal=[ 1|2|...] CapacitorList = (gen1 gen2 ...)
interface
@@ -26,30 +23,29 @@ interface
CktElement,
DSSClass,
Arraydef,
- ucomplex,
+ UComplex, DSSUcomplex,
utilities,
DSSPointerList,
Classes;
type
+{$SCOPEDENUMS ON}
+ TUPFCControlProp = (
+ INVALID = 0,
+ UPFCList = 1
+ );
+{$SCOPEDENUMS OFF}
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TUPFCControl = class(TControlClass)
- PRIVATE
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const UPFCControlName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TUPFCControlObj = class(TControlElem)
PRIVATE
FUPFCNameList: TStringList;
@@ -62,27 +58,21 @@ TUPFCControlObj = class(TControlElem)
constructor Create(ParClass: TDSSClass; const UPFCControlName: String);
destructor Destroy; OVERRIDE;
+ procedure MakeLike(OtherPtr: Pointer); override;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
procedure RecalcElementData; OVERRIDE;
- procedure CalcYPrim; OVERRIDE; // Always Zero for a UPFCControl
procedure Sample; OVERRIDE; // Sample control quantities and set action times in Control Queue
procedure DoPendingAction(const Code, ProxyHdl: Integer); OVERRIDE; // Do the action that is pending from last sample
procedure Reset; OVERRIDE; // Reset to initial defined state
- procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present value of terminal Curr
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
function MakeUPFCList: Boolean;
end;
-{--------------------------------------------------------------------------}
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Circuit,
@@ -95,156 +85,73 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TUPFCControlObj;
+ TProp = TUPFCControlProp;
const
- NumPropsThisClass = 1;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-{--------------------------------------------------------------------------}
-constructor TUPFCControl.Create(dssContext: TDSSContext); // Creates superstructure for all UPFCControl objects
+constructor TUPFCControl.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
-
- Class_name := 'UPFCControl';
- DSSClassType := DSSClassType + UPFC_CONTROL;
-
- DefineProperties;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, UPFC_CONTROL, 'UPFCControl');
end;
-{--------------------------------------------------------------------------}
destructor TUPFCControl.Destroy;
-
begin
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TUPFCControl.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
- // Define Property names
-
- PropertyName[1] := 'UPFCList';
-
- PropertyHelp[1] := 'The list of all the UPFC devices to be controlled by this controller, ' +
- 'If left empty, this control will apply for all UPFCs in the model.';
+ // string lists
+ PropertyType[ord(TProp.UPFCList)] := TPropertyType.StringListProperty;
+ PropertyOffset[ord(TProp.UPFCList)] := ptruint(@obj.FUPFCNameList);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ inherited DefineProperties;
end;
-{--------------------------------------------------------------------------}
-function TUPFCControl.NewObject(const ObjName: String): Integer;
-begin
- // Make a new UPFCControl and add it to UPFCControl class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TUPFCControlObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
-end;
-
-{--------------------------------------------------------------------------}
-function TUPFCControl.Edit: Integer;
+function TUPFCControl.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
+ Obj: TObj;
begin
-
- // continue parsing WITH contents of Parser
- DSS.ActiveUPFCControlObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveUPFCControlObj;
-
- Result := 0;
-
- with DSS.ActiveUPFCControlObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 364);
- 1:
- InterpretTStringListArray(Param, FUPFCNameList);
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveUPFCControlObj, ParamPointer - NumPropsthisClass)
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
- // RecalcElementData;
- end;
-
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-
-{--------------------------------------------------------------------------}
-function TUPFCControl.MakeLike(const UPFCControlName: String): Integer;
+procedure TUPFCControlObj.MakeLike(OtherPtr: Pointer);
var
- OtherUPFCControl: TUPFCControlObj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this UPFCControl name in the present collection}
- OtherUPFCControl := Find(UPFCControlName);
- if OtherUPFCControl <> NIL then
- with DSS.ActiveUPFCControlObj do
- begin
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
- NPhases := OtherUPFCControl.Fnphases;
- NConds := OtherUPFCControl.Fnconds; // Force Reallocation of terminal stuff
-
- ElementName := OtherUPFCControl.ElementName;
- ControlledElement := OtherUPFCControl.ControlledElement; // Pointer to target circuit element
- MonitoredElement := OtherUPFCControl.MonitoredElement; // Pointer to target circuit element
-
- ElementTerminal := OtherUPFCControl.ElementTerminal;
-
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherUPFCControl.PropertyValue[i];
-
- end
- else
- DoSimpleMsg('Error in UPFCControl MakeLike: "' + UPFCControlName + '" Not Found.', 370);
+ ControlledElement := Other.ControlledElement; // Pointer to target circuit element
+ MonitoredElement := Other.MonitoredElement; // Pointer to target circuit element
+ ElementTerminal := Other.ElementTerminal;
end;
-
-{==========================================================================}
-{ TUPFCControlObj }
-{==========================================================================}
-
-
-{--------------------------------------------------------------------------}
constructor TUPFCControlObj.Create(ParClass: TDSSClass; const UPFCControlName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(UPFCControlName);
+ Name := AnsiLowerCase(UPFCControlName);
DSSObjType := ParClass.DSSClassType;
FUPFCNameList := TSTringList.Create;
@@ -258,93 +165,24 @@ constructor TUPFCControlObj.Create(ParClass: TDSSClass; const UPFCControlName: S
destructor TUPFCControlObj.Destroy;
begin
- ElementName := '';
inherited Destroy;
end;
-{--------------------------------------------------------------------------}
procedure TUPFCControlObj.RecalcElementData;
-var
- DevIndex: Integer;
begin
- {Check for existence of monitored element}
-
- DevIndex := GetCktElementIndex(ElementName); // Global function
- if DevIndex <= 0 then
- begin
- DoSimpleMsg('Monitored Element in UPFCControl.' + Name + ' does not exist:"' + ElementName + '"', 372);
- Exit;
- end;
-
- MonitoredElement := ActiveCircuit.CktElements.Get(DevIndex);
- if ElementTerminal > MonitoredElement.Nterms then
- begin
- DoErrorMsg('UPFCControl: "' + Name + '"',
- 'Terminal no. "' + '" does not exist.',
- 'Re-specify terminal no.', 371);
- end
- else
- begin
- // Sets name of i-th terminal's connected bus in UPFCControl's buslist
- Setbus(1, MonitoredElement.GetBus(ElementTerminal));
- end;
end;
-procedure TUPFCControlObj.MakePosSequence;
+procedure TUPFCControlObj.MakePosSequence();
begin
if MonitoredElement <> NIL then
begin
- Nphases := ControlledElement.NPhases;
+ FNphases := ControlledElement.NPhases;
Nconds := FNphases;
Setbus(1, MonitoredElement.GetBus(ElementTerminal));
end;
inherited;
end;
-{--------------------------------------------------------------------------}
-procedure TUPFCControlObj.CalcYPrim;
-begin
- // leave YPrims as nil and they will be ignored
- // Yprim is zeroed when created. Leave it as is.
- // IF YPrim=nil THEN YPrim := TcMatrix.CreateMatrix(Yorder);
-end;
-
-
-{--------------------------------------------------------------------------}
-procedure TUPFCControlObj.GetCurrents(Curr: pComplexArray);
-var
- i: Integer;
-begin
-
- for i := 1 to Fnconds do
- Curr^[i] := CZERO;
-
-end;
-
-{--------------------------------------------------------------------------}
-procedure TUPFCControlObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
- if Complete then
- begin
- FSWriteln(F);
- end;
-
-end;
-
-
-{--------------------------------------------------------------------------}
procedure TUPFCControlObj.DoPendingAction;
var
i: Integer;
@@ -360,7 +198,6 @@ procedure TUPFCControlObj.DoPendingAction;
end;
end;
-{--------------------------------------------------------------------------}
procedure TUPFCControlObj.Sample;
var
Update: Boolean;
@@ -390,14 +227,6 @@ procedure TUPFCControlObj.Sample;
end;
end;
-
-procedure TUPFCControlObj.InitPropertyValues(ArrayOffset: Integer);
-begin
- PropertyValue[1] := '[]'; //'UPFC List';
-
- inherited InitPropertyValues(NumPropsThisClass);
-end;
-
function TUPFCControlObj.MakeUPFCList: Boolean;
var
obj: TUPFCObj;
@@ -413,22 +242,22 @@ function TUPFCControlObj.MakeUPFCList: Boolean;
begin // Name list is defined - Use it
for i := 1 to ListSize do
begin
- obj := DSS.UPFCClass.Find(FUPFCNameList.Strings[i - 1]);
+ obj := ParentClass.Find(FUPFCNameList.Strings[i - 1]);
if Assigned(obj) and obj.Enabled then
- UPFCList.New := obj;
+ UPFCList.Add(obj);
end;
end
else // No list given
begin
{Search through the entire circuit for enabled UPFCs and add them to the list}
- for i := 1 to DSS.UPFCClass.ElementCount do
+ for i := 1 to ParentClass.ElementCount do
begin
- obj := DSS.UPFCClass.ElementList.Get(i);
+ obj := ParentClass.ElementList.Get(i);
// Checks if it's enabled
if obj.Enabled then
- UPFCList.New := obj;
+ UPFCList.Add(obj);
end;
{Allocate uniform weights}
@@ -445,18 +274,11 @@ function TUPFCControlObj.MakeUPFCList: Boolean;
if UPFCList.Count > 0 then
Result := TRUE;
-
end;
-
procedure TUPFCControlObj.Reset;
begin
// inherited;
-
end;
-
-initialization
-
-
-end.
+end.
\ No newline at end of file
diff --git a/src/Executive/ExecCommands.pas b/src/Executive/ExecCommands.pas
index a16779eb6..d36a9cc1b 100644
--- a/src/Executive/ExecCommands.pas
+++ b/src/Executive/ExecCommands.pas
@@ -12,21 +12,153 @@ interface
uses
Command, DSSClass;
-const
-{$IFNDEF DSS_CAPI_PM}
- NumExecCommands = 118;
-{$ELSE}
- NumExecCommands = 125;
+type
+ //Main executive commands
+ {$SCOPEDENUMS ON}
+ TExecCommand = (
+ INVALID = 0,
+ New = 1,
+ Edit = 2,
+ More = 3,
+ M = 4,
+ tilde = 5, // "~"
+ Select = 6,
+ Save = 7,
+ Show = 8,
+ Solve = 9,
+ Enable = 10,
+ Disable = 11,
+ Plot = 12,
+ Reset = 13,
+ Compile = 14,
+ SetOpt = 15, // "Set" = Set DSS Options
+ Dump = 16, // Debug dump
+ Open = 17, // Open a device terminal conductor
+ Close = 18, // Close a device terminal conductor
+ DoubleSlash = 19, // "//" Comment
+ Redirect = 20,
+ Help = 21,
+ Quit = 22,
+ questionmark = 23, // "?" = Property Value inquiry
+ Next = 24,
+ Panel = 25,
+ Sample = 26,
+ Clear = 27,
+ About = 28,
+ Calcvoltagebases = 29, // Computes voltage bases
+ SetkVBase = 30, // Set kV Base at a Bus
+ BuildY = 31, // forces Rebuild of Y matrix right now
+ Get = 32, // returns values set WITH Set command
+ Init = 33,
+ Export = 34,
+ Fileedit = 35,
+ Voltages = 36,
+ Currents = 37,
+ Powers = 38,
+ Seqvoltages = 39,
+ Seqcurrents = 40,
+ Seqpowers = 41,
+ Losses = 42,
+ Phaselosses = 43,
+ Cktlosses = 44,
+ Allocateloads = 45,
+ Formedit = 46,
+ Totals = 47, // Total all energymeters
+ Capacity = 48, // Find upper kW limit of system for present year
+ Classes = 49, // List of intrinsic classes
+ Userclasses = 50, // List of user-defined classes
+ Zsc = 51,
+ Zsc10 = 52,
+ ZscRefresh = 53,
+ Ysc = 54,
+ puvoltages = 55,
+ VarValues = 56,
+ Varnames = 57,
+ Buscoords = 58,
+ MakeBusList = 59,
+ MakePosSeq = 60,
+ Reduce = 61,
+ Interpolate = 62,
+ AlignFile = 63,
+ TOP = 64, //TODO: remove
+ Rotate = 65,
+ Vdiff = 66,
+ Summary = 67,
+ Distribute = 68,
+ DI_plot = 69,
+ Comparecases = 70,
+ YearlyCurves = 71,
+ CD = 72,
+ Visualize = 73,
+ CloseDI = 74,
+ DOScmd = 75,
+ Estimate = 76,
+ Reconductor = 77,
+ _InitSnap = 78,
+ _SolveNoControl = 79,
+ _SampleControls = 80,
+ _DoControlActions = 81,
+ _ShowControlQueue = 82,
+ _SolveDirect = 83,
+ _SolvePFlow = 84,
+ AddBusMarker = 85,
+ Uuids = 86,
+ SetLoadAndGenKV = 87,
+ CvrtLoadshapes = 88,
+ NodeDiff = 89,
+ Rephase = 90,
+ SetBusXY = 91,
+ UpdateStorage = 92,
+ Obfuscate = 93,
+ LatLongCoords = 94,
+ BatchEdit = 95,
+ Pstcalc = 96,
+ Variable = 97,
+ ReprocessBuses = 98,
+ ClearBusMarkers = 99,
+ RelCalc = 100,
+ vr = 101, // var
+ Cleanup = 102,
+ FinishTimeStep = 103,
+ NodeList = 104,
+ Connect = 105, //TODO: remove
+ Disconnect = 106, //TODO: remove
+ Remove = 107,
+ CalcIncMatrix = 108,
+ CalcIncMatrix_O = 109,
+ Refine_BusLevels = 110,
+ CalcLaplacian = 111,
+ ExportOverloads = 112,
+ ExportVViolations = 113,
+ Zsc012 = 114,
+ AllPCEatBus,
+ AllPDEatBus,
+ TotalPowers,
+ GISCoords,
+ ClearAll,
+ COMHelp
+{$IFDEF DSS_CAPI_PM}
+ ,
+ NewActor,
+ Wait,
+ SolveAll
+ {$IFDEF DSS_CAPI_ADIAKOPTICS}
+ ,
+ AggregateProfiles,
+ Tear_Circuit
+ {$ENDIF}
+ ,
+ Abort,
+ Clone
{$ENDIF}
+ );
+const
+ NumExecCommands = ord(High(TExecCommand));
var
+ ExecCommand: array[1..NumExecCommands] of String;
- ExecCommand,
- CommandHelp: array[1..NumExecCommands] of String;
-
- CommandList: TCommandList;
-
-procedure ProcessCommand(DSS: TDSSContext; const CmdLine: String);
+procedure ProcessCommand({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext; const CmdLine: String; LineNum: Integer = -1);
procedure DefineCommands;
procedure DisposeStrings;
@@ -51,555 +183,57 @@ implementation
DSSClassDefs,
EnergyMeter,
MemoryMap_lib,
- KLUSolve
-{$IFDEF DSS_CAPI_PM}
+ TypInfo,
+ KLUSolve,
+ Solution
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
, Diakoptics
, sparse_math
{$ENDIF}
;
-{$IFDEF DSS_CAPI_PM}
-const
- CMD_NewActor = 119;
- CMD_ClearAll = 120;
- CMD_Wait = 121;
- CMD_SolveAll = 122;
- CMD_Tear_Circuit = 123;
- CMD_Abort = 124;
- CMD_Clone = 125;
-
-
-
-{$ENDIF}
+type
+ Cmd = TExecCommand;
procedure DefineCommands;
-
+var
+ info: Pointer;
+ i: Integer;
begin
- {Main executive commands}
- ExecCommand[1] := 'New';
- ExecCommand[2] := 'Edit';
- ExecCommand[3] := 'More';
- ExecCommand[4] := 'M';
- ExecCommand[5] := '~';
- ExecCommand[6] := 'Select';
- ExecCommand[7] := 'Save';
- ExecCommand[8] := 'Show';
- ExecCommand[9] := 'Solve';
- ExecCommand[10] := 'Enable';
- ExecCommand[11] := 'Disable';
- ExecCommand[12] := 'Plot';
- ExecCommand[13] := 'Reset';
- ExecCommand[14] := 'Compile';
- ExecCommand[15] := 'Set'; // Set DSS Options
- ExecCommand[16] := 'Dump'; // Debug dump
- ExecCommand[17] := 'Open'; // Open a device terminal conductor
- ExecCommand[18] := 'Close'; // Close a device terminal conductor
- ExecCommand[19] := '//'; // Comment
- ExecCommand[20] := 'Redirect';
- ExecCommand[21] := 'Help';
- ExecCommand[22] := 'Quit';
- ExecCommand[23] := '?'; // Property Value inquiry
- ExecCommand[24] := 'Next';
- ExecCommand[25] := 'Panel';
- ExecCommand[26] := 'Sample';
- ExecCommand[27] := 'Clear';
- ExecCommand[28] := 'About';
- ExecCommand[29] := 'Calcvoltagebases'; // Computes voltage bases
- ExecCommand[30] := 'SetkVBase'; // Set kV Base at a Bus
- ExecCommand[31] := 'BuildY'; // forces Rebuild of Y matrix right now
- ExecCommand[32] := 'Get'; // returns values set WITH Set command
- ExecCommand[33] := 'Init';
- ExecCommand[34] := 'Export';
- ExecCommand[35] := 'Fileedit';
- ExecCommand[36] := 'Voltages';
- ExecCommand[37] := 'Currents';
- ExecCommand[38] := 'Powers';
- ExecCommand[39] := 'Seqvoltages';
- ExecCommand[40] := 'Seqcurrents';
- ExecCommand[41] := 'Seqpowers';
- ExecCommand[42] := 'Losses';
- ExecCommand[43] := 'Phaselosses';
- ExecCommand[44] := 'Cktlosses';
- ExecCommand[45] := 'Allocateloads';
- ExecCommand[46] := 'Formedit';
- ExecCommand[47] := 'Totals'; // Total all energymeters
- ExecCommand[48] := 'Capacity'; // Find upper kW limit of system for present year
- ExecCommand[49] := 'Classes'; // List of intrinsic classes
- ExecCommand[50] := 'Userclasses'; // List of user-defined classes
- ExecCommand[51] := 'Zsc';
- ExecCommand[52] := 'Zsc10';
- ExecCommand[53] := 'ZscRefresh';
- ExecCommand[54] := 'Ysc';
- ExecCommand[55] := 'puvoltages';
- ExecCommand[56] := 'VarValues';
- ExecCommand[57] := 'Varnames';
- ExecCommand[58] := 'Buscoords';
- ExecCommand[59] := 'MakeBusList';
- ExecCommand[60] := 'MakePosSeq';
- ExecCommand[61] := 'Reduce';
- ExecCommand[62] := 'Interpolate';
- ExecCommand[63] := 'AlignFile';
- ExecCommand[64] := 'TOP';
- ExecCommand[65] := 'Rotate';
- ExecCommand[66] := 'Vdiff';
- ExecCommand[67] := 'Summary';
- ExecCommand[68] := 'Distribute';
- ExecCommand[69] := 'DI_plot';
- ExecCommand[70] := 'Comparecases';
- ExecCommand[71] := 'YearlyCurves';
- ExecCommand[72] := 'CD';
- ExecCommand[73] := 'Visualize';
- ExecCommand[74] := 'CloseDI';
- ExecCommand[75] := 'DOScmd';
- ExecCommand[76] := 'Estimate';
- ExecCommand[77] := 'Reconductor';
- ExecCommand[78] := '_InitSnap';
- ExecCommand[79] := '_SolveNoControl';
- ExecCommand[80] := '_SampleControls';
- ExecCommand[81] := '_DoControlActions';
- ExecCommand[82] := '_ShowControlQueue';
- ExecCommand[83] := '_SolveDirect';
- ExecCommand[84] := '_SolvePFlow';
- ExecCommand[85] := 'AddBusMarker';
-
- ExecCommand[86] := 'Uuids';
- ExecCommand[87] := 'SetLoadAndGenKV';
- ExecCommand[88] := 'CvrtLoadshapes';
- ExecCommand[89] := 'NodeDiff';
- ExecCommand[90] := 'Rephase';
- ExecCommand[91] := 'SetBusXY';
- ExecCommand[92] := 'UpdateStorage';
- ExecCommand[93] := 'Obfuscate';
- ExecCommand[94] := 'LatLongCoords';
- ExecCommand[95] := 'BatchEdit';
- ExecCommand[96] := 'Pstcalc';
- ExecCommand[97] := 'Variable';
- ExecCommand[98] := 'ReprocessBuses';
- ExecCommand[99] := 'ClearBusMarkers';
- ExecCommand[100] := 'RelCalc';
- ExecCommand[101] := 'var';
- ExecCommand[102] := 'Cleanup';
- ExecCommand[103] := 'FinishTimeStep';
- ExecCommand[104] := 'NodeList';
- ExecCommand[105] := 'Connect';
- ExecCommand[106] := 'Disconnect';
- ExecCommand[107] := 'Remove';
- ExecCommand[108] := 'CalcIncMatrix';
- ExecCommand[109] := 'CalcIncMatrix_O';
- ExecCommand[110] := 'Refine_BusLevels';
- ExecCommand[111] := 'CalcLaplacian';
- ExecCommand[112] := 'ExportOverloads';
- ExecCommand[113] := 'ExportVViolations';
- ExecCommand[114] := 'Zsc012';
- ExecCommand[115] := 'AggregateProfiles';
- ExecCommand[116] := 'AllPCEatBus';
- ExecCommand[117] := 'AllPDEatBus';
- ExecCommand[118] := 'TotalPowers';
-{$IFDEF DSS_CAPI_PM}
- ExecCommand[CMD_NewActor] := 'NewActor';
- ExecCommand[CMD_ClearAll] := 'ClearAll';
- ExecCommand[CMD_Wait] := 'Wait';
- ExecCommand[CMD_SolveAll] := 'SolveAll';
- ExecCommand[CMD_Tear_Circuit] := 'Tear_Circuit';
- ExecCommand[CMD_Abort] := 'Abort';
- ExecCommand[CMD_Clone] := 'Clone';
-{$ENDIF}
-
- CommandHelp[1] := 'Create a new object within the DSS. Object becomes the ' +
- 'active object' + CRLF +
- 'Example: New Line.line1 ...';
- CommandHelp[2] := 'Edit an object. The object is selected and it then becomes the active object.' + CRLF + CRLF +
- 'Note that Edit is the default command. You many change a property value simply by ' +
- 'giving the full property name and the new value, for example:' + CRLF + CRLF +
- 'line.line1.r1=.04' + CRLF +
- 'vsource.source.kvll=230';
- CommandHelp[3] := 'Continuation of editing on the active object.';
- CommandHelp[4] := 'Continuation of editing on the active object. An abbreviation for More';
- CommandHelp[5] := 'Continuation of editing on the active object. An abbreviation.' + CRLF +
- CRLF +
- 'Example:' + CRLF +
- 'New Line.Line1 Bus1=aaa bus2=bbb' + CRLF +
- '~ R1=.058' + CRLF +
- '~ X1=.1121';
- CommandHelp[6] := 'Selects an element and makes it the active element. You can also specify the ' +
- 'active terminal (default = 1).' + CRLF + CRLF +
- 'Syntax:' + CRLF +
- 'Select [element=]elementname [terminal=]terminalnumber ' + CRLF + CRLF +
- 'Example:' + CRLF +
- 'Select Line.Line1 ' + CRLF +
- '~ R1=.1' + CRLF + '(continue editing)' + CRLF + CRLF +
- 'Select Line.Line1 2 ' + CRLF +
- 'Voltages (returns voltages at terminal 2 in Result)';
- CommandHelp[7] := '{Save [class=]{Meters | Circuit | Voltages | (classname)} [file=]filename [dir=]directory ' + CRLF + CRLF +
- 'Default class = Meters, which saves the present values in both monitors and energy meters in the active circuit. ' + CRLF + CRLF +
- '"Save Circuit" saves the present enabled circuit elements to the specified subdirectory in standard DSS form ' +
- 'with a Master.txt file and separate files for each class of data. ' + CRLF + CRLF +
- 'If Dir= not specified a unique name based on the circuit name is created automatically. ' + CRLF + CRLF +
- 'If Dir= is specified, any existing files are overwritten. ' + CRLF + CRLF +
- '"Save Voltages" saves the present solution in a simple CSV format in a file called DSS_SavedVoltages. ' +
- 'Used for VDIFF command.' + CRLF + CRLF +
- 'Any class can be saved to a file. If no filename specified, the classname is used.';
- CommandHelp[8] := 'Writes selected results to a text file and brings ' +
- 'up the default text editor (see Set Editor=....) with the file for you to browse.' + CRLF + CRLF +
- 'See separate help on Show command. ' + CRLF + CRLF +
- 'Default is "show voltages LN Seq". ';
- CommandHelp[9] := 'Perform the solution of the present solution mode. You can set any option ' +
- 'that you can set with the Set command (see Set). ' +
- 'The Solve command is virtually synonymous with the Set command except that ' +
- 'a solution is performed after the options are processed.';
- CommandHelp[10] := 'Enables a circuit element or entire class. Example:' + CRLF +
- 'Enable load.loadxxx' + CRLF +
- 'Enable generator.* (enables all generators)';
- CommandHelp[11] := 'Disables a circuit element or entire class. Example:' + CRLF +
- 'Disable load.loadxxx' + CRLF +
- 'Disable generator.* (Disables all generators)' + CRLF + CRLF +
- 'The item remains defined, but is not included in the solution.';
- CommandHelp[12] := 'Plots circuits and results in a variety of manners. See separate Plot command help.';
- CommandHelp[13] := '{MOnitors | MEters | Faults | Controls | Eventlog | Keeplist |(no argument) } Resets all Monitors, Energymeters, etc. ' +
- 'If no argument specified, resets all options listed.';
- CommandHelp[14] := 'Reads the designated file name containing DSS commands ' +
- 'and processes them as if they were entered directly into the command line. ' +
- 'The file is said to be "compiled." ' +
- 'Similar to "redirect" except changes the default directory to the path of the specified file.' + CRLF + CRLF +
- 'Syntax:' + CRLF +
- 'Compile filename';
- CommandHelp[15] := 'Used to set various DSS solution modes and options. You may also set the options with the Solve command. ' +
- 'See "Options" for help.';
- CommandHelp[16] := 'Display the properties of either a specific DSS object or a complete dump ' +
- 'on all variables in the problem (Warning! Could be very large!).' +
- ' Brings up the default text editor with the text file written by this command.' + CRLF +
- ' Syntax: dump [class.obj] [debug]' + CRLF +
- ' Examples:' + CRLF + CRLF +
- ' Dump line.line1 ' + CRLF +
- ' Dump solution (dumps all solution vars) ' + CRLF +
- ' Dump commands (dumps all commands to a text file) ' + CRLF +
- ' Dump transformer.* (dumps all transformers)' + CRLF +
- ' Dump ALLOCationfactors (load allocation factors)' + CRLF +
- ' Dump Buslist (bus name hash list)' + CRLF +
- ' Dump Devicelist (Device name hash list)' + CRLF +
- ' Dump (dumps all objects in circuit) ';
- //' Dump debug'; // Debug dump
- CommandHelp[17] := 'Opens the specified terminal and conductor of the specified circuit element. ' +
- 'If the conductor is not specified, all phase conductors of the terminal are opened.' + CRLF + CRLF +
- 'Examples:' + CRLF +
- 'Open line.line1 2 ' + CRLF +
- '(opens all phases of terminal 2)' + CRLF + CRLF +
- 'Open line.line1 2 3' + CRLF +
- '(opens the 3rd conductor of terminal 2)';
- CommandHelp[18] := 'Opposite of the Open command.'; // Close a device terminal conductor
- CommandHelp[19] := 'Comment. Command line is ignored.'; // Comment
- CommandHelp[20] := 'Reads the designated file name containing DSS commands ' +
- 'and processes them as if they were entered directly into the command line. ' +
- 'Similar to "Compile", but leaves current directory where it was when Redirect command is invoked.' +
- 'Can temporarily change to subdirectories if nested Redirect commands require.' + Crlf + crlf +
- 'ex: redirect filename';
- CommandHelp[21] := 'Gives this display.';
- CommandHelp[22] := 'Shuts down DSS unless this is the DLL version. Then it does nothing; DLL parent is responsible for shutting down the DLL.';
- CommandHelp[23] := 'Inquiry for property value. Result is put into GlobalReault and can be seen in the Result Window. ' +
- 'Specify the full property name.' + CRLF + CRLF +
- 'Example: ? Line.Line1.R1' + CRLF + CRLF +
- 'Note you can set this property merely by saying:' + CRLF +
- 'Line.line1.r1=.058'; // Property Value inquiry
- CommandHelp[24] := '{Year | Hour | t} Increments year, hour, or time as specified. If "t" is ' +
- 'specified, then increments time by current step size.';
- CommandHelp[25] := 'Displays main control panel window.';
- CommandHelp[26] := 'Force all monitors and meters to take a sample for the most recent solution. Keep in mind that meters will perform integration.';
- CommandHelp[27] := 'Clear all circuits currently in memory.';
- CommandHelp[28] := 'Display "About Box". (Result string set to Version string.)';
- CommandHelp[29] := 'Calculates voltagebase for buses based on voltage bases defined ' +
- 'with Set voltagebases=... command.';
- CommandHelp[30] := 'Command to explicitly set the base voltage for a bus. ' +
- 'Bus must be previously defined. Parameters in order are:' + crlf +
- 'Bus = {bus name}' + Crlf +
- 'kVLL = (line-to-line base kV)' + crlf +
- 'kVLN = (line-to-neutral base kV)' + Crlf + Crlf +
- 'kV base is normally given in line-to-line kV (phase-phase). ' +
- 'However, it may also be specified by line-to-neutral kV.' + crlf +
- 'The following exampes are equivalent:' + crlf + crlf +
- 'setkvbase Bus=B9654 kVLL=13.2' + crlf +
- 'setkvbase B9654 13.2' + crlf +
- 'setkvbase B9654 kvln=7.62';
- CommandHelp[31] := 'Forces rebuild of Y matrix upon next Solve command regardless of need. ' +
- 'The usual reason for doing this would be to reset the matrix for another ' +
- 'load level when using LoadModel=PowerFlow (the default) when the system is difficult to ' +
- 'solve when the load is far from its base value. Works by invalidating the Y primitive ' +
- 'matrices for all the Power Conversion elements.';
- CommandHelp[32] := 'Returns DSS property values set using the Set command. ' +
- 'Result is returne in Result property of the Text interface. ' + CRLF + CRLF +
- 'VBA Example:' + CRLF + CRLF +
- 'DSSText.Command = "Get mode"' + CRLF +
- 'Answer = DSSText.Result' + CRLF + CRLF +
- 'Multiple properties may be requested on one get. The results are appended ' +
- 'and the individual values separated by commas.' + CRLF + CRLF +
- 'See help on Set command for property names.';
- CommandHelp[33] := 'This command forces reinitialization of the solution for the next Solve command. ' +
- 'To minimize iterations, most solutions start with the previous solution unless there ' +
- 'has been a circuit change. However, if the previous solution is bad, it may be necessary ' +
- 'to re-initialize. In most cases, a re-initiallization results in a zero-load power flow ' +
- 'solution with only the series power delivery elements considered.';
- CommandHelp[34] := 'Export various solution values to CSV (or XML) files for import into other programs. ' +
- 'Creates a new file except for Energymeter and Generator objects, for which ' +
- 'the results for each device of this class are APPENDED to the CSV File. You may export to ' +
- 'a specific file by specifying the file name as the LAST parameter on the line. For example:' + CRLF + CRLF +
- ' Export Voltage Myvoltagefile.CSV' + CRLF + CRLF +
- 'Otherwise, the default file names shown in the Export help are used. ' +
- 'For Energymeter and Generator, specifying the switch "/multiple" (or /m) for the file name will cause ' +
- 'a separate file to be written for each meter or generator. ' +
- 'The default is for a single file containing all elements.' + CRLF + CRLF +
- 'May be abreviated Export V, Export C, etc. Default is "V" for voltages.' +
- ' If Set ShowExport=Yes, the output file will be automatically displayed in the default editor. ' +
- 'Otherwise, you must open the file separately. The name appears in the Result window.';
- CommandHelp[35] := 'Edit specified file in default text file editor (see Set Editor= option).' + CRLF + CRLF +
- 'Fileedit EXP_METERS.CSV (brings up the meters export file)' + CRLF + CRLF +
- '"FileEdit" may be abbreviated to a unique character string.';
- CommandHelp[36] := 'Returns the voltages for the ACTIVE BUS in the Result string. ' +
- 'For setting the active Bus, use the Select command or the Set Bus= option. ' +
- 'Returned as magnitude and angle quantities, comma separated, one set per conductor of the terminal.';
- CommandHelp[37] := 'Returns the currents for each conductor of ALL terminals of the active circuit element in the Result string. ' +
- '(See Select command.)' +
- 'Returned as comma-separated magnitude and angle.';
- CommandHelp[38] := 'Returns the powers (complex) going into each conductors of ALL terminals of the active circuit element in the Result string. ' +
- '(See Select command.)' +
- 'Returned as comma-separated kW and kvar.';
- CommandHelp[39] := 'Returns the sequence voltages at all terminals of the active circuit element (see Select command) in Result string. Returned as comma-separated magnitude only values.' +
- 'Order of returned values: 0, 1, 2 (for each terminal).';
- CommandHelp[40] := 'Returns the sequence currents into all terminals of the active circuit element (see Select command) in Result string. Returned as comma-separated magnitude only values.' +
- 'Order of returned values: 0, 1, 2 (for each terminal).';
- CommandHelp[41] := 'Returns the sequence powers into all terminals of the active circuit element (see Select command) in Result string. Returned as comma-separated kw, kvar pairs.' +
- 'Order of returned values: 0, 1, 2 (for each terminal).';
- CommandHelp[42] := 'Returns the total losses for the active circuit element (see Select command) ' +
- 'in the Result string in kW, kvar.';
- CommandHelp[43] := 'Returns the losses for the active circuit element (see Select command) ' +
- 'for each PHASE in the Result string in comma-separated kW, kvar pairs.';
- CommandHelp[44] := 'Returns the total losses for the active circuit in the Result string in kW, kvar.';
- CommandHelp[45] := 'Estimates the allocation factors for loads that are defined using the XFKVA property. ' +
- 'Requires that energymeter objects be defined with the PEAKCURRENT property set. ' +
- 'Loads that are not in the zone of an energymeter cannot be allocated.';
- CommandHelp[46] := 'FormEdit [class.object]. Brings up form editor on active DSS object.';
- CommandHelp[47] := 'Totals all EnergyMeter objects in the circuit and reports register totals in the result string.';
- CommandHelp[48] := 'Find the maximum load the active circuit can serve in the PRESENT YEAR. Uses the EnergyMeter objects with the registers ' +
- 'set with the SET UEREGS= (..) command for the AutoAdd functions. Syntax (defaults shown):' + CRLF + CRLF +
- 'capacity [start=]0.9 [increment=]0.005' + CRLF + CRLF +
- 'Returns the metered kW (load + losses - generation) and per unit load multiplier for the loading level at which something in the system reports an overload or undervoltage. ' +
- 'If no violations, then it returns the metered kW for peak load for the year (1.0 multiplier). ' +
- 'Aborts and returns 0 if no energymeters.';
- CommandHelp[49] := 'List of intrinsic DSS Classes. Returns comma-separated list in Result variable.';
- CommandHelp[50] := 'List of user-defined DSS Classes. Returns comma-separated list in Result variable.';
- CommandHelp[51] := 'Returns full Zsc matrix for the ACTIVE BUS in comma-separated complex number form.';
- CommandHelp[52] := 'Returns symmetrical component impedances, Z1, Z0 for the ACTIVE BUS in comma-separated R+jX form.';
- CommandHelp[53] := 'Refreshes Zsc matrix for the ACTIVE BUS.';
- CommandHelp[54] := 'Returns full Ysc matrix for the ACTIVE BUS in comma-separated complex number form G + jB.';
- CommandHelp[55] := 'Just like the Voltages command, except the voltages are in per unit if the kVbase at the bus is defined.';
- CommandHelp[56] := 'Returns variable values for active element if PC element. Otherwise, returns null.';
- CommandHelp[57] := 'Returns variable names for active element if PC element. Otherwise, returns null.';
- CommandHelp[58] := 'Define x,y coordinates for buses. Execute after Solve or MakeBusList command is executed so that bus lists are defined.' +
- 'Reads coordinates from a CSV file with records of the form: busname, x, y.' + CRLF + CRLF +
- 'Example: BusCoords [file=]xxxx.csv';
- CommandHelp[59] := 'Updates the buslist, if needed, using the currently enabled circuit elements. (This happens automatically for Solve command.)' +
- ' See ReprocessBuses';
- CommandHelp[60] := 'Attempts to convert present circuit model to a positive sequence equivalent. ' +
- 'It is recommended to Save the circuit after this and edit the saved version to correct possible misinterpretations.';
- CommandHelp[61] := '{All | MeterName} Default is "All". Reduce the circuit according to reduction options. ' +
- 'See "Set ReduceOptions" and "Set Keeplist" options.' +
- 'Energymeter objects actually perform the reduction. "All" causes all meters to reduce their zones.';
- CommandHelp[62] := '{All | MeterName} Default is "All". Interpolates coordinates for missing bus coordinates in meter zone';
- CommandHelp[63] := 'Alignfile [file=]filename. Aligns DSS script files in columns for easier reading.';
- CommandHelp[64] := '[class=]{Loadshape | Tshape | Monitor } [object=]{ALL (Loadshapes only) | objectname}. ' +
- 'Send specified object to TOP. Loadshapes and TShapes must be hourly fixed interval. ' + CRLF + CRLF +
- '**Not supported in DSS Extensions**';
- CommandHelp[65] := 'Usage: Rotate [angle=]nnn. Rotate circuit plotting coordinates by specified angle (degrees). ';
- CommandHelp[66] := 'Displays the difference between the present solution and the last on saved using the SAVE VOLTAGES command.';
- CommandHelp[67] := 'Returns a power flow summary of the most recent solution in the global result string.';
- CommandHelp[68] := 'kw=nn how={Proportional* | Uniform |Random | Skip} skip=nn PF=nn file=filename MW=nn What=[Generator*|Load]' + CRLF + CRLF +
- 'Creates a DSS script file to distribute Generator or Load objects on the system in the manner specified by "how".' + CRLF +
- 'kW = total generation to be distributed (default=1000) ' + CRLF +
- 'how= process name as indicated (default=proportional to load)' + CRLF +
- 'skip = no. of buses to skip for "How=Skip" (default=1)' + CRLF +
- 'PF = power factor for new generators (default=1.0)' + CRLF +
- 'file = name of file to save (default=distgenerators.dss or distloads.dss)' + CRLF +
- 'MW = alternate way to specify kW (default = 1)' + CRLF +
- 'What = what type of device to add, Generator (default) or Load';
- CommandHelp[69] := '[case=]casename [year=]yr [registers=](reg1, reg2,...) [peak=]y/n [meter=]metername' + CRLF +
- 'Plots demand interval (DI) results from yearly simulation cases. ' +
- 'Plots selected registers from selected meter file (default = DI_Totals.CSV). ' +
- 'Peak defaults to NO. If YES, only daily peak of specified registers ' +
- 'is plotted. Example:' + CRLF + CRLF +
- ' DI_Plot basecase year=5 registers=(9,11) no';
- CommandHelp[70] := '[Case1=]casename [case2=]casename [register=](register number) [meter=]{Totals* | SystemMeter | metername}. ' + CRLF +
- 'Compares yearly simulations of two specified cases with respect to the quantity in the designated register ' +
- 'from the designated meter file. ' +
- 'Defaults: Register=9 meter=Totals. Example:' + CRLF + CRLF +
- 'Comparecases base pvgens 10';
- CommandHelp[71] := '[cases=](case1, case2, ...) [registers=](reg1, reg2, ...) [meter=]{Totals* | SystemMeter | metername}' +
- 'Plots yearly curves for specified cases and registers. ' + CRLF +
- 'Default: meter=Totals. Example: ' + CRLF + CRLF +
- 'yearlycurves cases=(basecase, pvgens) registers=9';
- CommandHelp[72] := 'Change default directory to specified directory' + CRLF + CRLF +
- 'CD dirname';
- CommandHelp[73] := '[What=] one of {Currents* | Voltages | Powers} [element=]full_element_name (class.name). ' +
- 'Shows the selected quantity for selected element on a multiphase line drawing in phasor values.';
- CommandHelp[74] := 'Close all DI files ... useful at end of yearly solution where DI files are left open. ' +
- '(Reset and Set Year=nnn will also close the DI files)';
- CommandHelp[75] := 'Do a DOS command. Sends the command "cmd ... " to Windows. Execute the "cmd /?" command ' +
- 'in a DOS window to see the options. To do a DOS command and automatically exit, do ' + CRLF + CRLF +
- 'DOScmd /c ...command string ...' + CRLF + CRLF +
- 'To keep the DOS window open, use /k switch.';
- CommandHelp[76] := 'Execute state estimator on present circuit given present sensor values.';
- CommandHelp[77] := 'Reconductor a line section. Must be in an EnergyMeter zone. ' + CRLF +
- 'Syntax: Reconductor Line1=... Line2=... {LineCode= | Geometry = } EditString="..." NPhases=#' + CRLF +
- 'Line1 and Line2 may be given in any order. All lines in the path between the two are redefined ' +
- 'with either the LineCode or Geometry (not both). You may also add an optional string the alter any other line properties. ' +
- 'The edit string should be enclosed in quotes or parens or brackets.' + CRLF +
- 'Nphases is an optional filter on the number of phases in line segments to change.';
- CommandHelp[78] := 'For step control of solution process: Intialize iteration counters, etc. that normally occurs at the ' +
- 'start of a snapshot solution process.';
- CommandHelp[79] := 'For step control of solution process: Solves the circuit in present state but does not check for control actions.';
- CommandHelp[80] := 'For step control of solution process: Sample the control elements, which push control action requests onto the control queue.';
- CommandHelp[81] := 'For step control of solution process: Pops control actions off the control queue according to the present control mode rules. ' +
- 'Dispatches contol actions to proper control element "DoPendingAction" handlers.';
- CommandHelp[82] := 'For step control of solution process: Show the present control queue contents.';
- CommandHelp[83] := 'For step control of solution process: Invoke direct solution function in DSS. Non-iterative solution of Y matrix and active sources only.';
- CommandHelp[84] := 'For step control of solution process: Invoke iterative power flow solution function of DSS directly.';
- CommandHelp[85] := 'Add a marker to a bus in a circuit plot. Markers must be added before issuing the Plot command. Effect is persistent until circuit is cleared. ' +
- 'See also ClearBusMarkers command. Example: ' + CRLF + CRLF +
- 'ClearBusMarkers !...Clears any previous bus markers' + CRLF +
- 'AddBusMarker Bus=Mybusname code=5 color=Red size=3' + CRLF + CRLF +
- 'You can use any of the standard color names or RGB numbers. See Help on C1 property in Plot command.';
- CommandHelp[86] := 'Read UUIDs (v4) for class names. Tab or comma-delimited file with full object name and UUID';
- CommandHelp[87] := 'Set load and generator object kv to agree with the bus they are connected to using the bus voltage base and connection type.';
- CommandHelp[88] := 'Convert all Loadshapes presently loaded into either files of single or files of double. ' +
- 'Usually files of singles are adequate precision for loadshapes. Syntax:' + CRLF + CRLF +
- 'cvrtloadshapes type=sng (this is the default)' + crlf +
- 'cvrtloadshapes type=dbl' + CRLF + CRLF +
- 'A DSS script for loading the loadshapes from the created files is produced and displayed in the default editor. ';
- CommandHelp[89] := 'Global result is set to voltage difference, volts and degrees, (Node1 - Node2) between any two nodes. Syntax:' + CRLF + CRLF +
- ' NodeDiff Node1=MyBus.1 Node2=MyOtherBus.1';
- CommandHelp[90] := 'Generates a script to change the phase designation of all lines downstream from a start in line. Useful for such things as moving a single-phase ' +
- 'lateral from one phase to another and keep the phase designation consistent for reporting functions that need it to be ' +
- '(not required for simply solving). ' + CRLF + CRLF +
- 'StartLine=... PhaseDesignation="..." EditString="..." ScriptFileName=... StopAtTransformers=Y/N/T/F' + CRLF + CRLF +
- 'Enclose the PhaseDesignation in quotes since it contains periods (dots).' + CRLF +
- 'You may add and optional EditString to edit any other line properties.' + CRLF + CRLF +
- 'Rephase StartLine=Line.L100 PhaseDesignation=".2" EditString="phases=1" ScriptFile=Myphasechangefile.DSS Stop=No';
- CommandHelp[91] := 'Bus=... X=... Y=... Set the X, Y coordinates for a single bus. Prerequisite: Bus must exist as a result of a Solve, CalcVoltageBases, or MakeBusList command.';
- CommandHelp[92] := 'Update Storage elements based on present solution and time interval. ';
- CommandHelp[93] := 'Change Bus and circuit element names to generic values to remove identifying names. Generally, ' +
- 'you will follow this command immediately by a "Save Circuit Dir=MyDirName" command.';
- CommandHelp[94] := 'Define x,y coordinates for buses using Latitude and Longitude values (decimal numbers). Similar to BusCoords command. ' +
- 'Execute after Solve command or MakeBusList command is executed so that bus lists are defined.' +
- 'Reads coordinates from a CSV file with records of the form: busname, Latitude, Longitude.' + CRLF + CRLF +
- 'Example: LatLongCoords [file=]xxxx.csv' + CRLF + CRLF +
- 'Note: Longitude is mapped to x coordinate and Latitude is mapped to y coordinate.';
- CommandHelp[95] := 'Batch edit objects in the same class. Example: BatchEdit Load..* duty=duty_shape' + CRLF +
- 'In place of the object name, supply a PERL regular expression. .* matches all names.' + CRLF +
- 'The subsequent parameter string is applied to each object selected.';
- CommandHelp[96] := 'Pst calculation. PstCalc Npts=nnn Voltages=[array] dt=nnn freq=nn lamp=120 or 230.' + CRLF +
- 'Set Npts to a big enough value to hold the incoming voltage array. ' + CRLF +
- 'dt = time increment in seconds. default is 1' + CRLF +
- 'freq = base frequency in Hz 50 or 60. Default is default base frequency' + CRLF +
- 'Lamp= 120 for North America; 230 for Europe. Default is 120' + CRLF + CRLF +
- 'PSTCalc Npts=1900 V=[file=MyCSVFile.CSV, Col=3, Header=y] dt=1 freq=60 lamp=120';
- CommandHelp[97] := '[name=] MyVariableName [Index=] IndexofMyVariable ' + CRLF + CRLF +
- 'Returns the value of the specified state variable of the active circuit element, if a PCelement. ' +
- 'Returns the value as a string in the Result window or the Text.Result interface if using the COM server. ' + CRLF + CRLF +
- 'You may specify the variable by name or by its index. You can determine the index using the VarNames command. ' +
- 'If any part of the request is invalid, the Result is null.';
- CommandHelp[98] := 'Forces reprocessing of bus definitions whether there has been a change or not. Use for rebuilding meter zone lists ' +
- 'when a line length changes, for example or some other event that would not normally trigger an update to the bus list.';
- CommandHelp[99] := 'Clear all bus markers created with the AddBusMarker command.';
- CommandHelp[100] := '[restore=Y/N]Perform reliability calcs: Failure rates and number of interruptions. ' + CRLF + CRLF +
- 'Optional parameter:' + CRLF + CRLF + 'If restore=y automatic restoration of unfaulted section is assumed.';
- CommandHelp[101] := 'Define and view script variables. Variable names begin with "@"' + CRLF + CRLF +
- 'Usage:' + CRLF + CRLF +
- 'var @varname1=values @varname2=value2 ...' + CRLF +
- 'var @varname1 (shows the value of @varname1)' + CRLF +
- 'var (displays all variabiles and values)' + CRLF + CRLF +
- 'Example of using a variable:' + CRLF + CRLF +
- 'FileEdit @LastFile';
- CommandHelp[102] := 'Force execution of the end-of-time-step cleanup functions that samples/saves meters and updates selected state variables such as storage level';
- CommandHelp[103] := 'Do Cleanup, sample monitors, and increment time.';
- CommandHelp[104] := '[Circuit element name] (Optional) Returns a list of node numbers for all conductors of all terminals of the active circuit element in the Result window or interface.' +
- 'If the optional circuit element name is supplied, the program makes it the active element. Usage:' + CRLF + CRLF +
- 'NodeList' + CRLF +
- 'NodeList Line.Myline';
- CommandHelp[105] := 'Request to create a TCP/IP socket to communicate data with external modules. This function requires the host address and TCP port to connect.';
- CommandHelp[106] := 'Request to terminate a TCP/IP socket. This function requires the host address and TCP port to disconnect.';
- CommandHelp[107] := '{ElementName=} [KeepLoad=Y*/N] [EditString="..."] ' +
- 'Remove (disable) all branches downline from the PDelement named by "ElementName" property. Circuit must have an Energymeter on this branch. ' +
- 'If KeepLoad=Y (default) a new Load element is defined and kW, kvar set to ' +
- 'present power flow solution for the first element eliminated. ' +
- 'The EditString is applied to each new Load element defined. ' + CRLF +
- 'If KeepLoad=N, all downline elements are disabled. Examples: ' + CRLF + CRLF +
- 'Remove Line.Lin3021' + CRLF +
- 'Remove Line.L22 Editstring="Daily=Dailycurve Duty=SolarShape' + CRLF +
- 'Remove Line.L333 KeepLoad=No';
- CommandHelp[108] := 'Calculates the incidence matrix of the Active Circuit';
- CommandHelp[109] := 'Calculates the incidence matrix of the Active Circuit. However, in this case the matrix will be calculated by considering its hierarchical order,' +
- 'which means that the buses order will be generated considering their distribution from the substation to the last load in a radial hierarchy';
- CommandHelp[110] := 'This function takes the bus levels array and traces all the possible paths considering the longest paths from the substation to the longest branches' +
- ' within the circuit. Then, the new paths are filled with 0 to complement the oroginal levels proposed by the calcincmatrix_o command.';
- CommandHelp[111] := 'Calculate the laplacian matrix using the incidence matrix' + CRLF +
- 'previously calculated, this means that before calling this command' + CRLF +
- 'the incidence matrix needs to be calculated using calcincmatrix/calcincmatrix_o';
- CommandHelp[112] := 'Exports the overloads report with the content available at the moment of the call. It only affects the overloads report for the active actor.';
- CommandHelp[113] := 'Exports the voltage violations report with the content available at the moment of the call. It only affects the voltage violations report for the active actor.';
- CommandHelp[114] := 'Returns symmetrical component short circuit impedances Z0, Z1, and Z2 for the ACTIVE 3-PHASE BUS. Determined from Zsc matrix.';
- CommandHelp[115] := 'Aggregates the load shapes in the model using the number of zones given in the argument.' + CRLF +
- 'Use this command when the number of load shapes is considerably big, this algorithm will simplify' + CRLF +
- 'the amount of load shapes in order to make the memory consumption lower for the model.' + CRLF +
- 'The output of this algorithm is a script describing the new load shapes and their application into loads across the model.' + CRLF +
- 'The argument on this command can be: Actual/pu to define the units in which the load profiles are.' + CRLF +
- 'Check the OpenDSS user manual for details';
- CommandHelp[116] := 'Brings back the names of all PCE connected to the bus specified in the argument.' + CRLF +
- 'The command goes as follows:' + CRLF + CRLF +
- 'AllPCEatBus myBus' + CRLF + CRLF +
- 'Where "myBus" is the name of the bus of interest';
- CommandHelp[117] := 'Brings back the names of all PDE connected to the bus specified in the argument.' + CRLF +
- 'The command goes as follows:' + CRLF + CRLF +
- 'AllPDEatBus myBus' + CRLF + CRLF +
- 'Where "myBus" is the name of the bus of interest';
- CommandHelp[118] := 'Returns the total powers (complex) at ALL terminals of the active circuit element in the Result string. '+
- '(See Select command.)' +
- 'Returned as comma-separated kW and kvar.';
-{$IFDEF DSS_CAPI_PM}
- CommandHelp[CMD_NewActor] := 'This command creates a new actor (OpenDSS Instance) and sets the new actor as the active actor. ' +
- 'There can be only 1 circuit per actor. The NewActor command will increment the variable NumOfActors;' +
- ' however, if the number of actors is the same as the number of available CPUs the new actor will not be created ' +
- 'generating an error message. This instruction will deliver the ID of the active actor. This command does not requires a precedent command.';
- CommandHelp[CMD_ClearAll] := 'Clears all the circuits and all the actors, after this instruction there will be only 1 actor (actor 1) and will be the active actor';
- CommandHelp[CMD_Wait] := 'Pauses the scripting thread until all the active actors are Ready to receive new commands (have finished all their tasks and are ready to receive new simulation orders).';
- CommandHelp[CMD_SolveAll] := 'Solves all the circuits (Actors) loaded into memory by the user';
- CommandHelp[CMD_Tear_Circuit] := 'Estimates the buses for tearing the system in many parts as CPUs - 1 are in the local computer, is used for tearing the interconnected circuit into a' +
- ' balanced (same number of nodes) collection of subsystems for the A-Diakoptics algorithm';
- CommandHelp[CMD_Abort] := 'Aborts all the simulations running';
- CommandHelp[CMD_Clone] := 'Clones the active circuit. This command creates as many copies of the active cirucit as indicated in the argument ' +
- 'if the number of requested clones does not overpasses the number of local CPUs. The form of this command is clone X where' +
- 'X is the number of clones to be created';
-{$ENDIF}
+ info := TypeInfo(Cmd);
+ for i := 1 to NumExecCommands do
+ ExecCommand[i] := GetEnumName(info, i);
+ // Replace the ones that don't fit as Pascal identifiers
+ ExecCommand[ord(Cmd.vr)] := 'var';
+ ExecCommand[ord(Cmd.tilde)] := '~';
+ ExecCommand[ord(Cmd.doubleslash)] := '//';
+ ExecCommand[ord(Cmd.questionmark)] := '?';
+ ExecCommand[ord(Cmd.SetOpt)] := 'Set';
end;
-//----------------------------------------------------------------------------
-procedure ProcessCommand(DSS: TDSSContext; const CmdLine: String);
+procedure ProcessCommand({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext; const CmdLine: String; LineNum: Integer);
var
+ CommandList: TCommandList;
ParamPointer: Integer;
ParamName: String;
Param: String;
ObjName, PropName: String;
{$IFDEF DSS_CAPI_PM}
i: Integer;
- PMParent: TDSSContext;
+ PMParent, DSS: TDSSContext;
begin
- PMParent := DSS.GetPrime();
+ PMParent := MainDSS.GetPrime();
+ DSS := MainDSS.ActiveChild;
{$ELSE}
begin
{$ENDIF}
try
-
+ CommandList := DSS.DSSExecutive.CommandList;
DSS.CmdResult := 0;
DSS.ErrorNumber := 0; // Reset Error number
DSS.GlobalResult := '';
-{Load up the parser and process the first parameter only}
+ // Load up the parser and process the first parameter only
DSS.LastCmdLine := CmdLine;
DSS.Parser.CmdString := DSS.LastCmdLine; // Load up command parser
DSS.LastCommandWasCompile := FALSE;
@@ -610,82 +244,72 @@ procedure ProcessCommand(DSS: TDSSContext; const CmdLine: String);
if Length(Param) = 0 then
Exit; // Skip blank line
- // Check for Command verb or Property Value
- // Commands do not have equal signs so ParamName must be zero
+ // Check for Command verb or Property Value
+ // Commands do not have equal signs so ParamName must be zero
if Length(ParamName) = 0 then
ParamPointer := CommandList.GetCommand(Param);
- // Check first for Compile or Redirect and get outta here
+ // Check first for Compile or Redirect and get outta here
case ParamPointer of
- 14:
+ ord(Cmd.Compile), ord(Cmd.Redirect):
begin
with DSS.DSSExecutive do
if RecorderOn then
Write_to_RecorderFile(CRLF + '!*********' + CmdLine);
- DSS.CmdResult := DSS.DSSExecutive.DoRedirect(TRUE);
+ DSS.CmdResult := DSS.DSSExecutive.DoRedirect(ParamPointer = ord(Cmd.Compile));
Exit;
- end;//'Compile';
- 20:
- begin
- with DSS.DSSExecutive do
- if RecorderOn then
- Write_to_RecorderFile(CRLF + '!*********' + CmdLine);
- DSS.CmdResult := DSS.DSSExecutive.DoRedirect(FALSE);
- Exit;
- end; //'Redirect';
+ end;
else // Write everything direct to recorder, if ON
with DSS.DSSExecutive do
if RecorderOn then
Write_to_RecorderFile(CmdLine);
end;
- // Things that are OK to do before a circuit is defined
+ // Things that are OK to do before a circuit is defined
case ParamPointer of
-
- 1:
+ ord(Cmd.New):
DSS.CmdResult := DSS.DSSExecutive.DoNewCmd; // new
- 15:
+ ord(Cmd.SetOpt):
if not Assigned(DSS.ActiveCircuit) then
begin
DoSetCmd_NoCircuit(DSS); // can only call this if no circuit active
Exit; // We exit with either a good outcome or bad
end;
- 19:
-{Do Nothing - comment};
-
- 21:
+ ord(Cmd.DoubleSlash):
+ ; // Do Nothing - comment
+ ord(Cmd.Help):
DSS.CmdResult := DSS.DSSExecutive.DoHelpCmd;
- 22:
+ ord(Cmd.Quit):
;
// if not IsDLL then ExitControlPanel; // Quit in Stand alone version
- 25:
- DoSimpleMsg(DSS, 'Command "panel" supported in DSS Extensions.', 999);
- 27:
+ ord(Cmd.Panel):
+ DoSimpleMsg(DSS, _('Command "panel" supported in DSS Extensions.'), 999);
+ ord(Cmd.Clear):
DSS.DSSExecutive.DoClearCmd;
- 28:
+ ord(Cmd.About):
DSS.DSSExecutive.DoAboutBox;
- 32:
+ ord(Cmd.Get):
if not Assigned(DSS.ActiveCircuit) then
begin
DoGetCmd_NoCircuit(DSS); // can only call this if no circuit active
Exit; // We exit with either a good outcome or bad
end;
- 35:
+ ord(Cmd.Fileedit):
DSS.CmdResult := DSS.DSSExecutive.DoFileEditCmd;
- 49:
+ ord(Cmd.Classes):
DSS.CmdResult := DSS.DSSExecutive.DoClassesCmd;
- 50:
+ ord(Cmd.Userclasses):
DSS.CmdResult := DSS.DSSExecutive.DoUserClassesCmd;
- 63:
+ ord(Cmd.AlignFile):
DSS.CmdResult := DSS.DSSExecutive.DoAlignFileCmd;
- 69:
+ ord(Cmd.DI_plot):
DSS.CmdResult := DSS.DSSExecutive.DoDI_PlotCmd;
- 70:
+ ord(Cmd.Comparecases):
DSS.CmdResult := DSS.DSSExecutive.DoCompareCasesCmd;
- 71:
+ ord(Cmd.YearlyCurves):
DSS.CmdResult := DSS.DSSExecutive.DoYearlyCurvesCmd;
- 72:
+ ord(Cmd.CD):
begin
ParamName := DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
@@ -695,80 +319,63 @@ procedure ProcessCommand(DSS: TDSSContext; const CmdLine: String);
SetDataPath(DSS, Param); // change datadirectory
end
else
- DoSimpleMsg(DSS, 'Directory "' + Param + '" not found.', 282);
+ DoSimpleMsg(DSS, 'Directory "%s" not found.', [Param], 282);
end;
- 75:
- DSS.DSSExecutive.DoADosCmd;
- 88:
+ ord(Cmd.DOScmd):
+ if DSS_CAPI_ALLOW_DOSCMD then
+ DSS.DSSExecutive.DoADosCmd()
+ else
+ DoSimpleMsg(DSS, _('DOScmd is disabled. Enable it via API or set the environment variable DSS_CAPI_ALLOW_DOSCMD=1 before starting the process.'), 283);
+ ord(Cmd.CvrtLoadshapes):
DSS.DSSExecutive.DoCvrtLoadshapesCmd;
-
- 101:
+ ord(Cmd.vr):
DSS.DSSExecutive.DoVarCmd;
-
- 108: // CalcIncMatrix
- begin
- DSS.ActiveCircuit.Solution.Calc_Inc_Matrix();
- end;
- 109: // CalcIncMatrix_O
- begin
- DSS.ActiveCircuit.Solution.Calc_Inc_Matrix_Org();
- end;
- 110: // Refine_BusLevels
- begin
- DSS.ActiveCircuit.Get_paths_4_Coverage();
- DSS.GlobalResult := inttostr(length(DSS.ActiveCircuit.Path_Idx) - 1) + ' new paths detected';
- end;
- 111: // CalcLaplacian
- begin
- with DSS.ActiveCircuit.Solution do
- begin
- Laplacian := IncMat.Transpose(); // Transposes the Incidence Matrix
- Laplacian := Laplacian.multiply(IncMat); // IncMatT*IncMat
- end;
- end;
{$IFDEF DSS_CAPI_PM}
- CMD_NewActor:
+ ord(Cmd.NewActor):
begin
New_Actor_Slot(DSS);
end;
- CMD_ClearAll:
+ ord(Cmd.ClearAll):
DSS.DSSExecutive.DoClearAllCmd;
- CMD_Wait:
+ ord(Cmd.Wait):
+ if PMParent.Parallel_enabled then
+ Wait4Actors(DSS, ALL_ACTORS);
+ ord(Cmd.SolveAll):
begin
- if DSS.Parallel_enabled then
- Wait4Actors(DSS, 0);
- end;
- CMD_SolveAll:
- begin
- PMParent.IsSolveAll := TRUE; //TODO
+ PMParent.IsSolveAll := TRUE;
for i := 0 to PMParent.NumOfActors - 1 do
begin
- DSS.CmdResult := DoSetCmd(PMParent.Children[i], 1);
+ PMParent.ActiveChild := PMParent.Children[i];
+ PMParent.ActiveChild.CmdResult := DoSetCmd(PMParent.ActiveChild, 1);
end;
end;
- CMD_Tear_Circuit:
+{$ELSE}
+ ord(Cmd.ClearAll):
+ DSS.DSSExecutive.DoClearCmd;
+{$ENDIF}
+ ord(Cmd.COMHelp):
begin
- ADiakoptics_Tearing(DSS);
+ DoSimpleMsg(DSS, _('COMHelp is not available on DSS Extensions. You can download "OpenDSS_COM.chm" at https://sourceforge.net/p/electricdss/code/HEAD/tree/trunk/Version8/Distrib/x64/OpenDSS_COM.chm?format=raw as well as other example and documentation files from the official OpenDSS distribution at https://sourceforge.net/p/electricdss/code/HEAD/tree/trunk/Version8/Distrib/ and subfolders. Please see https://dss-extensions.org/ for further links.'), 999);
+ DSS.CmdResult := 0;
end;
-{$ENDIF}
else
if DSS.ActiveCircuit = NIL then
begin
- DoSimpleMsg(DSS, 'You must create a new circuit object first: "new circuit.mycktname" to execute this command.', 301);
+ DoSimpleMsg(DSS, _('You must create a new circuit object first: "new circuit.mycktname" to execute this command.'), 301);
Exit;
end;
end;
- // Now check to see if this is a command or a property reference
+ // Now check to see if this is a command or a property reference
if ParamPointer = 0 then
begin
- {If not a command or the command is unknown, THEN it could be a property of a circuit element}
+ // If not a command or the command is unknown, THEN it could be a property of a circuit element
- {If a command or no text beFORe the = sign, THEN error}
+ // If a command or no text beFORe the = sign, THEN error
if (Length(ParamName) = 0) or (Comparetext(paramName, 'command') = 0) then
begin
- DoSimpleMsg(DSS, 'Unknown Command: "' + Param + '" ' + CRLF + DSS.Parser.CmdString, 302);
+ DoSimpleMsg(DSS, 'Unknown Command: "%s" %s', [Param, CRLF + DSS.Parser.CmdString], 302);
DSS.CmdResult := 1;
end
else
@@ -778,259 +385,289 @@ procedure ProcessCommand(DSS: TDSSContext; const CmdLine: String);
SetObject(DSS, ObjName); // Set active element
if DSS.ActiveDSSObject <> NIL then
begin
- // rebuild command line and pass to editor
- // use quotes to ensure first parameter is interpreted OK after rebuild
+ // rebuild command line and pass to editor
+ // use quotes to ensure first parameter is interpreted OK after rebuild
DSS.Parser.CmdString := PropName + '="' + Param + '" ' + DSS.Parser.Remainder;
- DSS.ActiveDSSClass.Edit;
+ DSS.ActiveDSSClass.Edit(DSS.Parser);
end;
end;
Exit; // Done - don't need to do anything ELSE
end;
- // Process the rest of the commands
+ // Process the rest of the commands
case ParamPointer of
-
- 2:
- DSS.CmdResult := DSS.DSSExecutive.DoEditCmd; // edit
- 3..5:
+ ord(Cmd.CalcIncMatrix):
+ begin
+ DSS.ActiveCircuit.Solution.Calc_Inc_Matrix();
+ end;
+ ord(Cmd.CalcIncMatrix_O):
+ begin
+ DSS.ActiveCircuit.Solution.Calc_Inc_Matrix_Org();
+ end;
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ ord(Cmd.Refine_BusLevels):
+ begin
+ DSS.ActiveCircuit.Get_paths_4_Coverage();
+ DSS.GlobalResult := Format(_('%d new paths detected'), [length(DSS.ActiveCircuit.Path_Idx) - 1]);
+ end;
+{$ENDIF}
+ ord(Cmd.CalcLaplacian):
+ begin
+ with DSS.ActiveCircuit.Solution do
+ begin
+ Laplacian := IncMat.Transpose(); // Transposes the Incidence Matrix
+ Laplacian := Laplacian.multiply(IncMat); // IncMatT*IncMat
+ end;
+ end;
+ ord(Cmd.Edit):
+ DSS.CmdResult := DSS.DSSExecutive.DoEditCmd;
+ ord(Cmd.More), ord(Cmd.M), ord(Cmd.tilde):
DSS.CmdResult := DSS.DSSExecutive.DoMoreCmd; // more , m, ~
- 6:
+ ord(Cmd.Select):
DSS.CmdResult := DSS.DSSExecutive.DoSelectCmd;
- 7:
+ ord(Cmd.Save):
DSS.CmdResult := DSS.DSSExecutive.DoSaveCmd; //'save';
- 8:
+ ord(Cmd.Show):
DSS.CmdResult := DoShowCmd(DSS); //'show';
- 9:
+ ord(Cmd.Solve):
begin
{$IFDEF DSS_CAPI_PM}
- PMParent.IsSolveAll := FALSE; //TODO
- DSS.ActiveCircuit.AD_Init := FALSE; //TODO
+ PMParent.IsSolveAll := FALSE;
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ DSS.ActiveCircuit.AD_Init := FALSE;
+{$ENDIF}
{$ENDIF}
DSS.CmdResult := DoSetCmd(DSS, 1); // changed from DoSolveCmd; //'solve';
end;
- 10:
+ ord(Cmd.Enable):
DSS.CmdResult := DSS.DSSExecutive.DoEnableCmd;
- 11:
+ ord(Cmd.Disable):
DSS.CmdResult := DSS.DSSExecutive.DoDisableCmd;
- 12:
+ ord(Cmd.Plot):
begin
DoPlotCmd(DSS);
DSS.CmdResult := 0;
end;
- 13:
+ ord(Cmd.Reset):
DSS.CmdResult := DSS.DSSExecutive.DoResetCmd; //'resetmonitors';
- 15:
+ ord(Cmd.SetOpt):
DSS.CmdResult := DoSetCmd(DSS, 0); //'set WITH no solve'
- 16:
+ ord(Cmd.Dump):
DSS.CmdResult := DSS.DSSExecutive.DoPropertyDump;
- 17:
+ ord(Cmd.Open):
DSS.CmdResult := DSS.DSSExecutive.DoOpenCmd;
- 18:
+ ord(Cmd.Close):
DSS.CmdResult := DSS.DSSExecutive.DoCloseCmd;
-
- 23:
+ ord(Cmd.questionmark):
DSS.CmdResult := DSS.DSSExecutive.DoQueryCmd;
- 24:
+ ord(Cmd.Next):
DSS.CmdResult := DSS.DSSExecutive.DoNextCmd; // Advances time
- 25:
- DoSimpleMsg(DSS, 'Command supported in DSS Extensions.', 999);
- 26:
+ ord(Cmd.Panel):
+ DoSimpleMsg(DSS, _('Command supported in DSS Extensions.'), 999);
+ ord(Cmd.Sample):
DSS.CmdResult := DSS.DSSExecutive.DoSampleCmd;
- {27: Begin ClearAllCircuits; DisposeDSSClasses; CreateDSSClasses; End;}
- {28: DoAboutBox; }
- 29:
+ // ord(Cmd.Clear): Begin ClearAllCircuits; DisposeDSSClasses; CreateDSSClasses; End;
+ // ord(Cmd.About): DoAboutBox;
+ ord(Cmd.Calcvoltagebases):
DSS.CmdResult := DSS.DSSExecutive.DoSetVoltageBases;
- 30:
+ ord(Cmd.SetkVBase):
DSS.CmdResult := DSS.DSSExecutive.DoSetkVBase;
- 31:
+ ord(Cmd.BuildY):
DSS.ActiveCircuit.InvalidateAllPCElements; // FORce rebuilding of Y
- 32:
+ ord(Cmd.Get):
DSS.CmdResult := DoGetCmd(DSS);
- 33:
+ ord(Cmd.Init):
DSS.ActiveCircuit.Solution.SolutionInitialized := FALSE;
- 34:
+ ord(Cmd.Export):
DSS.CmdResult := DoExportCmd(DSS);
- {35: DSS.CmdResult := DoFileEditCmd;}
- 36:
+ // ord(Cmd.Fileedit): DSS.CmdResult := DoFileEditCmd;
+ ord(Cmd.Voltages):
DSS.CmdResult := DSS.DSSExecutive.DovoltagesCmd(FALSE);
- 37:
+ ord(Cmd.Currents):
DSS.CmdResult := DSS.DSSExecutive.DocurrentsCmd;
- 38:
+ ord(Cmd.Powers):
DSS.CmdResult := DSS.DSSExecutive.DopowersCmd(0);
- 39:
+ ord(Cmd.Seqvoltages):
DSS.CmdResult := DSS.DSSExecutive.DoseqvoltagesCmd;
- 40:
+ ord(Cmd.Seqcurrents):
DSS.CmdResult := DSS.DSSExecutive.DoseqcurrentsCmd;
- 41:
+ ord(Cmd.Seqpowers):
DSS.CmdResult := DSS.DSSExecutive.DoseqpowersCmd;
- 42:
+ ord(Cmd.Losses):
DSS.CmdResult := DSS.DSSExecutive.DolossesCmd;
- 43:
+ ord(Cmd.Phaselosses):
DSS.CmdResult := DSS.DSSExecutive.DophaselossesCmd;
- 44:
+ ord(Cmd.Cktlosses):
DSS.CmdResult := DSS.DSSExecutive.DocktlossesCmd;
- 45:
+ ord(Cmd.Allocateloads):
DSS.CmdResult := DSS.DSSExecutive.DoAllocateLoadsCmd;
- 46:
+ ord(Cmd.Formedit):
DSS.CmdResult := DSS.DSSExecutive.DoFormEditCmd;
- 47:
+ ord(Cmd.Totals):
DSS.CmdResult := DSS.DSSExecutive.DoMeterTotals;
- 48:
+ ord(Cmd.Capacity):
DSS.CmdResult := DSS.DSSExecutive.DoCapacityCmd;
-// 49: DSS.CmdResult := DoClassesCmd;
-// 50: DSS.CmdResult := DoUserClassesCmd;
- 51:
+ // ord(Cmd.Classes): DSS.CmdResult := DoClassesCmd;
+ // ord(Cmd.Userclasses): DSS.CmdResult := DoUserClassesCmd;
+ ord(Cmd.Zsc):
DSS.CmdResult := DSS.DSSExecutive.DoZscCmd(TRUE);
- 52:
+ ord(Cmd.Zsc10):
DSS.CmdResult := DSS.DSSExecutive.DoZsc10cmd;
- 53:
+ ord(Cmd.ZscRefresh):
DSS.CmdResult := DSS.DSSExecutive.DoZscRefresh;
- 54:
+ ord(Cmd.Ysc):
DSS.CmdResult := DSS.DSSExecutive.DoZscCmd(FALSE);
- 55:
+ ord(Cmd.puvoltages):
DSS.CmdResult := DSS.DSSExecutive.DovoltagesCmd(TRUE);
- 56:
+ ord(Cmd.VarValues):
DSS.CmdResult := DSS.DSSExecutive.DoVarValuesCmd;
- 57:
+ ord(Cmd.Varnames):
DSS.CmdResult := DSS.DSSExecutive.DoVarNamesCmd;
- 58:
+ ord(Cmd.Buscoords):
DSS.CmdResult := DSS.DSSExecutive.DoBusCoordsCmd(FALSE);
- 59:
+ ord(Cmd.MakeBusList):
with DSS.ActiveCircuit do
if BusNameRedefined then
ReprocessBusDefs;
- 60:
+ ord(Cmd.MakePosSeq):
DSS.CmdResult := DSS.DSSExecutive.DoMakePosSeq;
- 61:
+ ord(Cmd.Reduce):
DSS.CmdResult := DSS.DSSExecutive.DoReduceCmd;
- 62:
+ ord(Cmd.Interpolate):
DSS.CmdResult := DSS.DSSExecutive.DoInterpolateCmd;
- 64:
+ ord(Cmd.TOP):
begin
- DoSimpleMsg(DSS, 'TOP is not supported in DSS Extensions.', 999);
+ DoSimpleMsg(DSS, _('TOP is not supported in DSS Extensions.'), 999);
DSS.CmdResult := 0;
end;
- 65:
+ ord(Cmd.Rotate):
DSS.CmdResult := DSS.DSSExecutive.DoRotateCmd;
- 66:
+ ord(Cmd.Vdiff):
DSS.CmdResult := DSS.DSSExecutive.DoVdiffCmd;
- 67:
+ ord(Cmd.Summary):
DSS.CmdResult := DSS.DSSExecutive.DoSummaryCmd;
- 68:
+ ord(Cmd.Distribute):
DSS.CmdResult := DSS.DSSExecutive.DoDistributeCmd;
-// 69;
-// 70;
-// 71;
-// 72;
- 73:
+ // ord(Cmd.DI_plot);
+ // ord(Cmd.Comparecases);
+ // ord(Cmd.YearlyCurves);
+ // ord(Cmd.CD);
+ ord(Cmd.Visualize):
DSS.CmdResult := DSS.DSSExecutive.DoVisualizeCmd;
- 74:
+ ord(Cmd.CloseDI):
DSS.CmdResult := DSS.DSSExecutive.DoCloseDICmd;
- 76:
+ ord(Cmd.Estimate):
DSS.CmdResult := DSS.DSSExecutive.DoEstimateCmd;
- 77:
+ ord(Cmd.Reconductor):
DSS.CmdResult := DSS.DSSExecutive.DoReconductorCmd;
- {Step solution commands}
- 78:
+
+ // Step solution commands
+ ord(Cmd._InitSnap):
DSS.ActiveCircuit.Solution.SnapShotInit;
- 79:
+ ord(Cmd._SolveNoControl):
DSS.ActiveCircuit.Solution.SolveCircuit;
- 80:
+ ord(Cmd._SampleControls):
DSS.ActiveCircuit.Solution.SampleControlDevices;
- 81:
+ ord(Cmd._DoControlActions):
DSS.ActiveCircuit.Solution.DoControlActions;
- 82:
+ ord(Cmd._ShowControlQueue):
DSS.ActiveCircuit.ControlQueue.ShowQueue(DSS.OutputDirectory + DSS.CircuitName_ + 'ControlQueue.csv');
- 83:
+ ord(Cmd._SolveDirect):
DSS.ActiveCircuit.Solution.SolveDirect;
- 84:
+ ord(Cmd._SolvePFlow):
DSS.ActiveCircuit.Solution.DoPFLOWsolution;
- 85:
+ ord(Cmd.AddBusMarker):
DSS.CmdResult := DSS.DSSExecutive.DoAddMarkerCmd;
- 86:
+ ord(Cmd.Uuids):
DSS.CmdResult := DSS.DSSExecutive.DoUuidsCmd;
- 87:
+ ord(Cmd.SetLoadAndGenKV):
DSS.CmdResult := DSS.DSSExecutive.DoSetLoadAndGenKVCmd;
-// 88:;
- 89:
+ // ord(Cmd.CvrtLoadshapes):;
+ ord(Cmd.NodeDiff):
DSS.CmdResult := DSS.DSSExecutive.DoNodeDiffCmd;
- 90:
+ ord(Cmd.Rephase):
DSS.CmdResult := DSS.DSSExecutive.DoRephaseCmd;
- 91:
+ ord(Cmd.SetBusXY):
DSS.CmdResult := DSS.DSSExecutive.DoSetBusXYCmd;
- 92:
+ ord(Cmd.UpdateStorage):
DSS.CmdResult := DSS.DSSExecutive.DoUpDateStorageCmd;
- 93:
+ ord(Cmd.Obfuscate):
Obfuscate(DSS);
- 94:
+ ord(Cmd.LatLongCoords):
DSS.CmdResult := DSS.DSSExecutive.DoBusCoordsCmd(TRUE); // swaps X and Y
- 95:
+ ord(Cmd.BatchEdit):
DSS.CmdResult := DSS.DSSExecutive.DoBatchEditCmd;
- 96:
+ ord(Cmd.Pstcalc):
DSS.CmdResult := DSS.DSSExecutive.DoPstCalc;
- 97:
+ ord(Cmd.Variable):
DSS.CmdResult := DSS.DSSExecutive.DoValVarCmd;
- 98:
+ ord(Cmd.ReprocessBuses):
DSS.ActiveCircuit.ReprocessBusDefs;
- 99:
+ ord(Cmd.ClearBusMarkers):
DSS.ActiveCircuit.ClearBusMarkers;
- 100:
+ ord(Cmd.RelCalc):
DSS.CmdResult := DSS.DSSExecutive.DoLambdaCalcs; // Option: Assume Restoration
- 102:
+ ord(Cmd.Cleanup):
DSS.ActiveCircuit.Solution.EndofTimeStepCleanup;
- 103:
+ ord(Cmd.FinishTimeStep):
DSS.ActiveCircuit.Solution.FinishTimeStep;
- 104:
+ ord(Cmd.NodeList):
DSS.CmdResult := DSS.DSSExecutive.DoNodeListCmd;
- 105:
+ ord(Cmd.GISCoords):
+ ; // Do nothing here on DSS C-API. Just ignore it silently so files
+ // saved with EPRI's version can be loaded more easily.
+ // OpenDSS-GIS is out of the scope proposed by DSS Extensions,
+ // but we could provide compatibility when/if OpenDSS-GIS becomes
+ // at least available to users outside of EPRI.
+ ord(Cmd.Connect):
begin
- DoSimpleMsg(DSS, 'Winsock TCP/IP connection is not supported in DSS Extensions', 999);
+ DoSimpleMsg(DSS, _('Winsock TCP/IP connection is not supported in DSS Extensions'), 999);
DSS.CmdResult := 0;
end;
- 106:
+ ord(Cmd.Disconnect):
begin
- DoSimpleMsg(DSS, 'Winsock TCP/IP connection is not supported in DSS Extensions', 999);
+ DoSimpleMsg(DSS, _('Winsock TCP/IP connection is not supported in DSS Extensions'), 999);
DSS.CmdResult := 0;
end;
- 107:
+ ord(Cmd.Remove):
DSS.DSSExecutive.DoRemoveCmd;
- 112:
- begin
+ ord(Cmd.ExportOverloads):
if DSS.EnergyMeterClass.OV_MHandle <> nil then
- CloseMHandler(DSS, DSS.EnergyMeterClass.OV_MHandle, DSS.EnergyMeterClass.DI_Dir + PathDelim + 'DI_Overloads' + '.CSV', DSS.EnergyMeterClass.OV_Append);
+ CloseMHandler(DSS, DSS.EnergyMeterClass.OV_MHandle, DSS.EnergyMeterClass.DI_Dir + PathDelim + 'DI_Overloads' + '.csv', DSS.EnergyMeterClass.OV_Append);
{$IFDEF DSS_CAPI_PM}
- CMD_Abort:
- PMParent.SolutionAbort := TRUE; //TODO
- CMD_Clone:
+ ord(Cmd.Abort):
+ for i := 0 to High(PMParent.Children) do
+ PMParent.Children[i].SolutionAbort := TRUE;
+ ord(Cmd.Clone):
DoClone(DSS);
{$ENDIF}
- end;
- 113:
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ ord(Cmd.Tear_Circuit):
+ ADiakoptics_Tearing(DSS, False);
+ ord(Cmd.AggregateProfiles):
begin
- if DSS.EnergyMeterClass.VR_MHandle <> nil then
- CloseMHandler(DSS, DSS.EnergyMeterClass.VR_MHandle, DSS.EnergyMeterClass.DI_Dir + PathDelim + 'DI_VoltExceptions' + '.CSV', DSS.EnergyMeterClass.VR_Append);
+ DSS.Parser.NextParam;
+ DSS.ActiveCircuit.AggregateProfiles(DSS.Parser.StrValue);
end;
- 114:
+{$ENDIF}
+ ord(Cmd.ExportVViolations):
+ if DSS.EnergyMeterClass.VR_MHandle <> nil then
+ CloseMHandler(DSS, DSS.EnergyMeterClass.VR_MHandle, DSS.EnergyMeterClass.DI_Dir + PathDelim + 'DI_VoltExceptions' + '.csv', DSS.EnergyMeterClass.VR_Append);
+ ord(Cmd.Zsc012):
DSS.CmdResult := DSS.DSSExecutive.DoZsc012Cmd; // Get full symmetrical component transformation of Zsc
- 115:
- begin
- DoSimpleMsg(DSS, 'AggregateProfiles is not supported in DSS Extensions yet.', 304);
- // Parser.NextParam;
- // DSS.ActiveCircuit.AggregateProfiles(Parser.StrValue);
- end;
- 116:
+ ord(Cmd.AllPCEatBus):
begin
DSS.Parser.NextParam;
DSS.GlobalResult := DSS.ActiveCircuit.ReportPCEatBus(DSS.Parser.StrValue);
end;
- 117:
+ ord(Cmd.AllPDEatBus):
begin
DSS.Parser.NextParam;
DSS.GlobalResult := DSS.ActiveCircuit.ReportPDEatBus(DSS.Parser.StrValue);
end;
- 118:
+ ord(Cmd.TotalPowers):
DSS.CmdResult := DSS.DSSExecutive.DopowersCmd(1);
else
// Ignore excess parameters
@@ -1038,9 +675,9 @@ procedure ProcessCommand(DSS: TDSSContext; const CmdLine: String);
except
On E: Exception do
- DoErrorMsg(DSS, ('ProcessCommand' + CRLF + 'Exception Raised While Processing DSS Command:' + CRLF + DSS.Parser.CmdString),
- E.Message,
- 'Error in command string or circuit data.', 303);
+ DoErrorMsg(DSS,
+ Format(_('ProcessCommand: Exception Raised While Processing DSS Command: %s'), [CRLF + DSS.Parser.CmdString]),
+ E.Message, _('Error in command string or circuit data.'), 303);
end;
{$IFNDEF DSS_CAPI_PM}
DSS.ParserVars.Add('@result', DSS.GlobalResult)
@@ -1050,12 +687,10 @@ procedure ProcessCommand(DSS: TDSSContext; const CmdLine: String);
procedure DisposeStrings;
var
i: Integer;
-
begin
for i := 1 to NumExecCommands do
begin
ExecCommand[i] := '';
- CommandHelp[i] := '';
end;
end;
diff --git a/src/Executive/ExecHelper.pas b/src/Executive/ExecHelper.pas
index d9794ef63..5bbd8e9d3 100644
--- a/src/Executive/ExecHelper.pas
+++ b/src/Executive/ExecHelper.pas
@@ -6,23 +6,15 @@
----------------------------------------------------------
}
-{Functions for performing DSS Exec Commands and Options}
-{
- 8-17-00 Updated Property Dump to handle wildcards
- 10-23-00 Fixed EnergyMeters iteration error in DoAllocateLoadsCmd
- 7/6/01 Fixed autobuslist command parsing of file
- 7/19/01 Added DoMeterTotals
- 8/1/01 Revised the Capacity Command return values
- 9/12/02 Added Classes and UserClasses
- 3/29/03 Implemented DoPlotCmd and Buscoords
- 4/24/03 Implemented Keep list and other stuff related to circuit reduction
-}
+// Functions for performing DSS Exec Commands and Options
-{$WARN UNIT_PLATFORM OFF}
+{$MODE DELPHI}
interface
-uses Executive;
+uses
+ Executive,
+ Classes;
type
TExecHelper = class helper for TExecutive
@@ -125,7 +117,6 @@ TExecHelper = class helper for TExecutive
PROCEDURE DoSetNormal(pctNormal:Double);
-
PROCEDURE Set_Time;
PROCEDURE ParseObjName(const fullname:String; VAR objname, propname:String);
@@ -140,12 +131,10 @@ TExecHelper = class helper for TExecutive
FUNCTION SetActiveCktElement:Integer;
FUNCTION DoPropertyDump:Integer;
-
-
+
private
procedure MarkCapandReactorBuses;
-
end;
implementation
@@ -154,57 +143,52 @@ implementation
Circuit, Monitor, {ShowResults, ExportResults,}
DSSClass, DSSObject, Utilities, Solution,
EnergyMeter, Generator, LoadShape, Load, PCElement, CktElement,
- uComplex, mathutil, Bus, SolutionAlgs,
+ UComplex, DSSUcomplex, mathutil, Bus, SolutionAlgs,
CmdForms, ExecCommands,
Dynamics, Capacitor, Reactor, Line, Lineunits, Math,
- Classes, CktElementClass, Sensor, ExportCIMXML, NamedObject,
+ CktElementClass, Sensor, ExportCIMXML, NamedObject,
RegExpr,PstCalc,
PDELement, ReduceAlgs, Ucmatrix,
- BufStream, DSSHelper;
+ BufStream,
+ fpjson, DSSHelper;
Var
SaveCommands, DistributeCommands, DI_PlotCommands,
ReconductorCommands, RephaseCommands, AddMarkerCommands,
SetBusXYCommands, PstCalcCommands, RemoveCommands :TCommandList;
-
-
-//----------------------------------------------------------------------------
procedure TExecHelper.GetObjClassAndName(VAR ObjClass,ObjName:String);
VAR
ParamName:String;
Param:String;
-Begin
-
-{
- We're looking for Object Definition:
-
- ParamName = 'object' IF given
- and the name of the object
-
- Object=Capacitor.C1
- or just Capacitor.C1
-
- If no dot, last class is assumed
-}
- ObjClass := '';
- ObjName := '';
- ParamName := LowerCase(DSS.Parser.NextParam);
- Param := DSS.Parser.StrValue;
- IF Length(ParamName)>0 THEN Begin // IF specified, must be object or an abbreviation
- IF ComparetextShortest(ParamName, 'object')<>0 THEN Begin
- DoSimpleMsg(DSS, 'object=Class.Name expected as first parameter in command.'+ CRLF + DSS.Parser.CmdString, 240);
- Exit;
+begin
+ // We're looking for Object Definition:
+ //
+ // ParamName = 'object' IF given
+ // and the name of the object
+ //
+ // Object=Capacitor.C1
+ // or just Capacitor.C1
+ //
+ // If no dot, last class is assumed
+
+ ObjClass := '';
+ ObjName := '';
+ ParamName := AnsiLowerCase(DSS.Parser.NextParam);
+ Param := DSS.Parser.StrValue;
+ IF Length(ParamName)>0 THEN
+ Begin // IF specified, must be object or an abbreviation
+ IF ComparetextShortest(ParamName, 'object')<>0 THEN
+ Begin
+ DoSimpleMsg(DSS, 'object=Class.Name expected as first parameter in command. %s', [CRLF + DSS.Parser.CmdString], 240);
+ Exit;
End;
- End;
-
- ParseObjectClassandName(DSS, Param, ObjClass, ObjName); // see DSSGlobals
-
+ End;
+ ParseObjectClassandName(DSS, Param, ObjClass, ObjName); // see DSSGlobals
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoNewCmd:Integer;
// Process the New Command
@@ -219,8 +203,7 @@ function TExecHelper.DoNewCmd:Integer;
ObjClass, ObjName:String;
handle:Integer;
-Begin
-
+begin
Result := 0;
Handle := 0;
@@ -228,7 +211,7 @@ function TExecHelper.DoNewCmd:Integer;
IF CompareText(ObjClass,'solution') = 0
THEN Begin
- DoSimpleMsg(DSS, 'You cannot create new Solution objects through the command interface.', 241);
+ DoSimpleMsg(DSS, _('You cannot create new Solution objects through the command interface.'), 241);
Exit;
End;
@@ -247,15 +230,13 @@ function TExecHelper.DoNewCmd:Integer;
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoEditCmd:Integer;
// edit type=xxxx name=xxxx editstring
VAR
ObjType, ObjName:String;
-Begin
-
+begin
Result := 0;
GetObjClassAndName(ObjType, ObjName);
@@ -265,19 +246,15 @@ function TExecHelper.DoEditCmd:Integer;
// Do nothing
End
ELSE
- Begin
-
+ begin
// Everything ELSE must be a circuit element
Result := EditObject(ObjType, ObjName);
End;
+end;
-End;
-
-//----------------------------------------------------------------------------
function TExecHelper.DoBatchEditCmd:Integer;
// batchedit type=xxxx name=pattern editstring
-{$IFDEF FPC}
VAR
ObjType, Pattern:String;
RegEx1: TRegExpr;
@@ -288,13 +265,12 @@ function TExecHelper.DoBatchEditCmd:Integer;
GetObjClassAndName(ObjType, Pattern);
IF CompareText(ObjType, 'circuit')=0 THEN Begin
// Do nothing
- End ELSE Begin
-
+ End ELSE begin
DSS.LastClassReferenced := DSS.ClassNames.Find(ObjType);
CASE DSS.LastClassReferenced of
0: Begin
- DoSimpleMsg(DSS, 'BatchEdit Command: Object Type "' + ObjType + '" not found.'+ CRLF + DSS.Parser.CmdString, 267);
+ DoSimpleMsg(DSS, 'BatchEdit Command: Object Type "%s" not found. %s', [ObjType, CRLF + DSS.Parser.CmdString], 267);
Exit;
End;{Error}
ELSE
@@ -307,7 +283,7 @@ function TExecHelper.DoBatchEditCmd:Integer;
while pObj <> Nil do begin
if RegEx1.Exec(UTF8String(pObj.Name)) then begin
DSS.Parser.Position:=Params;
- DSS.ActiveDSSClass.Edit;
+ DSS.ActiveDSSClass.Edit(DSS.Parser);
end;
If DSS.ActiveDSSClass.Next>0 then pObj:=DSS.ActiveDSSObject else pObj := Nil;
end;
@@ -315,50 +291,8 @@ function TExecHelper.DoBatchEditCmd:Integer;
End;
End;
End;
-{$ELSE}
-VAR
- ObjType, Pattern:String;
- RegEx1: TPerlRegEx;
- pObj: TDSSObject;
- Params: Integer;
- iElement: Integer;
-Begin
- Result := 0;
- GetObjClassAndName(ObjType, Pattern);
- IF CompareText(ObjType, 'circuit')=0 THEN Begin
- // Do nothing
- End ELSE Begin
-
- DSS.LastClassReferenced := DSS.ClassNames.Find(ObjType);
-
- CASE DSS.LastClassReferenced of
- 0: Begin
- DoSimpleMsg(DSS, 'BatchEdit Command: Object Type "' + ObjType + '" not found.'+ CRLF + DSS.Parser.CmdString, 267);
- Exit;
- End;{Error}
- ELSE
- Params:=DSS.Parser.Position;
- DSS.ActiveDSSClass := DSS.DSSClassList.Get(DSS.LastClassReferenced);
- RegEx1:=TPerlRegEx.Create;
- RegEx1.Options:=[preCaseLess];
- RegEx1.RegEx:=Pattern; // UTF8String(Pattern);
- If DSS.ActiveDSSClass.First>0 then pObj:=DSS.ActiveDSSObject else pObj := Nil;
- while pObj <> Nil do begin
- RegEx1.Subject:= pObj.Name; //(pObj.Name);
- if RegEx1.Match then begin
- DSS.Parser.Position:=Params;
- DSS.ActiveDSSClass.Edit;
- end;
- If DSS.ActiveDSSClass.Next>0 then pObj:=DSS.ActiveDSSObject else pObj := Nil;
- end;
- End;
- End;
-End;
-{$ENDIF}
-//----------------------------------------------------------------------------
function TExecHelper.DoRedirect(IsCompile:Boolean):Integer;
-
// This routine should be recursive
// So you can redirect input an arbitrary number of times
@@ -366,13 +300,15 @@ function TExecHelper.DoRedirect(IsCompile:Boolean):Integer;
// If not Compile (is simple redirect), return to where we started
VAR
- Fin:TextFile;
- {ParamName,} InputLine, CurrDir, SaveDir, ReDirFileExp : String;
- LocalCompFileName : String;
- InBlockComment : Boolean;
+ Fin: TextFile;
+ InputLine, CurrDir, SaveDir, ReDirFileExp: String;
+ LocalCompFileName: String;
+ InBlockComment: Boolean;
strings: TStringList;
gotTheFile: Boolean;
stringIdx: Integer;
+ LineNum: Integer = 0;
+ Fstream: TStream;
Begin
gotTheFile := False;
strings := nil;
@@ -381,26 +317,57 @@ function TExecHelper.DoRedirect(IsCompile:Boolean):Integer;
// Therefore extent of block comment does not extend beyond a file
// Going back up the redirect stack
- // Get next parm and try to interpret as a file name
- {ParamName :=} DSS.Parser.NextParam;
-
- // Expanded path is required later as other Free Pascal functions
- // may fail with relative paths
- ReDirFileExp := ExpandFileName(DSS.Parser.StrValue);
-
- // First check if we need to workaround the SetCurrentDir issues
- if (not DSS_CAPI_ALLOW_CHANGE_DIR) then
+ if InZip then
+ begin
+ // Get next parm and try to interpret as a file name
+ DSS.Parser.NextParam;
+
+ if DSS.Parser.StrValue = '' then
+ exit; // ignore altogether IF null filename
+
+ try
+ Fstream := GetZipStream(DSS.Parser.StrValue);
+ except
+ on E: Exception do
+ begin
+ DoSimpleMsg(DSS, 'Redirect File "%s" could not be read: %s', [DSS.Parser.StrValue, E.Message], 2202);
+ DSS.SolutionAbort := TRUE;
+ Exit;
+ end;
+ end;
+
+
+ strings := TStringList.Create;
+ strings.LoadFromStream(Fstream);
+ Fstream.Free;
+ ReDirFileExp := DSS.inZipPath + DSS.Parser.StrValue;
+ gotTheFile := True;
+ SaveDir := DSS.inZipPath;
+ end
+ else
begin
- ReDirFileExp := ExpandFileName(AdjustInputFilePath(DSS, DSS.Parser.StrValue));
+ // Get next parm and try to interpret as a file name
+ DSS.Parser.NextParam;
+
+ // Expanded path is required later as other Free Pascal functions
+ // may fail with relative paths
+ ReDirFileExp := ExpandFileName(DSS.Parser.StrValue);
+
+ // First check if we need to workaround the SetCurrentDir issues
+ if (not DSS_CAPI_ALLOW_CHANGE_DIR) then
+ begin
+ ReDirFileExp := ExpandFileName(AdjustInputFilePath(DSS, DSS.Parser.StrValue));
+ end;
+
+ DSS.ReDirFile := ReDirFileExp;// DSS.Parser.StrValue;
+ if DSS.ReDirFile = '' then
+ exit; // ignore altogether IF null filename
+
+ SaveDir := DSS.CurrentDSSDir;
end;
-
- DSS.ReDirFile := DSS.Parser.StrValue;
- if DSS.ReDirFile = '' then
- exit; // ignore altogether IF null filename
-
- SaveDir := DSS.CurrentDSSDir;
+
- if FileExists(DSS.ReDirFile) then
+ if (not gotTheFile) and (FileExists(DSS.ReDirFile)) then
begin
// If the usual Pascal text file is broken,
// try a stream via a TStringList object
@@ -484,11 +451,12 @@ function TExecHelper.DoRedirect(IsCompile:Boolean):Integer;
IF Pos('.', DSS.ReDirFile)=0 THEN
Begin
DSS.ReDirFile := DSS.ReDirFile + '.dss';
+ LocalCompFileName := DSS.ReDirFile;
TRY
AssignFile(Fin, DSS.ReDirFile);
Reset(Fin);
EXCEPT
- DoSimpleMsg(DSS, 'Redirect File: "' + DSS.ReDirFile + '" Not Found.', 242);
+ DoSimpleMsg(DSS, 'Redirect file not found: "%s"', [DSS.Parser.StrValue], 242);
DSS.SolutionAbort := TRUE;
Exit;
End;
@@ -498,7 +466,7 @@ function TExecHelper.DoRedirect(IsCompile:Boolean):Integer;
if not gotTheFile then
begin
- DoSimpleMsg(DSS, 'Redirect File: "'+DSS.ReDirFile+'" Not Found.', 243);
+ DoSimpleMsg(DSS, 'Redirect file not found: "%s"', [DSS.Parser.StrValue], 243);
DSS.SolutionAbort := True;
exit; // Already had an extension, so just bail
end;
@@ -512,48 +480,60 @@ function TExecHelper.DoRedirect(IsCompile:Boolean):Integer;
// Change Directory to path specified by file in CASE that
// loads in more files
CurrDir := ExtractFileDir(DSS.ReDirFile);
- DSS.SetCurrentDSSDir(CurrDir);
- If IsCompile Then SetDataPath(DSS, CurrDir); // change datadirectory
+ if not InZip then
+ begin
+ DSS.SetCurrentDSSDir(CurrDir);
+ If IsCompile Then
+ SetDataPath(DSS, CurrDir); // change datadirectory
+ end
+ else
+ begin
+ SetInZipPath(CurrDir);
+ end;
DSS.Redirect_Abort := False;
- DSS.In_Redirect := True;
+ DSS.In_Redirect := True;
if strings = nil then
begin
// Traditional TextFile is used
- WHILE Not ( (EOF(Fin)) or (DSS.Redirect_Abort) ) DO
- Begin
+ while not ( (EOF(Fin)) or (DSS.Redirect_Abort) ) do
+ begin
Readln(Fin, InputLine);
+ Inc(LineNum);
if Length(InputLine) > 0 then
- BEGIN
- if Not InBlockComment then // look for '/*' at baginning of line
+ begin
+ if not InBlockComment then // look for '/*' at baginning of line
case InputLine[1] of
'/':
if (Length(InputLine) > 1) and (InputLine[2]='*') then
InBlockComment := TRUE;
end;
- If Not InBlockComment Then // process the command line
- If Not DSS.SolutionAbort Then
- ProcessCommand(DSS, InputLine)
- Else
+ if not InBlockComment then // process the command line
+ if not DSS.SolutionAbort then
+ Set_Command(InputLine, LineNum)
+ else
DSS.Redirect_Abort := True; // Abort file if solution was aborted
// in block comment ... look for */ and cancel block comment (whole line)
if InBlockComment then
if Pos('*/', Inputline) > 0 then
InBlockComment := FALSE;
- END;
+
+ end;
+
End // WHILE Not ( (EOF(Fin)) or (Redirect_Abort) ) DO
end
else
begin
// The string list is used
for stringIdx := 0 to (strings.Count - 1) do
- Begin
+ begin
if DSS.Redirect_Abort then
break;
+ LineNum := stringIdx + 1;
InputLine := strings[stringIdx];
if Length(InputLine) > 0 then
BEGIN
@@ -566,7 +546,7 @@ function TExecHelper.DoRedirect(IsCompile:Boolean):Integer;
If Not InBlockComment Then // process the command line
If Not DSS.SolutionAbort Then
- ProcessCommand(DSS, InputLine)
+ Set_Command(InputLine, LineNum)
Else
DSS.Redirect_Abort := True; // Abort file if solution was aborted
@@ -582,11 +562,17 @@ function TExecHelper.DoRedirect(IsCompile:Boolean):Integer;
DSS.ActiveCircuit.CurrentDirectory := CurrDir + PathDelim;
EXCEPT On E: Exception DO
- DoErrorMsg(DSS, 'DoRedirect'+CRLF+'Error Processing Input Stream in Compile/Redirect.',
+ DoErrorMsg(DSS, _('DoRedirect: Error Processing Input Stream in Compile/Redirect.'),
E.Message,
- 'Error in File: "' + DSS.ReDirFile + '" or Filename itself.', 244);
+ Format(_('Error in File: "%s" or Filename itself.'), [DSS.ReDirFile]), 244);
END;
FINALLY
+ if gotTheFile and (DSS.Redirect_Abort or (DSS.ErrorNumber <> 0)) then
+ begin
+ DSS.LastErrorMessage := DSS.LastErrorMessage + CRLF +
+ Format(_('[file: "%s", line: %d]'), [ReDirFileExp, LineNum]);
+ end;
+
if strings <> nil then
FreeAndNil(strings)
else
@@ -595,31 +581,37 @@ function TExecHelper.DoRedirect(IsCompile:Boolean):Integer;
DSS.In_Redirect := False;
DSS.ParserVars.Add('@lastfile', DSS.ReDirFile) ;
- If IsCompile Then
- Begin
- SetDataPath(DSS, CurrDir); // change datadirectory
- DSS.LastCommandWasCompile := True;
- DSS.ParserVars.Add('@lastcompilefile', LocalCompFileName); // will be last one off the stack
- End
- Else
- Begin
- DSS.SetCurrentDSSDir(SaveDir); // set back to where we were for redirect, but not compile
- DSS.ParserVars.Add('@lastredirectfile', DSS.ReDirFile);
- End;
+ if not InZip then
+ begin
+ If IsCompile Then
+ Begin
+ SetDataPath(DSS, CurrDir); // change datadirectory
+ DSS.LastCommandWasCompile := True;
+ DSS.ParserVars.Add('@lastcompilefile', LocalCompFileName); // will be last one off the stack
+ End
+ Else
+ Begin
+ DSS.SetCurrentDSSDir(SaveDir); // set back to where we were for redirect, but not compile
+ DSS.ParserVars.Add('@lastredirectfile', DSS.ReDirFile);
+ End;
+ end
+ else
+ begin
+ if not IsCompile then
+ SetInZipPath(SaveDir);
+ end;
END;
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoSelectCmd:Integer;
// select active object
// select element=elementname terminal=terminalnumber
VAR
ObjClass, ObjName,
- {ParamName,} Param:String;
-
-Begin
+ Param:String;
+begin
Result := 1;
GetObjClassAndName(ObjClass, ObjName); // Parse Object class and name
@@ -631,8 +623,7 @@ function TExecHelper.DoSelectCmd:Integer;
SetActiveCircuit(ObjName);
End
ELSE
- Begin
-
+ begin
// Everything else must be a circuit element
IF Length(ObjClass)>0 THEN SetObjectClass(DSS, ObjClass);
@@ -641,7 +632,7 @@ function TExecHelper.DoSelectCmd:Integer;
Begin
IF Not DSS.ActiveDSSClass.SetActive(Objname) THEN
Begin // scroll through list of objects untill a match
- DoSimpleMsg(DSS, 'Error! Object "' + ObjName + '" not found.'+ CRLF + DSS.Parser.CmdString, 245);
+ DoSimpleMsg(DSS, 'Error! Object "%s" not found. %s', [ObjName, CRLF + DSS.Parser.CmdString], 245);
Result := 0;
End
ELSE
@@ -653,7 +644,7 @@ function TExecHelper.DoSelectCmd:Integer;
ELSE Begin // for circuit types, set DSS.ActiveCircuit Element, too
ActiveCktElement := DSS.ActiveDSSClass.GetActiveObj;
// Now check for active terminal designation
- {ParamName := LowerCase(}DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
If Length(Param)>0
THEN ActiveCktElement.ActiveTerminalIdx := DSS.Parser.Intvalue
@@ -664,29 +655,24 @@ function TExecHelper.DoSelectCmd:Integer;
End;
End
ELSE Begin
- DoSimpleMsg(DSS, 'Error! Active object type/class is not set.', 246);
+ DoSimpleMsg(DSS, _('Error! Active object type/class is not set.'), 246);
Result := 0;
End;
End;
+end;
-End;
-
-//----------------------------------------------------------------------------
function TExecHelper.DoMoreCmd:Integer;
-
// more editstring (assumes active circuit element)
Begin
- IF DSS.ActiveDSSClass<>nil THEN Result := DSS.ActiveDSSClass.Edit
- ELSE Result := 0;
+ if DSS.ActiveDSSClass <> NIL then
+ Result := DSS.ActiveDSSClass.Edit(DSS.Parser)
+ else
+ Result := 0;
End;
-
-//----------------------------------------------------------------------------
FUNCTION TExecHelper.DoSaveCmd:Integer;
-
// Save current values in both monitors and Meters
-
VAR
pMon :TMonitorObj;
pMtr :TEnergyMeterObj;
@@ -699,7 +685,6 @@ function TExecHelper.DoMoreCmd:Integer;
SaveDir :String;
saveFile :String;
DSSClass :TDSSClass;
-
Begin
Result := 0;
ObjClass := '';
@@ -763,7 +748,7 @@ function TExecHelper.DoMoreCmd:Integer;
Try
mkDir(SaveDir);
Except
- On E:Exception Do DoSimpleMsg(DSS, 'Error making Directory: "'+SaveDir+'". ' + E.Message, 247);
+ On E:Exception Do DoSimpleMsg(DSS, 'Error making Directory: "%s". %s', [SaveDir, E.Message], 247);
End;
SaveFile := SaveDir + PathDelim + SaveFile;
End;
@@ -772,72 +757,44 @@ function TExecHelper.DoMoreCmd:Integer;
SetLastResultFile(DSS, SaveFile);
DSS.GlobalResult := SaveFile;
+end;
-End;
-
-
-//----------------------------------------------------------------------------
function TExecHelper.DoClearCmd:Integer;
Begin
- DSS.DSSExecutive.Clear;
- Result := 0;
+ DSS.DSSExecutive.Clear;
+ Result := 0;
End;
-//----------------------------------------------------------------------------
{$IFDEF DSS_CAPI_PM}
function TExecHelper.DoClearAllCmd:Integer;
Begin
- DSS.DSSExecutive.ClearAll;
- Result := 0;
-
-End;
+ DSS.DSSExecutive.ClearAll;
+ Result := 0;
+end;
{$ENDIF}
-//----------------------------------------------------------------------------
function TExecHelper.DoHelpCmd:Integer;
Begin
- ShowHelpForm(DSS.DSSClassList); // DSSForms Unit
+ ShowHelpForm(DSS); // DSSForms Unit
Result := 0;
End;
-
-
-//----------------------------------------------------------------------------
function TExecHelper.DoSampleCmd:Integer;
-
-// FORce all monitors and meters in active circuit to take a sample
-
-
+// Force all monitors and meters in active circuit to take a sample
Begin
-
- DSS.MonitorClass.SampleAll;
-
- DSS.EnergyMeterClass.SampleAll; // gets generators too
-
-
-
- Result := 0;
-
+ DSS.MonitorClass.SampleAll;
+ DSS.EnergyMeterClass.SampleAll; // gets generators too
+ Result := 0;
End;
-
-
-//----------------------------------------------------------------------------
function TExecHelper.DoSolveCmd:Integer;
Begin
- // just invoke solution obj's editor to pick up parsing and execute rest of command
- DSS.ActiveSolutionObj := DSS.ActiveCircuit.Solution;
- Result := DSS.SolutionClass.Edit;
-
+ Result := 0;
+ DSS.ActiveCircuit.Solution.Solve();
End;
-
-//----------------------------------------------------------------------------
function TExecHelper.SetActiveCktElement:Integer;
-
// Parses the object off the line and sets it active as a circuitelement.
-
VAR
ObjType, ObjName:String;
-Begin
-
+begin
Result := 0;
GetObjClassAndName(ObjType, ObjName);
@@ -847,14 +804,13 @@ function TExecHelper.SetActiveCktElement:Integer;
// Do nothing
End
ELSE
- Begin
-
+ begin
IF CompareText(ObjType, DSS.ActiveDSSClass.Name)<>0 THEN
DSS.LastClassReferenced := DSS.ClassNames.Find(ObjType);
CASE DSS.LastClassReferenced of
0: Begin
- DoSimpleMsg(DSS, 'Object Type "' + ObjType + '" not found.'+ CRLF + DSS.Parser.CmdString, 253);
+ DoSimpleMsg(DSS, 'Object Type "%s" not found. %s', [ObjType, CRLF + DSS.Parser.CmdString], 253);
Result := 0;
Exit;
End;{Error}
@@ -866,7 +822,7 @@ function TExecHelper.SetActiveCktElement:Integer;
WITH DSS.ActiveCircuit Do
Begin // scroll through list of objects until a match
CASE DSS.ActiveDSSObject.DSSObjType OF
- DSS_OBJECT: DoSimpleMsg(DSS, 'Error in SetActiveCktElement: Object not a circuit Element.'+ CRLF + DSS.Parser.CmdString, 254);
+ DSS_OBJECT: DoSimpleMsg(DSS, 'Error in SetActiveCktElement: Object not a circuit Element. %s', [CRLF + DSS.Parser.CmdString], 254);
ELSE Begin
ActiveCktElement := DSS.ActiveDSSClass.GetActiveObj;
Result:=1;
@@ -878,17 +834,12 @@ function TExecHelper.SetActiveCktElement:Integer;
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoEnableCmd:Integer;
-
Var Objtype, ObjName:String;
ClassPtr:TDSSClass;
CktElem:TDSSCktElement;
i:Integer;
-
-
-Begin
-
+begin
// Result := SetActiveCktElement;
// IF Result>0 THEN DSS.ActiveCircuit.ActiveCktElement.Enabled := True;
@@ -904,8 +855,7 @@ function TExecHelper.DoEnableCmd:Integer;
If Length(ObjType)>0 Then Begin
// only applies to CktElementClass objects
ClassPtr := GetDSSClassPtr(DSS, ObjType);
- If ClassPtr<> Nil Then Begin
-
+ If ClassPtr<> Nil Then begin
If (ClassPtr.DSSClassType and BASECLASSMASK) > 0 then Begin
// Everything else must be a circuit element
If CompareText(ObjName,'*') = 0 Then Begin
@@ -916,8 +866,7 @@ function TExecHelper.DoEnableCmd:Integer;
End;
End
- Else Begin
-
+ Else begin
// just load up the parser and call the edit routine for the object in question
DSS.Parser.CmdString := 'Enabled=true'; // Will only work for CktElements
@@ -926,18 +875,13 @@ function TExecHelper.DoEnableCmd:Integer;
End;
End;
End;
+end;
-End;
-
-//----------------------------------------------------------------------------
function TExecHelper.DoDisableCmd:Integer;
-
Var Objtype, ObjName:String;
ClassPtr:TDSSClass;
CktElem:TDSSCktElement;
i:Integer;
-
-
Begin
Result := 0;
@@ -951,8 +895,7 @@ function TExecHelper.DoDisableCmd:Integer;
If Length(ObjType)>0 Then Begin
// only applies to CktElementClass objects
ClassPtr := GetDSSClassPtr(DSS, ObjType);
- If ClassPtr<> Nil Then Begin
-
+ If ClassPtr<> Nil Then begin
If (ClassPtr.DSSClassType and BASECLASSMASK) > 0 then Begin
// Everything else must be a circuit element
If CompareText(ObjName,'*') = 0 Then Begin
@@ -963,8 +906,7 @@ function TExecHelper.DoDisableCmd:Integer;
End;
End
- Else Begin
-
+ Else begin
// just load up the parser and call the edit routine for the object in question
DSS.Parser.CmdString := 'Enabled=false'; // Will only work for CktElements
@@ -973,12 +915,10 @@ function TExecHelper.DoDisableCmd:Integer;
End;
End;
End;
-
// Result := SetActiveCktElement;
// IF Result>0 THEN DSS.ActiveCircuit.ActiveCktElement.Enabled := False;
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoPropertyDump:Integer;
VAR
@@ -987,11 +927,8 @@ function TExecHelper.DoPropertyDump:Integer;
SingleObject, Debugdump, IsSolution:Boolean;
i:Integer;
FileName:String;
- {ParamName:String;}
Param, Param2, ObjClass, ObjName:String;
-
-Begin
-
+begin
Result := 0;
SingleObject := False;
IsSolution := False;
@@ -1000,15 +937,11 @@ function TExecHelper.DoPropertyDump:Integer;
ObjName := ' ';
// Continue parsing command line - check for object name
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
IF Length(Param)>0 THEN
- Begin
-
+ begin
IF CompareText(Param, 'commands')=0 THEN
-{$IFNDEF DSS_CAPI}
- If Not NoFormsAllowed Then
-{$ENDIF}
Begin
DumpAllDSSCommands(DSS, FileName);
{$IFDEF DSS_CAPI}DSS.GlobalResult := FileName;{$ENDIF}
@@ -1016,35 +949,29 @@ function TExecHelper.DoPropertyDump:Integer;
Exit;
End;
- {dump bus names hash list}
+ // dump bus names hash list
if CompareText(Param, 'buslist')=0 then
-{$IFNDEF DSS_CAPI}
- If Not NoFormsAllowed Then
-{$ENDIF}
Begin
- FileName := DSS.OutputDirectory + 'Bus_Hash_List.Txt';
+ FileName := DSS.OutputDirectory + 'Bus_Hash_List.txt';
DSS.ActiveCircuit.BusList.DumpToFile(FileName);
{$IFDEF DSS_CAPI}DSS.GlobalResult := FileName;{$ENDIF}
FireOffEditor(DSS, FileName);
Exit;
End;
- {dump device names hash list}
+ // dump device names hash list
if CompareText(Param, 'devicelist')=0 then
-{$IFNDEF DSS_CAPI}
- If Not NoFormsAllowed Then
-{$ENDIF}
Begin
- FileName := DSS.OutputDirectory + 'Device_Hash_List.Txt';
+ FileName := DSS.OutputDirectory + 'Device_Hash_List.txt';
DSS.ActiveCircuit.DeviceList.DumpToFile(FileName);
{$IFDEF DSS_CAPI}DSS.GlobalResult := FileName;{$ENDIF}
FireOffEditor(DSS, FileName);
Exit;
End;
- IF CompareText(Copy(lowercase(Param),1,5), 'alloc')=0 THEN
+ IF CompareText(Copy(AnsiLowerCase(Param),1,5), 'alloc')=0 THEN
Begin
- FileName :=DSS.OutputDirectory + 'AllocationFactors.Txt';
+ FileName :=DSS.OutputDirectory + 'AllocationFactors.txt';
DumpAllocationFactors(DSS, FileName);
{$IFDEF DSS_CAPI}DSS.GlobalResult := FileName;{$ENDIF}
FireOffEditor(DSS, FileName);
@@ -1054,20 +981,19 @@ function TExecHelper.DoPropertyDump:Integer;
IF CompareText(Param,'debug')=0 THEN
DebugDump := TRUE
ELSE
- Begin
-
+ begin
IF CompareText(Param,'solution')=0 THEN
Begin
// Assume active circuit solution IF not qualified
- DSS.ActiveDSSClass := DSS.SolutionClass;
- DSS.ActiveDSSObject := DSS.ActiveCircuit.Solution;
+ // DSS.ActiveDSSClass := DSS.SolutionClass;
+ // DSS.ActiveDSSObject := DSS.ActiveCircuit.Solution;
IsSolution := TRUE;
End
ELSE
Begin
SingleObject := TRUE;
- // Check to see IF we want a debugdump on this object
- {ParamName :=} DSS.Parser.NextParam;
+ // Check to see IF we want a debugdump on this object
+ DSS.Parser.NextParam;
Param2 := DSS.Parser.StrValue;
IF CompareText(Param2,'debug')=0 THEN DebugDump := TRUE;
// Set active Element to be value in Param
@@ -1085,22 +1011,21 @@ function TExecHelper.DoPropertyDump:Integer;
End;
TRY
- F := TFileStream.Create(DSS.OutputDirectory + DSS.CircuitName_ + 'PropertyDump.Txt', fmCreate);
+ F := TBufferedFileStream.Create(DSS.OutputDirectory + DSS.CircuitName_ + 'PropertyDump.txt', fmCreate);
EXCEPT
On E:Exception DO
Begin
- DoErrorMsg(DSS, 'DoPropertyDump - opening '+ DSS.OutputDirectory +' DSS_PropertyDump.txt for writing in '+Getcurrentdir, E.Message, 'Disk protected or other file error', 255);
+ DoErrorMsg(DSS,
+ Format(_('DoPropertyDump - opening %s DSS_PropertyDump.txt for writing in %s'), [DSS.OutputDirectory, Getcurrentdir]),
+ E.Message, _('Disk protected or other file error'), 255);
Exit;
End;
End;
-
TRY
-
IF SingleObject THEN
- Begin
-
- {IF ObjName='*' then we dump all objects of this class}
+ begin
+ // IF ObjName='*' then we dump all objects of this class
CASE ObjName[1] of
'*':Begin
FOR i := 1 to DSS.ActiveDSSClass.ElementCount Do
@@ -1112,7 +1037,7 @@ function TExecHelper.DoPropertyDump:Integer;
ELSE
IF Not DSS.ActiveDSSClass.SetActive(Objname)
THEN Begin
- DoSimpleMsg(DSS, 'Error! Object "' + ObjName + '" not found.', 256) ;
+ DoSimpleMsg(DSS, 'Error! Object "%s" not found.', [ObjName], 256) ;
Exit;
End
ELSE DSS.ActiveDSSObject.DumpProperties(F, DebugDump); // Dump only properties of active circuit element
@@ -1120,10 +1045,9 @@ function TExecHelper.DoPropertyDump:Integer;
End
ELSE IF IsSolution THEN Begin
- DSS.ActiveDSSObject.DumpProperties(F, DebugDump);
+ DSS.ActiveCircuit.Solution.DumpProperties(F, DebugDump);
End
- ELSE Begin
-
+ ELSE begin
// Dump general Circuit stuff
IF DebugDump THEN DSS.ActiveCircuit.DebugDump(F);
@@ -1132,18 +1056,19 @@ function TExecHelper.DoPropertyDump:Integer;
pObject := DSS.ActiveCircuit.CktElements.First;
WHILE pObject <> Nil DO
Begin
- pObject.DumpProperties(F, DebugDump);
+ pObject.DumpProperties(F, DebugDump, True);
pObject := DSS.ActiveCircuit.CktElements.Next;
End;
pObject := DSS.DSSObjs.First;
WHILE pObject <> Nil DO
Begin
- pObject.DumpProperties(F, DebugDump);
+ pObject.DumpProperties(F, DebugDump, True);
pObject := DSS.DSSObjs.Next;
End;
EXCEPT
On E:Exception DO
- DoErrorMsg(DSS, 'DoPropertyDump - Problem writing file.', E.Message, 'File may be read only, in use, or disk full?', 257);
+ DoErrorMsg(DSS, _('DoPropertyDump - Problem writing file.'), E.Message,
+ _('File may be read only, in use, or disk full?'), 257);
End;
DSS.ActiveCircuit.Solution.DumpProperties(F,DebugDump);
@@ -1154,22 +1079,15 @@ function TExecHelper.DoPropertyDump:Integer;
FreeAndNil(F);
END; {TRY}
- FileName := DSS.OutputDirectory + DSS.CircuitName_ + 'PropertyDump.Txt';
+ FileName := DSS.OutputDirectory + DSS.CircuitName_ + 'PropertyDump.txt';
{$IFDEF DSS_CAPI}DSS.GlobalResult := FileName;{$ENDIF}
FireOffEditor(DSS, FileName);
+end;
-End;
-
-
-
-//----------------------------------------------------------------------------
procedure TExecHelper.Set_Time;
-
// for interpreting time specified as an array "hour, sec"
VAR
-
TimeArray:Array[1..2] of double;
-
Begin
DSS.Parser.ParseAsVector(2, pDoubleArray(@TimeArray));
WITH DSS.ActiveCircuit.Solution DO
@@ -1180,17 +1098,14 @@ procedure TExecHelper.Set_Time;
End;
End;
-//----------------------------------------------------------------------------
procedure TExecHelper.SetActiveCircuit(const cktname:String);
-
VAR
pCkt:TDSSCircuit;
-Begin
-
+begin
pCkt := DSS.Circuits.First;
WHILE pCkt<>nil DO
Begin
- IF CompareText(pCkt.Name, cktname)=0 THEN
+ IF AnsiCompareText(pCkt.Name, cktname)=0 THEN
Begin
DSS.ActiveCircuit := pCkt;
Exit;
@@ -1200,26 +1115,22 @@ procedure TExecHelper.SetActiveCircuit(const cktname:String);
// IF none is found, just leave as is after giving error
- DoSimpleMsg(DSS, 'Error! No circuit named "' + cktname + '" found.' + CRLF +
- 'Active circuit not changed.', 258);
+ DoSimpleMsg(DSS, 'Error! No circuit named "%s" found. Active circuit not changed.', [cktname], 258);
End;
-{-------------------------------------------}
-procedure TExecHelper.DoLegalVoltageBases;
+procedure TExecHelper.DoLegalVoltageBases;
VAR
Dummy :pDoubleArray;
i,
Num :Integer;
-
-Begin
-
+begin
Dummy := AllocMem(Sizeof(Double) * 1000); // Big Buffer
Num := DSS.Parser.ParseAsVector(1000, Dummy);
- {Parsing zero-fills the array}
+ // Parsing zero-fills the array
- {LegalVoltageBases is a zero-terminated array, so we have to allocate
- one more than the number of actual values}
+ // LegalVoltageBases is a zero-terminated array, so we have to allocate
+ // one more than the number of actual values
WITH DSS.ActiveCircuit Do
Begin
@@ -1230,27 +1141,22 @@ procedure TExecHelper.DoLegalVoltageBases;
Reallocmem(Dummy, 0);
End;
-
-
-//----------------------------------------------------------------------------
function TExecHelper.DoOpenCmd:Integer;
// Opens a terminal and conductor of a ckt Element
VAR
retval :Integer;
Terminal :Integer;
Conductor :Integer;
- //ParamName :string;
// syntax: "Open class.name term=xx cond=xx"
// IF cond is omitted, all conductors are opened.
-
Begin
retval := SetActiveCktElement;
IF retval>0 THEN
Begin
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Terminal := DSS.Parser.IntValue;
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Conductor := DSS.Parser.IntValue;
With DSS.ActiveCircuit Do
@@ -1262,32 +1168,27 @@ function TExecHelper.DoOpenCmd:Integer;
End
ELSE
Begin
- DoSimpleMsg(DSS, 'Error in Open Command: Circuit Element Not Found.' +CRLF+ DSS.Parser.CmdString, 259);
+ DoSimpleMsg(DSS, 'Error in Open Command: Circuit Element not found. %s', [CRLF+ DSS.Parser.CmdString], 259);
End;
Result := 0;
End;
-
-
-//----------------------------------------------------------------------------
function TExecHelper.DoCloseCmd:Integer;
// Closes a terminal and conductor of a ckt Element
VAR
retval:Integer;
Terminal:Integer;
Conductor:Integer;
-// ParamName : string;
// syntax: "Close class.name term=xx cond=xx"
// IF cond is omitted, all conductors are opened
-
Begin
retval := SetActiveCktElement;
IF retval>0 THEN
Begin
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Terminal := DSS.Parser.IntValue;
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Conductor := DSS.Parser.IntValue;
With DSS.ActiveCircuit Do
@@ -1300,23 +1201,19 @@ function TExecHelper.DoCloseCmd:Integer;
End
ELSE
Begin
- DoSimpleMsg(DSS, 'Error in Close Command: Circuit Element Not Found.' +CRLF+ DSS.Parser.CmdString, 260);
+ DoSimpleMsg(DSS, 'Error in Close Command: Circuit Element not found. %s', [CRLF+ DSS.Parser.CmdString], 260);
End;
Result := 0;
+end;
-End;
-
-//----------------------------------------------------------------------------
function TExecHelper.DoResetCmd:Integer;
VAR
- {ParamName,} Param :String;
-
+ Param :String;
Begin
Result := 0;
-
// Get next parm and try to interpret as a file name
- {ParamName :=} DSS.Parser.NextParam;
- Param := UpperCase(DSS.Parser.StrValue);
+ DSS.Parser.NextParam;
+ Param := AnsiUpperCase(DSS.Parser.StrValue);
IF Length(Param) = 0
THEN Begin
DoResetMonitors;
@@ -1340,11 +1237,10 @@ function TExecHelper.DoResetCmd:Integer;
ELSE
- DoSimpleMsg(DSS, 'Unknown argument to Reset Command: "'+ Param+'"', 261);
+ DoSimpleMsg(DSS, 'Unknown argument to Reset Command: "%s"', [Param], 261);
End;
-
-End;
+end;
procedure TExecHelper.MarkCapandReactorBuses;
Var
@@ -1352,9 +1248,8 @@ procedure TExecHelper.MarkCapandReactorBuses;
pCapElement:TCapacitorObj;
pReacElement:TReactorObj;
ObjRef:Integer;
-
begin
-{Mark all buses as keepers if there are capacitors or reactors on them}
+ // Mark all buses as keepers if there are capacitors or reactors on them
pClass := GetDSSClassPtr(DSS, 'capacitor');
If pClass<>Nil then
Begin
@@ -1370,8 +1265,7 @@ procedure TExecHelper.MarkCapandReactorBuses;
End;
End;
- {Now Get the Reactors}
-
+ // Now Get the Reactors
pClass := GetDSSClassPtr(DSS, 'reactor');
If pClass<>Nil then
Begin
@@ -1384,7 +1278,7 @@ procedure TExecHelper.MarkCapandReactorBuses;
If pReacElement.Enabled Then DSS.ActiveCircuit.Buses^[pReacElement.Terminals[0].Busref].Keep := TRUE;
Except
On E:Exception Do Begin
- DoSimpleMsg(DSS, Format('%s %s Reactor=%s Bus No.=%d ',[E.Message, CRLF, pReacElement.Name, pReacElement.NodeRef^[1] ]), 9999);
+ DoSimpleMsg(DSS, '%s %s Reactor=%s Bus No.=%d ',[E.Message, CRLF, pReacElement.Name, pReacElement.NodeRef^[1] ], 9999);
Break;
End;
End;
@@ -1393,21 +1287,20 @@ procedure TExecHelper.MarkCapandReactorBuses;
End;
end;
-//----------------------------------------------------------------------------
function TExecHelper.DoReduceCmd:Integer;
VAR
MetObj:TEnergyMeterObj;
MeterClass: TEnergyMeter;
- {ParamName,} Param :String;
+ Param :String;
DevClassIndex:Integer;
Begin
Result := 0;
// Get next parm and try to interpret as a file name
- {ParamName :=} DSS.Parser.NextParam;
- Param := UpperCase(DSS.Parser.StrValue);
+ DSS.Parser.NextParam;
+ Param := AnsiUpperCase(DSS.Parser.StrValue);
- {Mark Capacitor and Reactor buses as Keep so we don't lose them}
+ // Mark Capacitor and Reactor buses as Keep so we don't lose them
MarkCapandReactorBuses;
IF Length(Param) = 0 Then Param := 'A';
@@ -1422,7 +1315,7 @@ function TExecHelper.DoReduceCmd:Integer;
End;
ELSE
- {Reduce a specific meter}
+ // Reduce a specific meter
DevClassIndex := DSS.ClassNames.Find('energymeter');
IF DevClassIndex > 0 THEN
Begin
@@ -1432,22 +1325,18 @@ function TExecHelper.DoReduceCmd:Integer;
MetObj := MeterClass.GetActiveObj;
MetObj.ReduceZone;
End
- Else DoSimpleMsg(DSS, 'EnergyMeter "'+Param+'" not found.', 262);
+ Else DoSimpleMsg(DSS, 'EnergyMeter "%s" not found.', [Param], 262);
End;
End;
+end;
-End;
-
-//----------------------------------------------------------------------------
function TExecHelper.DoResetMonitors:Integer;
VAR
pMon:TMonitorObj;
-Begin
-
+begin
WITH DSS.ActiveCircuit DO
- Begin
-
+ begin
pMon := Monitors.First;
WHILE pMon<>nil DO
Begin
@@ -1457,42 +1346,35 @@ function TExecHelper.DoResetMonitors:Integer;
Result :=0;
End;
+end;
-End;
-
-//----------------------------------------------------------------------------
-function TExecHelper.DoFileEditCmd:Integer;
-
-VAR
- {ParamName,} Param :String;
-
-Begin
+function TExecHelper.DoFileEditCmd: Integer;
+var
+ Param: String;
+begin
Result := 0;
-
// Get next parm and try to interpret as a file name
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
- IF FileExists(Param) THEN FireOffEditor(DSS, Param)
- ELSE Begin
- DSS.GlobalResult := 'File "'+param+'" does not exist.';
+ if FileExists(Param) then
+ FireOffEditor(DSS, Param)
+ else
+ begin
+ DSS.GlobalResult := Format(_('File "%s" does not exist.'), [param]);
Result := 1;
- End;
-End;
+ end;
+end;
-//----------------------------------------------------------------------------
procedure TExecHelper.ParseObjName(const fullname:String; VAR objname, propname:String);
-
-{ Parse strings such as
-
- 1. Classname.Objectname,Property (full name)
- 2. Objectname.Property (classname omitted)
- 3. Property (classname and objectname omitted
-}
-
+// Parse strings such as
+//
+// 1. Classname.Objectname,Property (full name)
+// 2. Objectname.Property (classname omitted)
+// 3. Property (classname and objectname omitted
+//
VAR
DotPos1, DotPos2:Integer;
-
Begin
DotPos1 := Pos('.',fullname);
CASE Dotpos1 of
@@ -1502,8 +1384,7 @@ procedure TExecHelper.ParseObjName(const fullname:String; VAR objname, propname:
PropName := FullName;
End;
- ELSE Begin
-
+ ELSE begin
PropName := Copy(FullName,Dotpos1+1,(Length(FullName)-DotPos1));
DotPos2 := Pos('.', PropName);
CASE DotPos2 of
@@ -1524,65 +1405,57 @@ procedure TExecHelper.ParseObjName(const fullname:String; VAR objname, propname:
End;
function TExecHelper.DoQueryCmd:Integer;
-{ ? Command }
-{ Syntax: ? Line.Line1.R1}
+// ? Command
+// Syntax: ? Line.Line1.R1
VAR
- // ParamName:String;
Param, ObjName, PropName:String;
PropIndex:Integer;
-
-
-Begin
-
+begin
Result := 0;
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
ParseObjName(Param, ObjName, PropName);
IF CompareText(ObjName,'solution')=0 THEN
Begin // special for solution
- DSS.ActiveDSSClass := DSS.SolutionClass;
- DSS.ActiveDSSObject := DSS.ActiveCircuit.Solution;
+ // DSS.ActiveDSSClass := DSS.SolutionClass;
+ //DSS.ActiveDSSObject := DSS.ActiveCircuit.Solution;
+ DSS.GlobalPropertyValue := 'Property Unknown';
End ELSE
Begin
// Set Object Active
DSS.Parser.cmdstring := '"' + Objname + '"';
DoSelectCmd;
+ // Put property value in global VARiable
+ PropIndex := DSS.ActiveDSSClass.Propertyindex(PropName);
+ IF PropIndex>0 THEN
+ DSS.GlobalPropertyValue := DSS.ActiveDSSObject.GetPropertyValue(PropIndex)
+ ELSE
+ DSS.GlobalPropertyValue := 'Property Unknown';
End;
- // Put property value in global VARiable
- PropIndex := DSS.ActiveDSSClass.Propertyindex(PropName);
- IF PropIndex>0 THEN
- DSS.GlobalPropertyValue := DSS.ActiveDSSObject.GetPropertyValue(PropIndex)
- ELSE
- DSS.GlobalPropertyValue := 'Property Unknown';
-
DSS.GlobalResult := DSS.GlobalPropertyValue;
If DSS.LogQueries Then WriteQueryLogFile(DSS, param, DSS.GlobalResult); // write time-stamped query
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoResetMeters:Integer;
-
Begin
Result := 0;
DSS.EnergyMeterClass.ResetAll
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoNextCmd:Integer;
VAR
- {ParamName,} Param :String;
-
+ Param :String;
Begin
Result := 0;
// Get next parm and try to interpret as a file name
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
With DSS.ActiveCircuit.Solution Do
@@ -1594,38 +1467,23 @@ function TExecHelper.DoNextCmd:Integer;
ELSE
END;
+end;
-End;
-
-//----------------------------------------------------------------------------
procedure TExecHelper.DoAboutBox;
-
Begin
-
- If NoFormsAllowed Then Exit;
-
- ShowAboutBox;
-
-
+ If NoFormsAllowed Then Exit;
+ ShowAboutBox;
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoSetVoltageBases:integer;
-
-
Begin
-
Result := 0;
-
DSS.ActiveCircuit.Solution.SetVoltageBases;
-
End;
-//----------------------------------------------------------------------------
-function TExecHelper.AddObject(const ObjType, Name:String):Integer;
-
-
-Begin
-
+function TExecHelper.AddObject(const ObjType, Name: String): Integer;
+var
+ Obj: TDSSObject = NIL;
+begin
Result :=0;
// Search for class IF not already active
@@ -1635,7 +1493,7 @@ function TExecHelper.AddObject(const ObjType, Name:String):Integer;
CASE DSS.LastClassReferenced of
0: Begin
- DoSimpleMsg(DSS, 'New Command: Object Type "' + ObjType + '" not found.' + CRLF + DSS.Parser.CmdString, 263);
+ DoSimpleMsg(DSS, 'New Command: Object Type "%s" not found. %s', [ObjType, CRLF + DSS.Parser.CmdString], 263);
Result := 0;
Exit;
End;{Error}
@@ -1648,100 +1506,82 @@ function TExecHelper.AddObject(const ObjType, Name:String):Integer;
// Name must be supplied
IF Length(Name) = 0
THEN Begin
- DoSimpleMsg(DSS, 'Object Name Missing'+ CRLF + DSS.Parser.CmdString, 264);
+ DoSimpleMsg(DSS, 'Object Name Missing %s', [CRLF + DSS.Parser.CmdString], 264);
Exit;
End;
- // now let's make a new object or set an existing one active, whatever the case
+ // now let's make a new object or set an existing one active, whatever the case
CASE DSS.ActiveDSSClass.DSSClassType Of
// These can be added WITHout having an active circuit
// Duplicates not allowed in general DSS objects;
DSS_OBJECT : IF NOT DSS.ActiveDSSClass.SetActive(Name)
THEN Begin
- Result := DSS.ActiveDSSClass.NewObject(Name);
- DSS.DSSObjs.Add(DSS.ActiveDSSObject); // Stick in pointer list to keep track of it
+ Obj := DSS.ActiveDSSClass.NewObject(Name, True, Result);
+ DSS.DSSObjs.Add(Obj); // Stick in pointer list to keep track of it
End;
ELSE
// These are circuit elements
IF DSS.ActiveCircuit = nil
THEN Begin
- DoSimpleMsg(DSS, 'You Must Create a circuit first: "new circuit.yourcktname"', 265);
+ DoSimpleMsg(DSS, _('You Must Create a circuit first: "new circuit.yourcktname"'), 265);
Exit;
End;
// IF Object already exists. Treat as an Edit IF dulicates not allowed
IF DSS.ActiveCircuit.DuplicatesAllowed THEN
Begin
- Result := DSS.ActiveDSSClass.NewObject(Name); // Returns index into this class
- DSS.ActiveCircuit.AddCktElement(); // Adds active object to active circuit
+ Obj := DSS.ActiveDSSClass.NewObject(Name, True, Result); // Returns index into this class
+ DSS.ActiveCircuit.AddCktElement(TDSSCktElement(Obj)); // Adds active object to active circuit
End
ELSE
Begin // Check to see if we can set it active first
IF Not DSS.ActiveDSSClass.SetActive(Name) THEN
Begin
- Result := DSS.ActiveDSSClass.NewObject(Name); // Returns index into this class
- DSS.ActiveCircuit.AddCktElement(); // Adds active object to active circuit
+ Obj := DSS.ActiveDSSClass.NewObject(Name, True, Result); // Returns index into this class
+ DSS.ActiveCircuit.AddCktElement(TDSSCktElement(Obj)); // Adds active object to active circuit
End
ELSE
Begin
- DoSimpleMsg(DSS, 'Warning: Duplicate new element definition: "'+ DSS.ActiveDSSClass.Name+'.'+Name+'"'+
- CRLF+ 'Element being redefined.', 266);
+ DoSimpleMsg(DSS, 'Warning: Duplicate new element definition: "%s.%s". Element being redefined.', [DSS.ActiveDSSClass.Name, Name], 266);
End;
End;
End;
-
- // ActiveDSSObject now points to the object just added
- // IF a circuit element, ActiveCktElement in ActiveCircuit is also set
-
- If Result>0 Then DSS.ActiveDSSObject.ClassIndex := Result;
-
- DSS.ActiveDSSClass.Edit; // Process remaining instructions on the command line
-
+ DSS.ActiveDSSClass.Edit(DSS.Parser); // Process remaining instructions on the command line
End;
End;
-//----------------------------------------------------------------------------
function TExecHelper.EditObject(const ObjType, Name:String):Integer;
+begin
+ Result :=0;
+ DSS.LastClassReferenced := DSS.ClassNames.Find(ObjType);
-Begin
-
- Result :=0;
- DSS.LastClassReferenced := DSS.ClassNames.Find(ObjType);
-
- CASE DSS.LastClassReferenced of
- 0: Begin
- DoSimpleMsg(DSS, 'Edit Command: Object Type "' + ObjType + '" not found.'+ CRLF + DSS.Parser.CmdString, 267);
- Result := 0;
- Exit;
- End;{Error}
- ELSE
-
- // intrinsic and user Defined models
- // Edit the DSS object
- DSS.ActiveDSSClass := DSS.DSSClassList.Get(DSS.LastClassReferenced);
- IF DSS.ActiveDSSClass.SetActive(Name) THEN
- Begin
- Result := DSS.ActiveDSSClass.Edit; // Edit the active object
- End;
- End;
+ if DSS.LastClassReferenced = 0 then
+ begin
+ DoSimpleMsg(DSS, 'Edit Command: Object Type "%s" not found. %s', [ObjType, CRLF + DSS.Parser.CmdString], 267);
+ Result := 0;
+ Exit;
+ end;
-End;
+ // intrinsic and user Defined models
+ // Edit the DSS object
+ DSS.ActiveDSSClass := DSS.DSSClassList.Get(DSS.LastClassReferenced);
+ IF DSS.ActiveDSSClass.SetActive(Name) THEN
+ Begin
+ Result := DSS.ActiveDSSClass.Edit(DSS.Parser); // Edit the active object
+ End;
+ end;
-//----------------------------------------------------------------------------
function TExecHelper.DoSetkVBase: Integer;
-
-VAR
+var
ParamName, BusName:String;
kVValue :Double;
-
-Begin
-
-// Parse off next two items on line
+begin
+ // Parse off next two items on line
ParamName := DSS.Parser.NextParam;
- BusName := LowerCase(DSS.Parser.StrValue);
+ BusName := AnsiLowerCase(DSS.Parser.StrValue);
ParamName := DSS.Parser.NextParam;
kVValue := DSS.Parser.DblValue;
@@ -1763,27 +1603,18 @@ function TExecHelper.DoSetkVBase: Integer;
End
ELSE Begin
Result := 1;
- AppendGlobalResult(DSS, 'Bus ' + BusName + ' Not Found.');
+ AppendGlobalResult(DSS, 'Bus ' + BusName + ' not found.');
End;
End;
-
-
-
End;
-
-//----------------------------------------------------------------------------
PROCEDURE TExecHelper.DoAutoAddBusList(const S: String);
-
VAR
ParmName,
Param, S2 :String;
- F: TBufferedFileStream = nil;
-
-
+ F: TStream = nil;
begin
-
DSS.ActiveCircuit.AutoAddBusList.Clear;
// Load up auxiliary parser to reparse the array list or file name
@@ -1791,33 +1622,31 @@ function TExecHelper.DoSetkVBase: Integer;
ParmName := DSS.AuxParser.NextParam ;
Param := DSS.AuxParser.StrValue;
- {Syntax can be either a list of bus names or a file specification: File= ...}
+ // Syntax can be either a list of bus names or a file specification: File= ...
If CompareText(Parmname, 'file') = 0
THEN Begin
// load the list from a file
-
TRY
- F := TBufferedFileStream.Create(AdjustInputFilePath(DSS, Param), fmOpenRead);
+ F := DSS.GetROFileStream(Param);
WHILE (F.Position+1) < F.Size Do
- Begin // Fixed 7/8/01 to handle all sorts of bus names
+ Begin
FSReadln(F, S2);
DSS.AuxParser.CmdString := S2;
ParmName := DSS.AuxParser.NextParam ;
Param := DSS.AuxParser.StrValue;
- IF Length(Param) > 0
- THEN DSS.ActiveCircuit.AutoAddBusList.Add(Param);
+ IF Length(Param) > 0 THEN
+ DSS.ActiveCircuit.AutoAddBusList.Add(Param);
End;
FreeAndNil(F);
EXCEPT
- On E:Exception Do DoSimpleMsg(DSS, 'Error trying to read bus list file. Error is: '+E.message, 268);
+ On E:Exception Do
+ DoSimpleMsg(DSS, 'Error trying to read bus list file: %s', [E.message], 268);
END;
-
-
End
- ELSE Begin
-
+ ELSE
+ begin
// Parse bus names off of array list
WHILE Length(Param) > 0 Do
BEGIN
@@ -1827,39 +1656,28 @@ function TExecHelper.DoSetkVBase: Integer;
END;
End;
-
end;
-//----------------------------------------------------------------------------
procedure TExecHelper.DoKeeperBusList(Const S:String);
-
-
-// Created 4/25/03
-
-{Set Keep flag on buses found in list so they aren't eliminated by some reduction
- algorithm. This command is cumulative. To clear flag, use Reset Keeplist}
-
+// Set Keep flag on buses found in list so they aren't eliminated by some reduction
+// algorithm. This command is cumulative. To clear flag, use Reset Keeplist
VAR
ParmName,
Param, S2 :String;
- F: TBufferedFileStream = nil;
+ F: TStream = nil;
iBus :Integer;
-
begin
-
// Load up auxiliary parser to reparse the array list or file name
DSS.AuxParser.CmdString := S;
ParmName := DSS.AuxParser.NextParam ;
Param := DSS.AuxParser.StrValue;
- {Syntax can be either a list of bus names or a file specification: File= ...}
-
+ // Syntax can be either a list of bus names or a file specification: File= ...
If CompareText(Parmname, 'file') = 0 THEN
Begin
// load the list from a file
-
TRY
- F := TBufferedFileStream.Create(AdjustInputFilePath(DSS, Param), fmOpenRead);
+ F := DSS.GetROFileStream(Param);
WHILE (F.Position + 1) < F.Size Do
Begin // Fixed 7/8/01 to handle all sorts of bus names
FSReadln(F, S2);
@@ -1876,13 +1694,13 @@ procedure TExecHelper.DoKeeperBusList(Const S:String);
FreeAndNil(F);
EXCEPT
- On E:Exception Do DoSimpleMsg(DSS, 'Error trying to read bus list file "+param+". Error is: '+E.message, 269);
+ On E:Exception Do
+ DoSimpleMsg(DSS, 'Error trying to read bus list file "%s": %s', [param, E.message], 269);
END;
End
- ELSE Begin
-
+ ELSE begin
// Parse bus names off of array list
WHILE Length(Param) > 0 Do
BEGIN
@@ -1897,10 +1715,8 @@ procedure TExecHelper.DoKeeperBusList(Const S:String);
END;
End;
-
end;
-//----------------------------------------------------------------------------
function TExecHelper.DocktlossesCmd: Integer;
Var
LossValue :complex;
@@ -1913,8 +1729,6 @@ function TExecHelper.DocktlossesCmd: Integer;
DSS.GlobalResult := Format('%10.5g, %10.5g',[LossValue.re * 0.001, LossValue.im*0.001]);
End
ELSE DSS.GlobalResult := 'No Active Circuit.';
-
-
end;
function TExecHelper.DocurrentsCmd: Integer;
@@ -1940,23 +1754,18 @@ function TExecHelper.DocurrentsCmd: Integer;
End
Else
DSS.GlobalResult := 'No Active Circuit.';
-
-
end;
function TExecHelper.DoNodeListCmd: Integer;
VAR
NValues, i: Integer;
CktElementName: String;
-
-
-Begin
-
+begin
Result := 0;
If DSS.ActiveCircuit <> Nil Then
Begin
- {S :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
CktElementName := DSS.Parser.StrValue ;
If Length(CktElementName) > 0 Then SetObject(DSS, CktElementName);
@@ -1974,8 +1783,6 @@ function TExecHelper.DoNodeListCmd: Integer;
Else
DSS.GlobalResult := 'No Active Circuit.';
End;
-
-
end;
@@ -1995,7 +1802,6 @@ function TExecHelper.DolossesCmd: Integer;
End;
End
ELSE DSS.GlobalResult := 'No Active Circuit.';
-
end;
function TExecHelper.DophaselossesCmd: Integer;
@@ -2006,8 +1812,7 @@ function TExecHelper.DophaselossesCmd: Integer;
cBuffer:pComplexArray;
NValues, i : Integer;
-Begin
-
+begin
Result := 0;
IF DSS.ActiveCircuit <> Nil THEN
@@ -2067,7 +1872,7 @@ function TExecHelper.DopowersCmd(Total: Integer): Integer;
myEnd := NConds * j;
For i := myInit to myEnd DO
Begin
- myBuffer[j - 1] := cadd(myBuffer[j - 1], cBuffer^[i]);
+ myBuffer[j - 1] := myBuffer[j - 1] + cBuffer^[i];
End;
DSS.GlobalResult := DSS.GlobalResult+ Format('%10.5g, %10.5g,', [myBuffer[j - 1].re*0.001, myBuffer[j - 1].im*0.001]);
End;
@@ -2086,8 +1891,7 @@ function TExecHelper.DoseqcurrentsCmd: Integer;
IPh, I012 : Array[1..3] of Complex;
cBuffer:pComplexArray;
-Begin
-
+begin
Result := 0;
IF DSS.ActiveCircuit <> Nil THEN
WITH DSS.ActiveCircuit DO
@@ -2116,12 +1920,10 @@ function TExecHelper.DoseqcurrentsCmd: Integer;
End;
End;
Reallocmem(cBuffer,0);
- End; {ELSE}
- End; {WITH ActiveCktElement}
- End {IF/WITH DSS.ActiveCircuit}
+ End; // ELSE
+ End; // WITH ActiveCktElement
+ End // IF/WITH DSS.ActiveCircuit
ELSE DSS.GlobalResult := 'No Active Circuit';
-
-
end;
function TExecHelper.DoSeqpowersCmd: Integer;
@@ -2135,8 +1937,7 @@ function TExecHelper.DoSeqpowersCmd: Integer;
IPh, I012 : Array[1..3] of Complex;
cBuffer:pComplexArray;
-Begin
-
+begin
Result := 0;
IF DSS.ActiveCircuit <> Nil THEN
WITH DSS.ActiveCircuit DO Begin
@@ -2160,7 +1961,7 @@ function TExecHelper.DoSeqpowersCmd: Integer;
Phase2SymComp(pComplexArray(@Iph), pComplexArray(@I012));
Phase2SymComp(pComplexArray(@Vph), pComplexArray(@V012));
For i := 1 to 3 DO Begin
- S := Cmul(V012[i], conjg(I012[i]));
+ S := V012[i] * cong(I012[i]);
DSS.GlobalResult := DSS.GlobalResult+ Format('%10.5g, %10.5g,',[S.re*0.003, S.im*0.003]); // 3-phase kW conversion
End;
End;
@@ -2169,8 +1970,6 @@ function TExecHelper.DoSeqpowersCmd: Integer;
End;
End
ELSE DSS.GlobalResult := 'No Active Circuit';
-
-
end;
function TExecHelper.DoseqvoltagesCmd: Integer;
@@ -2201,11 +2000,9 @@ function TExecHelper.DoseqvoltagesCmd: Integer;
IF Nvalues < 3 THEN
For i := 1 to 3*Nterms DO DSS.GlobalResult := DSS.GlobalResult + '-1.0, ' // Signify n/A
ELSE
- Begin
-
+ begin
FOR j := 1 to Nterms Do
- Begin
-
+ begin
k :=(j-1)*NConds;
FOR i := 1 to 3 DO
Begin
@@ -2235,16 +2032,15 @@ function TExecHelper.DoseqvoltagesCmd: Integer;
END;
End
Else
- DSS.GlobalResult := 'Element Disabled'; // Disabled
+ DSS.GlobalResult := _('Element Disabled'); // Disabled
End
- ELSE DSS.GlobalResult := 'No Active Circuit';
+ ELSE DSS.GlobalResult := _('No Active Circuit');
End;
-//----------------------------------------------------------------------------
function TExecHelper.DovoltagesCmd(Const PerUnit:Boolean): Integer;
// Bus Voltages at active terminal
@@ -2254,8 +2050,7 @@ function TExecHelper.DovoltagesCmd(Const PerUnit:Boolean): Integer;
ActiveBus:TDSSBus;
VMag:Double;
-Begin
-
+begin
Result := 0;
IF DSS.ActiveCircuit <> Nil THEN
WITH DSS.ActiveCircuit DO
@@ -2278,10 +2073,8 @@ function TExecHelper.DovoltagesCmd(Const PerUnit:Boolean): Integer;
Else DSS.GlobalResult := 'No Active Bus.';
End
ELSE DSS.GlobalResult := 'No Active Circuit.';
-
end;
-//----------------------------------------------------------------------------
function TExecHelper.DoZscCmd(Zmatrix:Boolean): Integer;
// Bus Short Circuit matrix
@@ -2290,8 +2083,7 @@ function TExecHelper.DoZscCmd(Zmatrix:Boolean): Integer;
ActiveBus:TDSSBus;
Z:Complex;
-Begin
-
+begin
Result := 0;
IF DSS.ActiveCircuit <> Nil THEN
WITH DSS.ActiveCircuit DO
@@ -2303,8 +2095,7 @@ function TExecHelper.DoZscCmd(Zmatrix:Boolean): Integer;
If not assigned(ActiveBus.Zsc) Then Exit;
With ActiveBus Do
FOR i := 1 to NumNodesThisBus DO Begin
- For j := 1 to NumNodesThisBus Do Begin
-
+ For j := 1 to NumNodesThisBus Do begin
If ZMatrix Then Z := Zsc.GetElement(i,j)
Else Z := Ysc.GetElement(i,j);
DSS.GlobalResult := DSS.GlobalResult + Format('%-.5g, %-.5g, ', [Z.re, Z.im]);
@@ -2316,10 +2107,8 @@ function TExecHelper.DoZscCmd(Zmatrix:Boolean): Integer;
Else DSS.GlobalResult := 'No Active Bus.';
End
ELSE DSS.GlobalResult := 'No Active Circuit.';
-
end;
-//----------------------------------------------------------------------------
function TExecHelper.DoZsc012Cmd: Integer;
// Bus Short Circuit matrix
VAR
@@ -2360,7 +2149,7 @@ function TExecHelper.DoZsc012Cmd: Integer;
ZSC012 := Ap2s.MtrxMult(Zsc012Temp);
// Cleanup
Zsc012Temp.Free;
- {Just return diagonal elements only}
+ // Just return diagonal elements only
Z0 := Zsc012.GetElement(1,1);
Z1 := Zsc012.GetElement(2,2);
Z2 := Zsc012.GetElement(3,3);
@@ -2370,16 +2159,12 @@ function TExecHelper.DoZsc012Cmd: Integer;
end;
end;
-//----------------------------------------------------------------------------
function TExecHelper.DoZsc10Cmd: Integer;
// Bus Short Circuit matrix
-
VAR
ActiveBus:TDSSBus;
Z:Complex;
-
-Begin
-
+begin
Result := 0;
IF DSS.ActiveCircuit <> Nil THEN
WITH DSS.ActiveCircuit DO
@@ -2389,8 +2174,7 @@ function TExecHelper.DoZsc10Cmd: Integer;
ActiveBus := Buses^[ActiveBusIndex];
DSS.GlobalResult := '';
If not assigned(ActiveBus.Zsc) Then Exit;
- With ActiveBus Do Begin
-
+ With ActiveBus Do begin
Z := Zsc1;
DSS.GlobalResult := DSS.GlobalResult + Format('Z1, %-.5g, %-.5g, ', [Z.re, Z.im]) + CRLF;
@@ -2402,17 +2186,12 @@ function TExecHelper.DoZsc10Cmd: Integer;
Else DSS.GlobalResult := 'No Active Bus.';
End
ELSE DSS.GlobalResult := 'No Active Circuit.';
-
end;
-//----------------------------------------------------------------------------
function TExecHelper.DoAllocateLoadsCmd: Integer;
-
-{ Requires an EnergyMeter Object at the head of the feeder
- Adjusts loads defined by connected kVA or kWh billing
-}
-
+// Requires an EnergyMeter Object at the head of the feeder
+// Adjusts loads defined by connected kVA or kWh billing
VAR
pMeter :TEnergyMeterObj;
pSensor:TSensorObj;
@@ -2426,27 +2205,26 @@ function TExecHelper.DoAllocateLoadsCmd: Integer;
With Solution Do
Begin
If Mode <> TSolveMode.SNAPSHOT Then Mode := TSolveMode.SNAPSHOT; // Resets meters, etc. if not in snapshot mode
- Solve; {Make guess based on present allocationfactors}
+ Solve; // Make guess based on present allocationfactors
End;
- {Allocation loop -- make MaxAllocationIterations iterations}
- FOR iterCount := 1 to DSS.MaxAllocationIterations Do Begin
-
- {Do EnergyMeters}
+ // Allocation loop -- make MaxAllocationIterations iterations
+ FOR iterCount := 1 to DSS.MaxAllocationIterations Do begin
+ // Do EnergyMeters
pMeter := EnergyMeters.First;
WHILE pMeter <> NIL Do Begin
pMeter.CalcAllocationFactors;
pMeter := EnergyMeters.Next;
End;
- {Now do other Sensors}
+ // Now do other Sensors
pSensor := Sensors.First;
WHILE pSensor <> NIL Do Begin
pSensor.CalcAllocationFactors;
pSensor := Sensors.Next;
End;
- {Now let the EnergyMeters run down the circuit setting the loads}
+ // Now let the EnergyMeters run down the circuit setting the loads
pMeter := EnergyMeters.First;
WHILE pMeter <> NIL Do Begin
pMeter.AllocateLoad;
@@ -2458,7 +2236,6 @@ function TExecHelper.DoAllocateLoadsCmd: Integer;
End;
end;
-//----------------------------------------------------------------------------
procedure TExecHelper.DoSetAllocationFactors(const X: Double);
VAR
@@ -2466,7 +2243,7 @@ procedure TExecHelper.DoSetAllocationFactors(const X: Double);
begin
IF X <= 0.0
- THEN DoSimpleMsg(DSS, 'Allocation Factor must be greater than zero.', 271)
+ THEN DoSimpleMsg(DSS, _('Allocation Factor must be greater than zero.'), 271)
ELSE WITH DSS.ActiveCircuit Do
Begin
pLoad := Loads.First;
@@ -2485,7 +2262,7 @@ procedure TExecHelper.DoSetCFactors(const X: Double);
begin
IF X <= 0.0
- THEN DoSimpleMsg(DSS, 'CFactor must be greater than zero.', 271)
+ THEN DoSimpleMsg(DSS, _('CFactor must be greater than zero.'), 271)
ELSE WITH DSS.ActiveCircuit Do
Begin
pLoad := Loads.First;
@@ -2497,7 +2274,6 @@ procedure TExecHelper.DoSetCFactors(const X: Double);
End;
end;
-//----------------------------------------------------------------------------
function TExecHelper.DoHarmonicsList(const S:String):Integer;
VAR
@@ -2526,27 +2302,23 @@ function TExecHelper.DoHarmonicsList(const S:String):Integer;
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoFormEditCmd:Integer;
-Begin
-
+begin
Result := 0;
If NoFormsAllowed Then Exit;
DoSelectCmd; // Select ActiveObject
- IF DSS.ActiveDSSObject <> NIL THEN Begin
-
+ IF DSS.ActiveDSSObject <> NIL THEN begin
ShowPropEditForm;
End
ELSE Begin
- DoSimpleMsg(DSS, 'Element Not Found.', 272);
+ DoSimpleMsg(DSS, _('Element not found.'), 272);
Result := 1;
End;
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoMeterTotals:Integer;
Var
i: Integer;
@@ -2563,7 +2335,6 @@ function TExecHelper.DoMeterTotals:Integer;
End;
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoCapacityCmd:Integer;
Var
@@ -2586,7 +2357,7 @@ function TExecHelper.DoCapacityCmd:Integer;
END;
CASE ParamPointer OF
- 0: DoSimpleMsg(DSS, 'Unknown parameter "'+ParamName+'" for Capacity Command', 273);
+ 0: DoSimpleMsg(DSS, 'Unknown parameter "%s" for Capacity Command', [ParamName], 273);
1: DSS.ActiveCircuit.CapacityStart := DSS.Parser.DblValue;
2: DSS.ActiveCircuit.CapacityIncrement := DSS.Parser.DblValue;
@@ -2606,7 +2377,6 @@ function TExecHelper.DoCapacityCmd:Integer;
End;
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoClassesCmd:Integer;
VAR i:Integer;
@@ -2617,14 +2387,12 @@ function TExecHelper.DoClassesCmd:Integer;
Result := 0;
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoUserClassesCmd:Integer;
Begin
Result := 0;
AppendGlobalResult(DSS, 'No User Classes Defined.');
End;
-//----------------------------------------------------------------------------
function TExecHelper.DoZscRefresh:Integer;
Var j:Integer;
@@ -2646,25 +2414,20 @@ function TExecHelper.DoZscRefresh:Integer;
End;
Except
- On E:Exception Do DoSimpleMsg(DSS, 'ZscRefresh Error: ' + E.message + CRLF , 274);
+ On E:Exception Do DoSimpleMsg(DSS, 'ZscRefresh Error: %s', [E.message], 274);
End;
-
-
-End;
-
+end;
function TExecHelper.DoVarValuesCmd:Integer;
-
Var
i: Integer;
// PcElem:TPCElement;
-Begin
-
+begin
Result := 0;
If DSS.ActiveCircuit <> Nil Then
With DSS.ActiveCircuit Do
Begin
- {Check if PCElement}
+ // Check if PCElement
CASE (ActiveCktElement.DSSObjType and BASECLASSMASK) OF
PC_ELEMENT: With ActiveCktElement as TPCElement Do
Begin
@@ -2675,35 +2438,31 @@ function TExecHelper.DoVarValuesCmd:Integer;
AppendGlobalResult(DSS, 'Null');
End;
End;
-
-End;
+end;
function TExecHelper.DoValVarCmd:Integer;
-
-{Geg value of specified variable by name of index,}
+// Get value of specified variable by name of index
Var
ParamName, Param :String;
VarIndex :Integer;
PropIndex :Integer;
PCElem :TPCElement;
-Begin
-
+begin
Result := 0;
- {Check to make sure this is a PC Element. If not, return null string in global result}
+ // Check to make sure this is a PC Element. If not, return null string in global result
If (DSS.ActiveCircuit.ActiveCktElement.DSSObjType And BASECLASSMASK) <> PC_ELEMENT Then
DSS.GlobalResult := ''
- Else Begin
-
+ Else begin
PCElem := DSS.ActiveCircuit.ActiveCktElement As TPCElement;
- {Get next parameter on command line}
+ // Get next parameter on command line
- ParamName := UpperCase(DSS.Parser.NextParam);
+ ParamName := AnsiUpperCase(DSS.Parser.NextParam);
Param := DSS.Parser.StrValue;
PropIndex := 1;
@@ -2727,16 +2486,13 @@ function TExecHelper.DoValVarCmd:Integer;
Else DSS.GlobalResult := ''; {Invalid var name or index}
End;
-
-
-End;
+end;
function TExecHelper.DoVarNamesCmd :Integer;
Var
i: Integer;
-Begin
-
+begin
Result := 0;
If DSS.ActiveCircuit <> Nil Then
With DSS.ActiveCircuit Do
@@ -2752,35 +2508,30 @@ function TExecHelper.DoVarNamesCmd :Integer;
AppendGlobalResult(DSS, 'Null');
End;
End;
-
-End;
+end;
function TExecHelper.DoBusCoordsCmd(SwapXY:Boolean):Integer;
-
-{
- Format of File should be
-
- Busname, x, y
-
- (x, y are real values)
-
- If SwapXY is true, x and y values are swapped
-
-}
-
-Var
+// Format of File should be
+//
+// Busname, x, y
+//
+// (x, y are real values)
+//
+// If SwapXY is true, x and y values are swapped
+var
strings: TStringList = nil;
- {ParamName,} Param,
+ FStream: TStream = NIL;
+ Param,
BusName : String;
iB : Integer;
iLine : Integer;
stringIdx: Integer;
-Begin
+begin
Result := 0;
- {Get next parameter on command line}
+ // Get next parameter on command line
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
Try
@@ -2788,7 +2539,9 @@ function TExecHelper.DoBusCoordsCmd(SwapXY:Boolean):Integer;
Try
strings := TStringList.Create;
iLine := 0;
- strings.LoadFromFile(AdjustInputFilePath(DSS, Param));
+
+ Fstream := DSS.GetROFileStream(Param);
+ strings.LoadFromStream(Fstream);
for stringIdx := 0 to (strings.Count - 1) do
Begin
Inc(iLine);
@@ -2804,30 +2557,31 @@ function TExecHelper.DoBusCoordsCmd(SwapXY:Boolean):Integer;
End;
End;
End;
- {Else just ignore a bus that's not in the circuit}
+ // Else just ignore a bus that's not in the circuit
End;
Except
- {**CHANGE THIS ERROR MESSAGE**}
- ON E:Exception Do Begin
- If iLine = -1 Then DoSimpleMsg(DSS, 'Bus Coordinate file: "' + Param + '" not found; ' + E.Message , 275)
- Else DoSimpleMsg(DSS, 'Bus Coordinate file: Error Reading Line ' + InttoStr(Iline)+'; ' + E.Message , 275);
+ // **CHANGE THIS ERROR MESSAGE**
+ ON E:Exception Do
+ Begin
+ If iLine = -1 Then
+ DoSimpleMsg(DSS, 'Bus Coordinate file "%s" could not be read: %s', [Param, E.Message], 275)
+ Else
+ DoSimpleMsg(DSS, 'Bus Coordinate file: Error Reading Line %d; %s', [Iline, E.Message], 275);
End;
End;
Finally
FreeAndNil(strings);
+ FreeAndNil(FStream);
End;
-
-End;
+end;
function TExecHelper.DoMakePosSeq:Integer;
-
-Var
+var
CktElem:TDSSCktElement;
-
-Begin
+begin
Result := 0;
DSS.ActiveCircuit.PositiveSequence := TRUE;
@@ -2835,16 +2589,13 @@ function TExecHelper.DoMakePosSeq:Integer;
CktElem := DSS.ActiveCircuit.CktElements.First;
While CktElem<>Nil Do
Begin
- CktElem.MakePosSequence;
+ CktElem.MakePosSequence();
CktElem := DSS.ActiveCircuit.CktElements.Next;
End;
-
-End;
-
+end;
procedure TExecHelper.DoSetReduceStrategy(Const S:String);
-
Function AtLeast(i,j:Integer):Integer;
Begin If j NIL) Do
Begin
- CktElem.Checked := False;
+ Exclude(CktElem.Flags, Flg.Checked);
CktElem := CktElements.Next;
End;
End;
@@ -2937,20 +2687,19 @@ function TExecHelper.DoInterpolateCmd:Integer;
MetObj := MeterClass.GetActiveObj;
MetObj.InterpolateCoordinates;
End
- Else DoSimpleMsg(DSS, 'EnergyMeter "'+Param+'" not found.', 277);
+ Else DoSimpleMsg(DSS, 'EnergyMeter "%s" not found.', [Param], 277);
End;
End;
-
-End;
+end;
function TExecHelper.DoAlignFileCmd:Integer;
{Rewrites designated file, aligning the fields into columns}
Var
- {ParamName,} Param :String;
+ Param :String;
Begin
Result := 0;
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
@@ -2960,13 +2709,12 @@ function TExecHelper.DoAlignFileCmd:Integer;
End
Else
Begin
- DoSimpleMsg(DSS, 'File "'+Param+'" does not exist.', 278);
+ DoSimpleMsg(DSS, 'File "%s" does not exist.', [Param], 278);
Result := 1;
End;
If Result=0 Then FireOffEditor(DSS, DSS.GlobalResult);
-
-End; {DoAlignfileCmd}
+end; {DoAlignfileCmd}
procedure TExecHelper.DoSetNormal(pctNormal:Double);
@@ -2984,20 +2732,15 @@ procedure TExecHelper.DoSetNormal(pctNormal:Double);
End;
function TExecHelper.DoRotateCmd:Integer;
-
-{rotate about the center of the coordinates}
-
+// rotate about the center of the coordinates
Var
i:Integer;
Angle, xmin,xmax, ymin, ymax, xc, yc:Double;
- // ParamName:String;
a, vector: Complex;
-
Begin
Result := 0;
- If DSS.ActiveCircuit <> NIl then Begin
-
- {ParamName :=} DSS.Parser.NextParam;
+ If DSS.ActiveCircuit <> NIl then begin
+ DSS.Parser.NextParam;
Angle := DSS.Parser.DblValue * PI/180.0; // Deg to rad
a := cmplx(cos(Angle), Sin(Angle));
@@ -3024,7 +2767,7 @@ function TExecHelper.DoRotateCmd:Integer;
If Buses^[i].CoordDefined Then Begin
With Buses^[i] Do Begin
vector := cmplx(x-xc,y-yc);
- Vector := Cmul(Vector, a);
+ Vector := Vector * a;
x := xc+vector.re;
y := yc+vector.im;
End;
@@ -3032,8 +2775,7 @@ function TExecHelper.DoRotateCmd:Integer;
End;
End;
end;
-
-End;
+end;
function TExecHelper.DoVDiffCmd:Integer;
@@ -3041,32 +2783,35 @@ function TExecHelper.DoVDiffCmd:Integer;
Fin: TBufferedFileStream = nil;
Fout: TFileStream = nil;
sout: String;
- BusName, Line:String;
- i, node, busIndex:Integer;
- Vmag, Diff:Double;
-
+ BusName, Line:String;
+ i, node, busIndex:Integer;
+ Vmag, Diff:Double;
Begin
Result := 0;
- If FileExists(DSS.OutputDirectory {CurrentDSSDir} + DSS.CircuitName_ + 'SavedVoltages.Txt') Then Begin
- Try
- Try
- Fin := TBufferedFileStream.Create(DSS.OutputDirectory {CurrentDSSDir} + DSS.CircuitName_ + 'SavedVoltages.Txt', fmOpenRead);
- Fout := TFileStream.Create(DSS.OutputDirectory {CurrentDSSDir} + DSS.CircuitName_ + 'VDIFF.txt', fmCreate);
-
- While (Fin.Position + 1) < Fin.Size Do
- Begin
+ If FileExists(DSS.OutputDirectory {CurrentDSSDir} + DSS.CircuitName_ + 'SavedVoltages.txt') Then Begin
+ try
+ try
+ Fin := TBufferedFileStream.Create(DSS.OutputDirectory {CurrentDSSDir} + DSS.CircuitName_ + 'SavedVoltages.txt', fmOpenRead or fmShareDenyWrite);
+ Fout := TBufferedFileStream.Create(DSS.OutputDirectory {CurrentDSSDir} + DSS.CircuitName_ + 'VDIFF.txt', fmCreate);
+
+ while (Fin.Position + 1) < Fin.Size do
+ begin
FSReadln(Fin, Line);
DSS.AuxParser.CmdString := Line;
DSS.AuxParser.NextParam;
BusName := DSS.AuxParser.StrValue;
- If Length(BusName) > 0 Then Begin
+ If Length(BusName) > 0 Then
+ Begin
BusIndex := DSS.ActiveCircuit.BusList.Find(BusName);
- If BusIndex>0 Then Begin
+ If BusIndex>0 Then
+ Begin
DSS.AuxParser.Nextparam;
node := DSS.AuxParser.Intvalue;
With DSS.ActiveCircuit.Buses^[BusIndex] Do
- For i := 1 to NumNodesThisBus Do Begin
- If GetNum(i)=node then Begin
+ For i := 1 to NumNodesThisBus Do
+ Begin
+ If GetNum(i)=node then
+ Begin
DSS.AuxParser.Nextparam;
Vmag := DSS.AuxParser.Dblvalue;
Diff := Cabs(DSS.ActiveCircuit.Solution.NodeV^[GetRef(i)]) - Vmag;
@@ -3082,34 +2827,26 @@ function TExecHelper.DoVDiffCmd:Integer;
end;
End;
End;
-
End;
End;
End;
-
Except
On E:Exception Do Begin
- DoSimpleMsg(DSS, 'Error opening Saved Voltages or VDIFF File: '+E.message, 280);
+ DoSimpleMsg(DSS, 'Error opening Saved Voltages or VDIFF File: %s', [E.message], 280);
Exit;
End;
-
End;
-
Finally
-
FreeAndNil(Fin);
FreeAndNil(Fout);
-
FireOffEditor(DSS, DSS.OutputDirectory {CurrentDSSDir} + DSS.CircuitName_ + 'VDIFF.txt');
-
End;
-
End
- Else DoSimpleMsg(DSS, 'Error: No Saved Voltages.', 281);
-
-End;
+ Else
+ DoSimpleMsg(DSS, _('Error: No Saved Voltages.'), 281);
+end;
function TExecHelper.DoSummaryCmd:Integer;
@@ -3127,36 +2864,35 @@ function TExecHelper.DoSummaryCmd:Integer;
Else Begin
S := S + 'Status = NOT Solved' + CRLF;
End;
- S := S + 'Solution Mode = ' + GetSolutionModeID(DSS) + CRLF;
+ S := S + 'Solution Mode = ' + DSS.SolveModeEnum.OrdinalToString(ord(DSS.ActiveCircuit.Solution.mode)) + CRLF;
S := S + 'Number = ' + IntToStr(DSS.ActiveCircuit.Solution.NumberofTimes) + CRLF;
S := S + 'Load Mult = '+ Format('%5.3f', [DSS.ActiveCircuit.LoadMultiplier]) + CRLF;
S := S + 'Devices = '+ Format('%d', [DSS.ActiveCircuit.NumDevices]) + CRLF;
S := S + 'Buses = ' + Format('%d', [DSS.ActiveCircuit.NumBuses]) + CRLF;
S := S + 'Nodes = ' + Format('%d', [DSS.ActiveCircuit.NumNodes]) + CRLF;
- S := S + 'Control Mode =' + GetControlModeID(DSS) + CRLF;
+ S := S + 'Control Mode =' + DSS.ControlModeEnum.OrdinalToString(DSS.ActiveCircuit.Solution.Controlmode) + CRLF;
S := S + 'Total Iterations = '+IntToStr(DSS.ActiveCircuit.Solution.Iteration) + CRLF;
S := S + 'Control Iterations = '+IntToStr(DSS.ActiveCircuit.Solution.ControlIteration) + CRLF;
S := S + 'Max Sol Iter = ' +IntToStr(DSS.ActiveCircuit.Solution.MostIterationsDone ) + CRLF;
S := S + ' ' + CRLF;
S := S + ' - Circuit Summary -' + CRLF;
S := S + ' ' + CRLF;
- If DSS.ActiveCircuit <> Nil Then Begin
-
+ If DSS.ActiveCircuit <> Nil Then begin
S := S + Format('Year = %d ',[DSS.ActiveCircuit.Solution.Year]) + CRLF;
S := S + Format('Hour = %d ',[DSS.ActiveCircuit.Solution.DynaVars.intHour]) + CRLF;
S := S + 'Max pu. voltage = '+Format('%-.5g ',[GetMaxPUVoltage(DSS)]) + CRLF;
S := S + 'Min pu. voltage = '+Format('%-.5g ',[GetMinPUVoltage(DSS, TRUE)]) + CRLF;
- cPower := CmulReal(GetTotalPowerFromSources(DSS), 0.000001); // MVA
+ cPower := GetTotalPowerFromSources(DSS) * 0.000001; // MVA
S := S + Format('Total Active Power: %-.6g MW',[cpower.re]) + CRLF;
S := S + Format('Total Reactive Power: %-.6g Mvar',[cpower.im]) + CRLF;
- cLosses := CmulReal(DSS.ActiveCircuit.Losses, 0.000001);
+ cLosses := DSS.ActiveCircuit.Losses * 0.000001;
If cPower.re <> 0.0 Then S := S + Format('Total Active Losses: %-.6g MW, (%-.4g %%)',[cLosses.re,(Closses.re/cPower.re*100.0)]) + CRLF
Else S := S + 'Total Active Losses: ****** MW, (**** %%)' + CRLF;
S := S + Format('Total Reactive Losses: %-.6g Mvar',[cLosses.im]) + CRLF;
S := S + Format('Frequency = %-g Hz',[DSS.ActiveCircuit.Solution.Frequency]) + CRLF;
- S := S + 'Mode = '+GetSolutionModeID(DSS) + CRLF;
- S := S + 'Control Mode = '+GetControlModeID(DSS) + CRLF;
- S := S + 'Load Model = '+GetLoadModel(DSS) + CRLF;
+ S := S + 'Mode = '+DSS.SolveModeEnum.OrdinalToString(ord(DSS.ActiveCircuit.Solution.mode)) + CRLF;
+ S := S + 'Control Mode = ' + DSS.ControlModeEnum.OrdinalToString(DSS.ActiveCircuit.Solution.Controlmode) + CRLF;
+ S := S + 'Load Model = '+ DSS.DefaultLoadModelEnum.OrdinalToString(DSS.ActiveCircuit.Solution.LoadModel) + CRLF;
End;
DSS.GlobalResult := S;
@@ -3201,7 +2937,7 @@ function TExecHelper.DoDistributeCmd:Integer;
4: PF := DSS.Parser.DblValue;
5: FilName := DSS.Parser.StrValue;
6: kW := DSS.Parser.DblValue * 1000.0;
- 7: if (Uppercase(Param)[1]='L') then DoGenerators := FALSE Else DoGenerators := TRUE; // Load or Generator
+ 7: if (AnsiUpperCase(Param)[1]='L') then DoGenerators := FALSE Else DoGenerators := TRUE; // Load or Generator
ELSE
// ignore unnamed and extra parms
@@ -3218,7 +2954,6 @@ function TExecHelper.DoDistributeCmd:Integer;
End;
function TExecHelper.DoDI_PlotCmd:Integer;
-{$IF not (defined(DLL_ENGINE) or defined(FPC))}
Var
ParamName, Param:String;
ParamPointer, i:Integer;
@@ -3229,14 +2964,10 @@ function TExecHelper.DoDI_PlotCmd:Integer;
iRegisters:Array of Integer;
NumRegs:Integer;
PeakDay:Boolean;
-{$ENDIF}
Begin
-{$IF not (defined(DLL_ENGINE) or defined(FPC))}
- IF DIFilesAreOpen Then EnergyMeterClass.CloseAllDIFiles;
+ IF DSS.DIFilesAreOpen Then DSS.EnergyMeterClass.CloseAllDIFiles;
- If Not Assigned(DSSPlotObj) Then DSSPlotObj := TDSSPlot.Create;
-
- {Defaults}
+ // Defaults
NumRegs:=1;
SetLength(IRegisters, NumRegs);
iRegisters[0] := 9;
@@ -3257,7 +2988,7 @@ function TExecHelper.DoDI_PlotCmd:Integer;
1: CaseName := Param;
2: CaseYear := DSS.Parser.Intvalue;
3: Begin
- NumRegs := DSS.Parser.ParseAsVector(NumEMREgisters, @dRegisters);
+ NumRegs := DSS.Parser.ParseAsVector(NumEMREgisters, pDoubleArray(@dRegisters));
SetLength(iRegisters, NumRegs);
For i := 1 to NumRegs Do iRegisters[i-1] := Round(dRegisters[i]);
End;
@@ -3272,35 +3003,28 @@ function TExecHelper.DoDI_PlotCmd:Integer;
Param := DSS.Parser.StrValue;
End;
- DSSPlotObj.DoDI_Plot(CaseName, CaseYear, iRegisters, PeakDay, MeterName);
+ //TODO!!! DSSPlotObj.DoDI_Plot(CaseName, CaseYear, iRegisters, PeakDay, MeterName);
iRegisters := Nil;
-{$ENDIF}
Result := 0;
-
-End;
+end;
function TExecHelper.DoCompareCasesCmd:Integer;
-{$IF not (defined(DLL_ENGINE) or defined(FPC))}
Var
- ParamName, Param:String;
- ParamPointer:Integer;
- UnKnown:Boolean;
- Reg:Integer;
- CaseName1,
- CaseName2, WhichFile:String;
-{$ENDIF}
+ ParamName, Param: String;
+ ParamPointer: Integer;
+ UnKnown: Boolean;
+ Reg: Integer;
+ CaseName1, CaseName2, WhichFile: String;
Begin
-{$IF not (defined(DLL_ENGINE) or defined(FPC))}
- IF DIFilesAreOpen Then EnergyMeterClass.CloseAllDIFiles;
- If Not Assigned(DSSPlotObj) Then DSSPlotObj := TDSSPlot.Create;
+ IF DSS.DIFilesAreOpen Then DSS.EnergyMeterClass.CloseAllDIFiles;
CaseName1 := 'base';
CaseName2 := '';
Reg := 9; // Overload EEN
WhichFile := 'Totals';
ParamPointer := 0;
- ParamName := UpperCase(DSS.Parser.NextParam);
+ ParamName := AnsiUpperCase(DSS.Parser.NextParam);
Param := DSS.Parser.StrValue;
WHILE Length(Param)>0 DO
Begin
@@ -3326,33 +3050,26 @@ function TExecHelper.DoCompareCasesCmd:Integer;
// ignore unnamed and extra parms
End;
- ParamName := UpperCase(DSS.Parser.NextParam);
+ ParamName := AnsiUpperCase(DSS.Parser.NextParam);
Param := DSS.Parser.StrValue;
End;
- DSSPlotObj.DoCompareCases(CaseName1, CaseName2, WhichFile, Reg);
-{$ENDIF}
+ //TODO!!! DSSPlotObj.DoCompareCases(CaseName1, CaseName2, WhichFile, Reg);
Result := 0;
-
-End;
+end;
function TExecHelper.DoYearlyCurvesCmd:Integer;
-{$IF not (defined(DLL_ENGINE) or defined(FPC))}
Var
- ParamName, Param:String;
- ParamPointer, i:Integer;
- UnKnown:Boolean;
- CaseNames:TStringList;
- dRegisters:Array[1..NumEMRegisters] of Double;
- iRegisters:Array of Integer;
- Nregs:Integer;
- WhichFile:String;
-{$ENDIF}
+ ParamName, Param: String;
+ ParamPointer, i: Integer;
+ UnKnown: Boolean;
+ CaseNames: TStringList;
+ dRegisters: Array[1..NumEMRegisters] of Double;
+ iRegisters: Array of Integer;
+ Nregs: Integer;
+ WhichFile: String;
Begin
-{$IF not (defined(DLL_ENGINE) or defined(FPC))}
- IF DIFilesAreOpen Then EnergyMeterClass.CloseAllDIFiles;
-
- If Not Assigned(DSSPlotObj) Then DSSPlotObj := TDSSPlot.Create;
+ IF DSS.DIFilesAreOpen Then DSS.EnergyMeterClass.CloseAllDIFiles;
Nregs := 1;
SetLength(iRegisters, Nregs);
@@ -3360,7 +3077,6 @@ function TExecHelper.DoYearlyCurvesCmd:Integer;
CaseNames.Clear;
WhichFile := 'Totals';
-
ParamPointer := 0;
ParamName := DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
@@ -3369,7 +3085,7 @@ function TExecHelper.DoYearlyCurvesCmd:Integer;
Unknown := False;
IF (Length(ParamName) = 0) THEN Inc(ParamPointer)
- ELSE Case Uppercase(ParamName)[1] of
+ ELSE Case AnsiUpperCase(ParamName)[1] of
'C':ParamPointer := 1;
'R':ParamPointer := 2;
'M':ParamPointer := 3; {meter=}
@@ -3390,7 +3106,7 @@ function TExecHelper.DoYearlyCurvesCmd:Integer;
End;
End;
2: Begin
- NRegs := DSS.Parser.ParseAsVector(NumEMRegisters, @dRegisters);
+ NRegs := DSS.Parser.ParseAsVector(NumEMRegisters, pDoubleArray(@dRegisters));
SetLength(iRegisters, Nregs);
For i := 1 to NRegs Do iRegisters[i-1] := Round(dRegisters[i]);
end;
@@ -3403,88 +3119,103 @@ function TExecHelper.DoYearlyCurvesCmd:Integer;
Param := DSS.Parser.StrValue;
End;
- DSSPlotObj.DoYearlyCurvePlot(CaseNames, WhichFile, iRegisters);
+ //TODO!!! DSSPlotObj.DoYearlyCurvePlot(CaseNames, WhichFile, iRegisters);
iRegisters := Nil;
CaseNames.Free;
-{$ENDIF}
Result := 0;
End;
function TExecHelper.DoVisualizeCmd:Integer;
-{$IF not defined(FPC)}
Var
- DevIndex :integer;
- Param :String;
- ParamName :String;
- ParamPointer:Integer;
- Unknown :Boolean;
- Quantity :Integer;
- ElemName :String;
- pElem :TDSSObject;
-{$ENDIF}
+ DevIndex: integer;
+ Param: String;
+ ParamName: String;
+ ParamPointer: Integer;
+ Unknown: Boolean;
+ Quantity: String;
+ ElemName: String;
+ plotParamsStr: String;
+ pElem: TDSSObject;
+ plotParams: TJSONObject = NIL;
Begin
- Result := 0;
-{$IF not defined(FPC)}
+ Result := 0;
// Abort if no circuit or solution
- If not assigned(DSS.ActiveCircuit) Then
- Begin
- DoSimpleMsg(DSS, 'No circuit created.',24721);
- Exit;
- End;
- If not assigned(DSS.ActiveCircuit.Solution) OR not assigned(DSS.ActiveCircuit.Solution.NodeV) Then
- Begin
- DoSimpleMsg(DSS, 'The circuit must be solved before you can do this.',24722);
- Exit;
- End;
-
- Quantity := vizCURRENT;
- ElemName := '';
- {Parse rest of command line}
- ParamPointer := 0;
- ParamName := UpperCase(DSS.Parser.NextParam);
- Param := DSS.Parser.StrValue;
- WHILE Length(Param)>0 DO
- Begin
- Unknown := False;
- IF (Length(ParamName) = 0) THEN Inc(ParamPointer)
-
- ELSE Begin
- If CompareTextShortest(ParamName, 'WHAT')=0 then ParamPointer:=1
- ELSE If CompareTextShortest(ParamName, 'ELEMENT')=0 then ParamPointer:=2
- ELSE Unknown := TRUE;
- End;
-
- If Not Unknown then
- CASE ParamPointer OF
- 1: Case Lowercase(Param)[1] of
- 'c': Quantity := vizCURRENT;
- 'v': Quantity := vizVOLTAGE;
- 'p': Quantity := vizPOWER;
- End;
- 2: ElemName := Param;
- ELSE
- // ignore unnamed and extra parms
- End;
-
- ParamName := UpperCase(DSS.Parser.NextParam);
- Param := DSS.Parser.StrValue;
- End; {WHILE}
-
- {--------------------------------------------------------------}
-
- Devindex := GetCktElementIndex(ElemName); // Global function
- IF DevIndex > 0 THEN Begin // element must already exist
- pElem := DSS.ActiveCircuit.CktElements.Get(DevIndex);
- If pElem is TDSSCktElement Then Begin
- DSSPlotObj.DoVisualizationPlot(TDSSCktElement(pElem), Quantity);
- End Else Begin
- DoSimpleMsg(DSS, pElem.Name + ' must be a circuit element type!', 282); // Wrong type
+ If not assigned(DSS.ActiveCircuit) Then
+ Begin
+ DoSimpleMsg(DSS, _('No circuit created.'), 24721);
+ Exit;
+ End;
+ If not assigned(DSS.ActiveCircuit.Solution) OR not assigned(DSS.ActiveCircuit.Solution.NodeV) Then
+ Begin
+ DoSimpleMsg(DSS, _('The circuit must be solved before you can do this.'), 24722);
+ Exit;
+ End;
+ Quantity := 'Current';
+ ElemName := '';
+ // Parse rest of command line
+ ParamPointer := 0;
+ ParamName := AnsiUpperCase(DSS.Parser.NextParam);
+ Param := DSS.Parser.StrValue;
+ WHILE Length(Param)>0 DO
+ Begin
+ Unknown := False;
+ if (Length(ParamName) = 0) then
+ Inc(ParamPointer)
+ ELSE
+ begin
+ If CompareTextShortest(ParamName, 'WHAT')=0 then ParamPointer:=1
+ ELSE If CompareTextShortest(ParamName, 'ELEMENT')=0 then ParamPointer:=2
+ ELSE
+ Unknown := TRUE;
End;
- End Else Begin
- DoSimpleMsg(DSS, 'Requested Circuit Element: "' + ElemName + '" Not Found.',282 ); // Did not find it ..
- End;
-{$ENDIF}
+ If Not Unknown then
+ CASE ParamPointer OF
+ 1: Case AnsiLowerCase(Param)[1] of
+ 'c': Quantity := 'Current';
+ 'v': Quantity := 'Voltage';
+ 'p': Quantity := 'Power';
+ End;
+ 2: ElemName := Param;
+ ELSE
+ // ignore unnamed and extra parms
+ End;
+ ParamName := AnsiUpperCase(DSS.Parser.NextParam);
+ Param := DSS.Parser.StrValue;
+ End;
+ // --------------------------------------------------------------
+ Devindex := GetCktElementIndex(DSS, ElemName); // Global function
+ if DevIndex > 0 then
+ begin // element must already exist
+ pElem := DSS.ActiveCircuit.CktElements.Get(DevIndex);
+ if not (pElem is TDSSCktElement) then
+ begin
+ DoSimpleMsg(DSS, '"%s" must be a circuit element type!', [pElem.Name], 282); // Wrong type
+ Exit;
+ end;
+ end
+ else
+ begin
+ DoSimpleMsg(DSS, 'Requested Circuit Element: "%s" not found.', [ElemName], 282); // Did not find it ..
+ Exit;
+ end;
+
+ try
+ // pElem.ComputeIterminal();
+ // pElem.ComputeVTerminal();
+ plotParams := TJSONObject.Create([
+ 'PlotType', 'Visualize',
+ 'ElementName', pElem.Name,
+ 'ElementType', pElem.DSSClassName,
+ 'Quantity', Quantity
+ ]);
+ // plotParams.CompressedJSON := True;
+ plotParamsStr := plotParams.FormatJSON();
+ if (@DSS.DSSPlotCallback) <> NIL then
+ DSS.DSSPlotCallback(DSS, PChar(plotParamsStr));
+ finally
+ FreeAndNil(plotParams);
+ end;
End;
function TExecHelper.DoCloseDICmd:Integer;
@@ -3508,14 +3239,13 @@ function TExecHelper.DoEstimateCmd:Integer;
Begin
Result := 0;
- {Load current Estimation is driven by Energy Meters at head of feeders.}
+ // Load current Estimation is driven by Energy Meters at head of feeders.
DoAllocateLoadsCmd;
- {Let's look to see how well we did}
+ // Let's look to see how well we did
If not DSS.AutoShowExport Then DSS.DSSExecutive.Command := 'Set showexport=yes';
DSS.DSSExecutive.Command := 'Export Estimation';
-
-End;
+end;
@@ -3561,7 +3291,7 @@ function TExecHelper.DoReconductorCmd:Integer;
5: MyEditString := Param;
6: Nphases := DSS.Parser.IntValue;
Else
- DoSimpleMsg(DSS, 'Error: Unknown Parameter on command line: '+Param, 28701);
+ DoSimpleMsg(DSS, 'Error: Unknown Parameter on command line: %s', [Param], 28701);
End;
ParamName := DSS.Parser.NextParam;
@@ -3575,12 +3305,12 @@ function TExecHelper.DoReconductorCmd:Integer;
Line2 := StripClassName(Line2);
If (Length(Line1)=0) or (Length(Line2)=0) then Begin
- DoSimpleMsg(DSS, 'Both Line1 and Line2 must be specified!', 28702);
+ DoSimpleMsg(DSS, _('Both Line1 and Line2 must be specified!'), 28702);
Exit;
End;
If (Not LineCodeSpecified) and (Not GeometrySpecified) then Begin
- DoSimpleMsg(DSS, 'Either a new LineCode or a Geometry must be specified!', 28703);
+ DoSimpleMsg(DSS, _('Either a new LineCode or a Geometry must be specified!'), 28703);
Exit;
End;
@@ -3589,20 +3319,20 @@ function TExecHelper.DoReconductorCmd:Integer;
pLine2 := LineCLass.Find(Line2);
If (pLine1 = Nil) or (pLine2=NIL) then Begin
- If pLine1=Nil then DoSimpleMsg(DSS, 'Line.'+Line1+' not found.', 28704)
- Else If pLine2=Nil then DoSimpleMsg(DSS, 'Line.'+Line2+' not found.', 28704);
+ If pLine1=Nil then DoSimpleMsg(DSS, 'Line.%s not found.', [Line1], 28704)
+ Else If pLine2=Nil then DoSimpleMsg(DSS, 'Line.%s not found.', [Line2], 28704);
Exit;
End;
{Now check to make sure they are in the same meter's zone}
If (pLine1.MeterObj=Nil) or (pLine2.MeterObj=Nil) then Begin
- DoSimpleMsg(DSS, 'Error: Both Lines must be in the same EnergyMeter zone. One or both are not in any meter zone.', 28705);
+ DoSimpleMsg(DSS, _('Error: Both Lines must be in the same EnergyMeter zone. One or both are not in any meter zone.'), 28705);
Exit;
End;
If pLine1.MeterObj<>pline2.MeterObj then Begin
- DoSimpleMsg(DSS, 'Error: Line1 is in EnergyMeter.'+pLine1.MeterObj.Name+
- ' zone while Line2 is in EnergyMeter.'+pLine2.MeterObj.Name+ ' zone. Both must be in the same Zone.', 28706);
+ DoSimpleMsg(DSS, 'Error: Line1 is in %s zone while Line2 is in %s zone. Both must be in the same Zone.',
+ [pLine1.MeterObj.FullName, pLine2.MeterObj.FullName], 28706);
Exit;
End;
@@ -3621,11 +3351,10 @@ function TExecHelper.DoReconductorCmd:Integer;
1: TraceAndEdit(DSS, pLine1, pLine2, NPhases, Editstring);
2: TraceAndEdit(DSS, pLine2, pLine1, NPhases, Editstring);
Else
- DoSimpleMsg(DSS, 'Traceback path not found between Line1 and Line2.', 28707);
+ DoSimpleMsg(DSS, _('Traceback path not found between Line1 and Line2.'), 28707);
Exit;
end;
-
-End;
+end;
function TExecHelper.DoAddMarkerCmd:Integer;
Var
@@ -3663,8 +3392,7 @@ function TExecHelper.DoAddMarkerCmd:Integer;
ParamName := DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
End;
-
-End;
+end;
function TExecHelper.DoSetLoadAndGenKVCmd:Integer;
VAR
@@ -3678,7 +3406,6 @@ function TExecHelper.DoSetLoadAndGenKVCmd:Integer;
Result := 0;
pLoad := DSS.ActiveCircuit.Loads.First;
WHILE pLoad <> NIL Do Begin
- DSS.ActiveLoadObj := pLoad; // for UpdateVoltageBases to work
sBus := StripExtension (pLoad.GetBus(1));
iBus := DSS.ActiveCircuit.BusList.Find (sBus);
pBus := DSS.ActiveCircuit.Buses^[iBus];
@@ -3687,7 +3414,8 @@ function TExecHelper.DoSetLoadAndGenKVCmd:Integer;
pLoad.kVLoadBase := kvln * sqrt (3.0)
else
pLoad.kVLoadBase := kvln;
- pLoad.UpdateVoltageBases;
+
+ pLoad.PropertySideEffects(ord(TLoadProp.kV));
pLoad.RecalcElementData;
pLoad := DSS.ActiveCircuit.Loads.Next;
End;
@@ -3704,24 +3432,24 @@ function TExecHelper.DoSetLoadAndGenKVCmd:Integer;
pGen.PresentKV := kvln;
pGen.RecalcElementData;
End;
-
-End;
+end;
function TExecHelper.DoUuidsCmd:Integer;
Var
- F: TBufferedFileStream = nil;
- {ParamName,} Param, S, NameVal, UuidVal, DevClass, DevName: String;
+ F: TStream = nil;
+ Param, S, NameVal, UuidVal, DevClass, DevName: String;
pName: TNamedObject;
idx: integer;
Begin
DSS.CIMExporter.StartUuidList(DSS.ActiveCircuit.NumBuses + 2 * DSS.ActiveCircuit.NumDevices);
Result := 0;
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
Try
- F := TBufferedFileStream.Create(AdjustInputFilePath(DSS, Param), fmOpenRead);
+ F := DSS.GetROFileStream(Param);
DSS.AuxParser.Delimiters := ',';
- While (F.Position + 1) < F.Size Do Begin
+ While (F.Position + 1) < F.Size Do
+ Begin
FSReadln(F, S);
With DSS.AuxParser Do Begin
pName := nil;
@@ -3779,13 +3507,13 @@ function TExecHelper.DoCvrtLoadshapesCmd:Integer;
Fname :String;
Begin
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
If length(param)=0 then Param := 's';
{Double file or Single file?}
- CASE lowercase(param)[1] of
+ CASE AnsiLowerCase(param)[1] of
'd': Action := 'action=dblsave';
ELSE
Action := 'action=sngsave'; // default
@@ -3793,15 +3521,15 @@ function TExecHelper.DoCvrtLoadshapesCmd:Integer;
LoadShapeClass := GetDSSClassPtr(DSS, 'loadshape') as TLoadShape;
- Fname := DSS.OutputDirectory {CurrentDSSDir} + 'ReloadLoadshapes.DSS';
- F := TFileStream.Create(Fname, fmCreate);
+ Fname := DSS.OutputDirectory {CurrentDSSDir} + 'ReloadLoadshapes.dss';
+ F := TBufferedFileStream.Create(Fname, fmCreate);
iLoadshape := LoadShapeClass.First;
while iLoadshape > 0 do Begin
pLoadShape := LoadShapeClass.GetActiveObj;
DSS.Parser.CmdString := Action;
- pLoadShape.Edit;
- FSWriteln(F, Format('New Loadshape.%s Npts=%d Interval=%.8g %s',[pLoadShape.Name, pLoadShape.NumPoints, pLoadShape.Interval, DSS.GlobalResult]));
+ pLoadShape.Edit(DSS.Parser);
+ FSWriteln(F, Format('New %s Npts=%d Interval=%.8g %s', [pLoadShape.FullName, pLoadShape.NumPoints, pLoadShape.Interval, DSS.GlobalResult]));
iLoadshape := LoadShapeClass.Next;
End;
@@ -3826,8 +3554,7 @@ function TExecHelper.DoNodeDiffCmd:Integer;
NodeBuffer :Array[1..50] of Integer;
-Begin
-
+begin
Result := 0;
ParamName := DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
@@ -3840,37 +3567,34 @@ function TExecHelper.DoNodeDiffCmd:Integer;
If Pos('1',ParamName)>0 then sNode1 := Param;
// Get first node voltage
- DSS.AuxParser.Token := sNode1;
NodeBuffer[1] := 1;
- sBusName := DSS.AuxParser.ParseAsBusName (numNodes, pIntegerArray(@NodeBuffer));
+ sBusName := DSS.AuxParser.ParseAsBusName(sNode1, numNodes, pIntegerArray(@NodeBuffer));
iBusidx := DSS.ActiveCircuit.Buslist.Find(sBusName);
If iBusidx>0 Then Begin
B1Ref := DSS.ActiveCircuit.Buses^[iBusidx].Find(NodeBuffer[1])
End Else Begin
- DoSimpleMsg(DSS, Format('Bus %s not found.',[sBusName]), 28709);
+ DoSimpleMsg(DSS, 'Bus %s not found.', [sBusName], 28709);
Exit;
End;
V1 := DSS.ActiveCircuit.Solution.NodeV^[B1Ref];
// Get 2nd node voltage
- DSS.AuxParser.Token := sNode2;
NodeBuffer[1] := 1;
- sBusName := DSS.AuxParser.ParseAsBusName (numNodes, pIntegerArray(@NodeBuffer));
+ sBusName := DSS.AuxParser.ParseAsBusName(sNode2, numNodes, pIntegerArray(@NodeBuffer));
iBusidx := DSS.ActiveCircuit.Buslist.Find(sBusName);
If iBusidx>0 Then Begin
B2Ref := DSS.ActiveCircuit.Buses^[iBusidx].Find(NodeBuffer[1])
End Else Begin
- DoSimpleMsg(DSS, Format('Bus %s not found.',[sBusName]), 28710);
+ DoSimpleMsg(DSS, 'Bus %s not found.', [sBusName], 28710);
Exit;
End;
V2 := DSS.ActiveCircuit.Solution.NodeV^[B2Ref];
- VNodeDiff := CSub(V1, V2);
+ VNodeDiff := V1 - V2;
DSS.GlobalResult := Format('%.7g, V, %.7g, deg ',[Cabs(VNodeDiff), CDang(VNodeDiff) ]);
-
-End;
+end;
function TExecHelper.DoRephaseCmd:Integer;
Var
@@ -3889,7 +3613,7 @@ function TExecHelper.DoRephaseCmd:Integer;
Result := 0;
ParamPointer := 0;
MyEditString := '';
- ScriptfileName := 'RephaseEditScript.DSS';
+ ScriptfileName := 'RephaseEditScript.dss';
TransfStop := TRUE; // Stop at Transformers
ParamName := DSS.Parser.NextParam;
@@ -3905,7 +3629,7 @@ function TExecHelper.DoRephaseCmd:Integer;
4: ScriptFileName := Param;
5: TransfStop := InterpretYesNo(Param);
Else
- DoSimpleMsg(DSS, 'Error: Unknown Parameter on command line: '+Param, 28711);
+ DoSimpleMsg(DSS, 'Error: Unknown Parameter on command line: %s', [Param], 28711);
End;
ParamName := DSS.Parser.NextParam;
@@ -3915,23 +3639,22 @@ function TExecHelper.DoRephaseCmd:Integer;
LineClass := DSS.DSSClassList.Get(DSS.ClassNames.Find('Line'));
pStartLine := LineClass.Find(StripClassName(StartLine));
If pStartLine=Nil then Begin
- DoSimpleMsg(DSS, 'Starting Line ('+StartLine+') not found.', 28712);
+ DoSimpleMsg(DSS, 'Starting Line (%s) not found.', [StartLine], 28712);
Exit;
End;
{Check for some error conditions and abort if necessary}
If pStartLine.MeterObj=Nil then Begin
- DoSimpleMsg(DSS, 'Starting Line must be in an EnergyMeter zone.', 28713);
+ DoSimpleMsg(DSS, _('Starting Line must be in an EnergyMeter zone.'), 28713);
Exit;
End;
If not (pStartLine.MeterObj is TEnergyMeterObj) then Begin
- DoSimpleMsg(DSS, 'Starting Line must be in an EnergyMeter zone.', 28714);
+ DoSimpleMsg(DSS, _('Starting Line must be in an EnergyMeter zone.'), 28714);
Exit;
End;
GoForwardandRephase(DSS, pStartLine, NewPhases, MyEditString, ScriptfileName, TransfStop);
-
-End;
+end;
function TExecHelper.DoSetBusXYCmd:Integer;
@@ -3944,8 +3667,7 @@ function TExecHelper.DoSetBusXYCmd:Integer;
Yval :Double;
iB :Integer;
-Begin
-
+begin
Result := 0;
ParamName := DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
@@ -3960,7 +3682,7 @@ function TExecHelper.DoSetBusXYCmd:Integer;
2: Xval := DSS.Parser.DblValue;
3: Yval := DSS.Parser.DblValue;
Else
- DoSimpleMsg(DSS, 'Error: Unknown Parameter on command line: '+Param, 28721);
+ DoSimpleMsg(DSS, 'Error: Unknown Parameter on command line: %s', [Param], 28721);
End;
iB := DSS.ActiveCircuit.Buslist.Find(BusName);
@@ -3971,15 +3693,13 @@ function TExecHelper.DoSetBusXYCmd:Integer;
CoordDefined := TRUE;
End;
End Else Begin
- DoSimpleMsg(DSS, 'Error: Bus "' + BusName + '" Not Found.', 28722);
+ DoSimpleMsg(DSS, 'Error: Bus "%s" not found.', [BusName], 28722);
End;
ParamName := DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
End;
-
-
-End;
+end;
function TExecHelper.DoUpdateStorageCmd:Integer;
@@ -4008,8 +3728,7 @@ function TExecHelper.DoPstCalc;
S :String;
Freq :Double;
-Begin
-
+begin
Result := 0;
Varray := nil;
PstArray := nil;
@@ -4035,7 +3754,7 @@ function TExecHelper.DoPstCalc;
4: Freq := DSS.Parser.DblValue;
5: Lamp := DSS.Parser.IntValue;
Else
- DoSimpleMsg(DSS, 'Error: Unknown Parameter on command line: '+Param, 28722);
+ DoSimpleMsg(DSS, 'Error: Unknown Parameter on command line: %s', [Param], 28722);
End;
ParamName := DSS.Parser.NextParam;
@@ -4043,15 +3762,14 @@ function TExecHelper.DoPstCalc;
End;
If Npts>10 Then
- Begin
-
+ begin
nPst := PstRMS(PstArray, Varray, Freq, CyclesPerSample, Npts, Lamp);
// put resulting pst array in the result string
S := '';
For i := 1 to nPst Do S := S + Format('%.8g, ', [PstArray^[i]]);
DSS.GlobalResult := S;
End
- Else DoSimpleMsg(DSS, 'Insuffient number of points for Pst Calculation.', 28723);
+ Else DoSimpleMsg(DSS, _('Insuffient number of points for Pst Calculation.'), 28723);
Reallocmem(Varray, 0); // discard temp arrays
@@ -4059,8 +3777,7 @@ function TExecHelper.DoPstCalc;
End;
function TExecHelper.DoLambdaCalcs:Integer;
-{Execute fault rate and bus number of interruptions calc}
-
+// Execute fault rate and bus number of interruptions calc
Var pMeter : TEnergyMeterObj;
i : Integer;
//ParamName,
@@ -4073,11 +3790,11 @@ function TExecHelper.DoLambdaCalcs:Integer;
// Do for each Energymeter object in active circuit
pMeter := DSS.ActiveCircuit.EnergyMeters.First;
If pMeter=nil Then Begin
- DoSimpleMsg(DSS, 'No EnergyMeter Objects Defined. EnergyMeter objects required for this function.',28724);
+ DoSimpleMsg(DSS, _('No EnergyMeter Objects Defined. EnergyMeter objects required for this function.'), 28724);
Exit;
End;
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Param := DSS.Parser.StrValue ;
If Length(Param)>0 Then
@@ -4107,8 +3824,7 @@ function TExecHelper.DoVarCmd:Integer;
Param:String;
Str : String;
iVar : Integer;
-Begin
-
+begin
Result := 0;
ParamName := DSS.Parser.NextParam;
@@ -4116,9 +3832,6 @@ function TExecHelper.DoVarCmd:Integer;
If Length(Param)=0 Then // show all vars
Begin
-{$IFNDEF DSS_CAPI}
- If NoFormsAllowed Then Exit;
-{$ENDIF}
{
MsgStrings := TStringList.Create;
MsgStrings.Add('Variable, Value');
@@ -4126,15 +3839,11 @@ function TExecHelper.DoVarCmd:Integer;
MsgStrings.Add(DSS.ParserVars.VarString[iVar] );
ShowMessageForm(MsgStrings);
MsgStrings.Free;}
- Str := 'Variable, Value' + CRLF;
+ Str := _('Variable, Value') + CRLF;
for iVar := 1 to DSS.ParserVars.NumVariables do
Str := Str + DSS.ParserVars.VarString[iVar]+CRLF;
-{$IFNDEF DSS_CAPI}
- DoSimpleMsg(DSS, Str, 999345);
-{$ELSE}
DSS.GlobalResult := Str;
-{$ENDIF}
End Else if Length(ParamName)=0 then // show value of this var
Begin
DSS.GlobalResult := Param; // DSS.Parser substitutes @var with value
@@ -4144,7 +3853,7 @@ function TExecHelper.DoVarCmd:Integer;
case ParamName[1] of
'@': DSS.ParserVars.Add(ParamName, Param);
else
- DoSimpleMsg(DSS, 'Illegal Variable Name: ' + ParamName + '; Must begin with "@"', 28725);
+ DoSimpleMsg(DSS, 'Illegal Variable Name: %s; Must begin with "@"', [ParamName], 28725);
Exit;
end;
ParamName := DSS.Parser.NextParam;
@@ -4152,9 +3861,7 @@ function TExecHelper.DoVarCmd:Integer;
End;
End;
-
-
-End;
+end;
function TExecHelper.DoRemoveCmd:Integer;
var
@@ -4167,6 +3874,7 @@ function TExecHelper.DoRemoveCmd:Integer;
FKeepLoad: Boolean;
FEditString: String;
+ elem: TDSSObject;
pPDElem: TPDelement;
pMeter: TEnergyMeterObj;
FMeterName: String;
@@ -4174,7 +3882,7 @@ function TExecHelper.DoRemoveCmd:Integer;
Result := 0;
if DSS.ActiveCircuit = nil then
begin
- DoSimpleMsg(DSS, 'Error: There is no active circuit!', 28998);
+ DoSimpleMsg(DSS, _('Error: There is no active circuit!'), 28998);
Exit;
end;
@@ -4213,14 +3921,15 @@ function TExecHelper.DoRemoveCmd:Integer;
);
Exit;
end;
-
+
// first, checks if the element is not linked to an energy meter, if it does, abort (added 01/06/2021 -DM)
+ elem := DSS.ActiveCircuit.CktElements.Get(DeviceIndex);
with DSS.ActiveCircuit do
begin
pMeter := EnergyMeters.First;
while pMeter <> NIL do
begin
- if AnsiLowerCase(pMeter.ElementName) = AnsiLowerCase(FElementName) then
+ if pMeter.MeteredElement = elem then
begin
DoSimpleMsg(DSS,
Format('Error: Element %s is tied to an Energy Meter.', [FelementName]),
@@ -4237,7 +3946,7 @@ function TExecHelper.DoRemoveCmd:Integer;
if not (DSS.ActiveCircuit.ActiveCktElement is TPDElement) then
begin
DoSimpleMsg(DSS,
- Format('Error: Element %s is not a power delivery element (PDElement)', [FelementName]),
+ Format('Error: Element "%s" is not a power delivery element (PDElement)', [FelementName]),
28728
);
Exit;
@@ -4247,20 +3956,20 @@ function TExecHelper.DoRemoveCmd:Integer;
pPDElem := DSS.ActiveCircuit.ActiveCktElement as TPDElement;
if pPDElem.SensorObj = NIL then
begin
- DoSimpleMsg(DSS, Format(
- 'Element %s.%s is not in a meter zone! Add an Energymeter. ',
- [pPDelem.Parentclass.Name, pPDelem.name]
- ), 287261);
+ DoSimpleMsg(DSS,
+ 'Element "%s" is not in a meter zone! Add an Energymeter. ',
+ [pPDelem.FullName],
+ 287261);
Exit;
end;
- FMeterName := Format('%s.%s', [pPDElem.SensorObj.ParentClass.Name, pPDElem.SensorObj.Name]);
+ FMeterName := pPDElem.SensorObj.FullName;
SetObject(DSS, FMeterName);
if not (DSS.ActiveCircuit.ActiveCktElement is TEnergyMeterObj) then
begin
DoSimpleMsg(DSS,
- Format('Error: The Sensor Object for %s is not an EnergyMeter object', [FelementName]),
+ 'Error: The Sensor Object for "%s" is not an EnergyMeter object', [FelementName],
28727
);
Exit;
@@ -4272,32 +3981,17 @@ function TExecHelper.DoRemoveCmd:Integer;
end;
initialization
+ // Initialize Command lists
-{Initialize Command lists}
-
- SaveCommands := TCommandList.Create(['class', 'file', 'dir', 'keepdisabled']);
- SaveCommands.Abbrev := True;
+ SaveCommands := TCommandList.Create(['class', 'file', 'dir', 'keepdisabled'], True);
DI_PlotCommands := TCommandList.Create(['case','year','registers','peak','meter']);
- DistributeCommands := TCommandList.Create(['kW','how','skip','pf','file','MW','what']);
- DistributeCommands.Abbrev := True;
-
- ReconductorCommands := TCommandList.Create(['Line1', 'Line2', 'LineCode', 'Geometry', 'EditString', 'Nphases']);
- ReconductorCommands.Abbrev := True;
-
- RephaseCommands := TCommandList.Create(['StartLine', 'PhaseDesignation', 'EditString', 'ScriptFileName', 'StopAtTransformers']);
- RephaseCommands.Abbrev := True;
-
- AddMarkerCommands := TCommandList.Create(['Bus', 'code', 'color', 'size']);
- AddMarkerCommands.Abbrev := True;
-
- SetBusXYCommands := TCommandList.Create(['Bus', 'x', 'y']);
- SetBusXYCommands.Abbrev := True;
-
- PstCalcCommands := TCommandList.Create(['Npts', 'Voltages', 'dt', 'Frequency', 'lamp']);
- PstCalcCommands.abbrev := True;
-
- RemoveCommands := TCommandList.Create(['ElementName', 'KeepLoad', 'Editstring']);
- RemoveCommands.abbrev := True;
+ DistributeCommands := TCommandList.Create(['kW','how','skip','pf','file','MW','what'], True);
+ ReconductorCommands := TCommandList.Create(['Line1', 'Line2', 'LineCode', 'Geometry', 'EditString', 'Nphases'], True);
+ RephaseCommands := TCommandList.Create(['StartLine', 'PhaseDesignation', 'EditString', 'ScriptFileName', 'StopAtTransformers'], True);
+ AddMarkerCommands := TCommandList.Create(['Bus', 'code', 'color', 'size'], True);
+ SetBusXYCommands := TCommandList.Create(['Bus', 'x', 'y'], True);
+ PstCalcCommands := TCommandList.Create(['Npts', 'Voltages', 'dt', 'Frequency', 'lamp'], True);
+ RemoveCommands := TCommandList.Create(['ElementName', 'KeepLoad', 'Editstring'], True);
finalization
@@ -4311,6 +4005,4 @@ finalization
PstCalcCommands.Free;
RemoveCommands.Free;
-end.
-
-
+end.
\ No newline at end of file
diff --git a/src/Executive/ExecOptions.pas b/src/Executive/ExecOptions.pas
index 1e81719d5..1698db560 100644
--- a/src/Executive/ExecOptions.pas
+++ b/src/Executive/ExecOptions.pas
@@ -12,23 +12,156 @@ interface
uses
Command, DSSClass;
-const
-{$IFNDEF DSS_CAPI_PM}
- NumExecOptions = 115;
-{$ELSE}
- NumExecOptions = 127;
+type
+{$SCOPEDENUMS ON}
+ TExecOption = (
+ INVALID = 0,
+ typ = 1,
+ element = 2,
+ hour = 3,
+ sec = 4,
+ year = 5,
+ frequency = 6,
+ stepsize = 7,
+ mode = 8,
+ random = 9,
+ number = 10,
+ time = 11,
+ cls = 12,
+ obj = 13,
+ circuit = 14,
+ editor = 15,
+ tolerance = 16,
+ maxiterations = 17,
+ h = 18,
+ Loadmodel = 19,
+ Loadmult = 20,
+ normvminpu = 21,
+ normvmaxpu = 22,
+ emergvminpu = 23,
+ emergvmaxpu = 24,
+ pctmean = 25, // %mean
+ pctstddev = 26, // %stddev
+ LDCurve = 27, // Load Duration Curve
+ pctgrowth = 28, // %growth -- default growth rate
+ Genkw = 29,
+ Genpf = 30,
+ CapkVAR = 31,
+ Addtype = 32,
+ Allowduplicates = 33,
+ Zonelock = 34,
+ UEweight = 35,
+ Lossweight = 36,
+ UEregs = 37,
+ Lossregs = 38,
+ Voltagebases = 39, // changes the default voltage base rules
+ Algorithm = 40, // changes the default voltage base rules
+ Trapezoidal = 41,
+ Autobuslist = 42, // array of bus names to include in auto add solutions
+ Controlmode = 43,
+ Tracecontrol = 44,
+ Genmult = 45,
+ Defaultdaily = 46,
+ Defaultyearly = 47,
+ Allocationfactors = 48,
+ Cktmodel = 49,
+ Pricesignal = 50,
+ Pricecurve = 51,
+ Terminal = 52,
+ Basefrequency = 53,
+ Harmonics = 54,
+ Maxcontroliter = 55,
+ Bus = 56,
+ Datapath = 57,
+ KeepList = 58,
+ ReduceOption = 59,
+ DemandInterval = 60,
+ pctNormal = 61, // %Normal
+ DIVerbose = 62,
+ Casename = 63,
+ Markercode = 64,
+ Nodewidth = 65,
+ Log = 66,
+ Recorder = 67,
+ Overloadreport = 68,
+ Voltexceptionreport = 69,
+ Cfactors = 70,
+ Showexport = 71,
+ Numallociterations = 72,
+ DefaultBaseFrequency = 73,
+ Markswitches = 74,
+ Switchmarkercode = 75,
+ Daisysize = 76,
+ Marktransformers = 77,
+ TransMarkerCode = 78,
+ TransMarkerSize = 79,
+ LoadShapeClass = 80,
+ EarthModel = 81,
+ QueryLog = 82,
+ MarkCapacitors = 83,
+ MarkRegulators = 84,
+ MarkPVSystems = 85,
+ MarkStorage = 86,
+ CapMarkerCode = 87,
+ RegMarkerCode = 88,
+ PVMarkerCode = 89,
+ StoreMarkerCode = 90,
+ CapMarkerSize = 91,
+ RegMarkerSize = 92,
+ PVMarkerSize = 93,
+ StoreMarkerSize = 94,
+ NeglectLoadY = 95,
+ MarkFuses = 96,
+ FuseMarkerCode = 97,
+ FuseMarkerSize = 98,
+ MarkReclosers = 99,
+ RecloserMarkerCode = 100,
+ RecloserMarkerSize = 101,
+ RegistryUpdate = 102,
+ MarkRelays = 103,
+ RelayMarkerCode = 104,
+ RelayMarkerSize = 105,
+ ProcessTime = 106,
+ TotalTime = 107,
+ StepTime = 108,
+ SampleEnergyMeters = 109,
+ MinIterations = 110, // default is 2
+ DSSVisualizationTool = 111,
+ KeepLoad = 112,
+ Zmag = 113,
+ SeasonRating = 114,
+ SeasonSignal = 115
+{$IFDEF DSS_CAPI_PM}
+ ,
+ NumCPUs = 116,
+ NumCores = 117,
+ NumActors = 118,
+ ActiveActor = 119,
+ CPU = 120,
+ ActorProgress = 121,
+ Parallel = 122,
+ ConcatenateReports = 123,
+ NUMANodes
{$ENDIF}
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ ,
+ Coverage,
+ Num_SubCircuits,
+ ADiakoptics,
+ LinkBranches
+{$ENDIF}
+ );
+{$SCOPEDENUMS OFF}
+const
+ NumExecOptions = ord(High(TExecOption));
var
- ExecOption,
- OptionHelp: array[1..NumExecOptions] of String;
- OptionList: TCommandList;
-
-function DoGetCmd(DSS: TDSSContext): Integer;
-function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
-function DoSetCmd_NoCircuit(DSS: TDSSContext): Boolean; // Set Commands that do not require a circuit
-function DoGetCmd_NoCircuit(DSS: TDSSContext): Boolean; // Get Commands that do not require a circuit
+ ExecOption: array[1..NumExecOptions] of String;
+function DoGetCmd({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext): Integer;
+function DoSetCmd({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext; SolveOption: Integer): Integer;
+function DoSetCmd_NoCircuit({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext): Boolean; // Set Commands that do not require a circuit
+function DoGetCmd_NoCircuit({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext): Boolean; // Get Commands that do not require a circuit
implementation
@@ -44,460 +177,50 @@ implementation
Sysutils,
Solution,
Energymeter,
- DSSHelper
-{$IFDEF DSS_CAPI_PM}
+ Dynamics,
+ DSSHelper,
+ StrUtils,
+ TypInfo
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
, Diakoptics
{$ENDIF}
;
-{$IFDEF DSS_CAPI_PM}
-const
- OPTION_NumCPUs = 116;
- OPTION_NumCores = 117;
- OPTION_NumActors = 118;
- OPTION_ActiveActor = 119;
- OPTION_CPU = 120;
- OPTION_ActorProgress = 121;
- OPTION_Parallel = 122;
- OPTION_ConcatenateReports = 123;
- OPTION_Coverage = 124;
- OPTION_Num_SubCircuits = 125;
- OPTION_ADiakoptics = 126;
- OPTION_LinkBranches = 127;
-{$ENDIF}
+type
+ Opt = TExecOption;
procedure DefineOptions;
-
+var
+ info: Pointer;
+ i: Integer;
+ name: String;
begin
+ info := TypeInfo(TExecOption);
+ for i := 1 to NumExecOptions do
+ begin
+ name := ReplaceStr(GetEnumName(info, i), 'pct', '%');
+ if name = 'cls' then
+ name := 'class'
+ else if name = 'typ' then
+ name := name + 'e'
+ else if name = 'obj' then
+ name := 'object';
- ExecOption[1] := 'type';
- ExecOption[2] := 'element';
- ExecOption[3] := 'hour';
- ExecOption[4] := 'sec';
- ExecOption[5] := 'year';
- ExecOption[6] := 'frequency';
- ExecOption[7] := 'stepsize';
- ExecOption[8] := 'mode';
- ExecOption[9] := 'random';
- ExecOption[10] := 'number';
- ExecOption[11] := 'time';
- ExecOption[12] := 'class';
- ExecOption[13] := 'object';
- ExecOption[14] := 'circuit';
- ExecOption[15] := 'editor';
- ExecOption[16] := 'tolerance';
- ExecOption[17] := 'maxiterations';
- ExecOption[18] := 'h';
- ExecOption[19] := 'Loadmodel';
- ExecOption[20] := 'Loadmult';
- ExecOption[21] := 'normvminpu';
- ExecOption[22] := 'normvmaxpu';
- ExecOption[23] := 'emergvminpu';
- ExecOption[24] := 'emergvmaxpu';
- ExecOption[25] := '%mean';
- ExecOption[26] := '%stddev';
- ExecOption[27] := 'LDCurve'; // Load Duration Curve
- ExecOption[28] := '%growth'; // default growth rate
- ExecOption[29] := 'Genkw';
- ExecOption[30] := 'Genpf';
- ExecOption[31] := 'CapkVAR';
- ExecOption[32] := 'Addtype';
- ExecOption[33] := 'Allowduplicates';
- ExecOption[34] := 'Zonelock';
- ExecOption[35] := 'UEweight';
- ExecOption[36] := 'Lossweight';
- ExecOption[37] := 'UEregs';
- ExecOption[38] := 'Lossregs';
- ExecOption[39] := 'Voltagebases'; // changes the default voltage base rules
- ExecOption[40] := 'Algorithm'; // changes the default voltage base rules
- ExecOption[41] := 'Trapezoidal';
- ExecOption[42] := 'Autobuslist'; // array of bus names to include in auto add solutions
- ExecOption[43] := 'Controlmode';
- ExecOption[44] := 'Tracecontrol';
- ExecOption[45] := 'Genmult';
- ExecOption[46] := 'Defaultdaily';
- ExecOption[47] := 'Defaultyearly';
- ExecOption[48] := 'Allocationfactors';
- ExecOption[49] := 'Cktmodel';
- ExecOption[50] := 'Pricesignal';
- ExecOption[51] := 'Pricecurve';
- ExecOption[52] := 'Terminal';
- ExecOption[53] := 'Basefrequency';
- ExecOption[54] := 'Harmonics';
- ExecOption[55] := 'Maxcontroliter';
- ExecOption[56] := 'Bus';
- ExecOption[57] := 'Datapath';
- ExecOption[58] := 'KeepList';
- ExecOption[59] := 'ReduceOption';
- ExecOption[60] := 'DemandInterval';
- ExecOption[61] := '%Normal';
- ExecOption[62] := 'DIVerbose';
- ExecOption[63] := 'Casename';
- ExecOption[64] := 'Markercode';
- ExecOption[65] := 'Nodewidth';
- ExecOption[66] := 'Log';
- ExecOption[67] := 'Recorder';
- ExecOption[68] := 'Overloadreport';
- ExecOption[69] := 'Voltexceptionreport';
- ExecOption[70] := 'Cfactors';
- ExecOption[71] := 'Showexport';
- ExecOption[72] := 'Numallociterations';
- ExecOption[73] := 'DefaultBaseFrequency';
- ExecOption[74] := 'Markswitches';
- ExecOption[75] := 'Switchmarkercode';
- ExecOption[76] := 'Daisysize';
- ExecOption[77] := 'Marktransformers';
- ExecOption[78] := 'TransMarkerCode';
- ExecOption[79] := 'TransMarkerSize';
- ExecOption[80] := 'LoadShapeClass';
- ExecOption[81] := 'EarthModel';
- ExecOption[82] := 'QueryLog';
- ExecOption[83] := 'MarkCapacitors';
- ExecOption[84] := 'MarkRegulators';
- ExecOption[85] := 'MarkPVSystems';
- ExecOption[86] := 'MarkStorage';
- ExecOption[87] := 'CapMarkerCode';
- ExecOption[88] := 'RegMarkerCode';
- ExecOption[89] := 'PVMarkerCode';
- ExecOption[90] := 'StoreMarkerCode';
- ExecOption[91] := 'CapMarkerSize';
- ExecOption[92] := 'RegMarkerSize';
- ExecOption[93] := 'PVMarkerSize';
- ExecOption[94] := 'StoreMarkerSize';
- ExecOption[95] := 'NeglectLoadY';
- ExecOption[96] := 'MarkFuses';
- ExecOption[97] := 'FuseMarkerCode';
- ExecOption[98] := 'FuseMarkerSize';
- ExecOption[99] := 'MarkReclosers';
- ExecOption[100] := 'RecloserMarkerCode';
- ExecOption[101] := 'RecloserMarkerSize';
- ExecOption[102] := 'RegistryUpdate';
- ExecOption[103] := 'MarkRelays';
- ExecOption[104] := 'RelayMarkerCode';
- ExecOption[105] := 'RelayMarkerSize';
- ExecOption[106] := 'ProcessTime';
- ExecOption[107] := 'TotalTime';
- ExecOption[108] := 'StepTime';
- ExecOption[109] := 'SampleEnergyMeters';
- ExecOption[110] := 'MinIterations'; // default is 2
- ExecOption[111] := 'DSSVisualizationTool';
- ExecOption[112] := 'KeepLoad';
- ExecOption[113] := 'Zmag';
- ExecOption[114] := 'SeasonRating';
- ExecOption[115] := 'SeasonSignal';
-
-{$IFDEF DSS_CAPI_PM}
- ExecOption[OPTION_NumCPUs] := 'NumCPUs';
- ExecOption[OPTION_NumCores] := 'NumCores';
- ExecOption[OPTION_NumActors] := 'NumActors';
- ExecOption[OPTION_ActiveActor] := 'ActiveActor';
- ExecOption[OPTION_CPU] := 'CPU';
- ExecOption[OPTION_ActorProgress] := 'ActorProgress';
- ExecOption[OPTION_Parallel] := 'Parallel';
- ExecOption[OPTION_ConcatenateReports] := 'ConcatenateReports';
- ExecOption[OPTION_Coverage] := 'Coverage';
- ExecOption[OPTION_Num_SubCircuits] := 'Num_SubCircuits';
- ExecOption[OPTION_ADiakoptics] := 'ADiakoptics';
- ExecOption[OPTION_LinkBranches] := 'LinkBranches';
-{$ENDIF}
-
- OptionHelp[1] := 'Sets the active DSS class type. Same as Class=...';
- OptionHelp[2] := 'Sets the active DSS element by name. You can use ' +
- 'the complete object spec (class.name) or just the ' +
- 'name. if full name is specifed, class becomes the active ' +
- 'class, also.';
- OptionHelp[3] := 'Sets the hour used for the start time of the solution.';
- OptionHelp[4] := 'Sets the seconds from the hour for the start time of the solution.';
- OptionHelp[5] := 'Sets the Year (integer number) to be used for the solution. ' +
- 'for certain solution types, this determines the growth multiplier.';
- OptionHelp[6] := 'Sets the frequency for the solution of the active circuit.';
- OptionHelp[7] := 'Sets the time step size for the active circuit. Default units are s. ' +
- 'May also be specified in minutes or hours by appending "m" or "h" to the value. For example:' + CRLF + CRLF +
- ' stepsize=.25h ' + CRLF + ' stepsize=15m' + CRLF + ' stepsize=900s';
- OptionHelp[8] := 'Set the solution Mode: One of' +
- CRLF + ' Snapshot,' +
- CRLF + ' Daily,' +
- CRLF + ' Yearly (follow Yearly curve),' +
- CRLF + ' DIrect,' +
- CRLF + ' DUtycycle,' +
- CRLF + ' Time, ( see LoadShapeClass, SampleEnergymeters options)' +
- CRLF + ' DYnamic, ( see LoadShapeClass option)' +
- CRLF + ' Harmonic,' +
- CRLF + ' HarmonicT, (sequential Harmonic Mode)' +
- CRLF + ' M1 (Monte Carlo 1),' +
- CRLF + ' M2 (Monte Carlo 2),' +
- CRLF + ' M3 (Monte Carlo 3),' +
- CRLF + ' Faultstudy,' +
- CRLF + ' MF (monte carlo fault study)' +
- CRLF + ' Peakday,' +
- CRLF + ' LD1 (load-duration 1)' +
- CRLF + ' LD2 (load-duration 2)' +
- CRLF + ' AutoAdd (see AddType)' + CRLF + CRLF +
- 'Side effect: setting the Mode propergy resets all monitors and energy meters. It also ' +
- 'resets the time step, etc. to defaults for each mode. After the initial reset, the user ' +
- 'must explicitly reset the monitors and/or meters until another Set Mode= command.';
- OptionHelp[9] := 'One of [Uniform | Gaussian | Lognormal | None ] for Monte Carlo Variables.';
- OptionHelp[10] := 'Number of solutions or time steps to perform for each Solve command. Defaults for selected modes: ' + CRLF + CRLF +
- 'Daily = 24' + CRLF + 'Yearly = 8760' + CRLF + 'Duty = 100';
- OptionHelp[11] := 'Specify the solution start time as an array:' + CRLF +
- 'time=(hour, secs)';
- OptionHelp[12] := 'Synonym for Type=. (See above)';
- OptionHelp[13] := 'Synonym for Element=. (See above)';
- OptionHelp[14] := 'Set the active circuit by name.';
- OptionHelp[15] := 'Set the command string required to start up the editor preferred by the user. Does not require a circuit defined.';
- OptionHelp[16] := 'Sets the solution tolerance. Default is 0.0001.';
- OptionHelp[17] := 'Sets the maximum allowable iterations for power flow solutions. Default is 15.';
- OptionHelp[18] := 'Alternate name for time step size.';
- OptionHelp[19] := '{Powerflow | Admittance} depending on the type of solution you wish to perform. ' +
- 'If admittance, a non-iterative, direct solution is done with all loads and generators modeled by their ' +
- 'equivalent admittance.';
- OptionHelp[20] := 'Global load multiplier for this circuit. Does not affect loads ' +
- 'designated to be "fixed". All other base kW values are multiplied by this number. ' +
- 'Defaults to 1.0 when the circuit is created. As with other values, it always stays ' +
- 'at the last value to which it was set until changed again.';
- OptionHelp[21] := 'Minimum permissible per unit voltage for normal conditions. Default is 0.95.';
- OptionHelp[22] := 'Maximum permissible per unit voltage for normal conditions. Default is 1.05.';
- OptionHelp[23] := 'Minimum permissible per unit voltage for emergency (contingency) conditions. Default is 0.90.';
- OptionHelp[24] := 'Maximum permissible per unit voltage for emergency (contingency) conditions. Default is 1.08.';
- OptionHelp[25] := 'Percent mean to use for global load multiplier. Default is 65%.';
- OptionHelp[26] := 'Percent Standard deviation to use for global load multiplier. Default is 9%.';
- OptionHelp[27] := 'Set Load-Duration Curve. Global load multiplier is defined by this curve for LD1 and LD2 solution modes. Default is Nil.';
- OptionHelp[28] := 'Set default annual growth rate, percent, for loads with no growth curve specified. Default is 2.5.';
- OptionHelp[29] := 'Size of generator, kW, to automatically add to system. Default is 1000.0';
- OptionHelp[30] := 'Power factor of generator to assume for automatic addition. Default is 1.0.';
- OptionHelp[31] := 'Size of capacitor, kVAR, to automatically add to system. Default is 600.0.';
- OptionHelp[32] := '{Generator | Capacitor} Default is Generator. Type of device for AutoAdd Mode.';
- OptionHelp[33] := '{YES/TRUE | NO/FALSE} Default is No. Flag to indicate if it is OK to have devices of same name in the same class. ' +
- 'If No, then a New command is treated as an Edit command. ' +
- 'If Yes, then a New command will always result in a device being added.';
- OptionHelp[34] := '{YES/TRUE | NO/FALSE} Default is No. if No, then meter zones are recomputed each time there is a change in the circuit. ' +
- 'If Yes, then meter zones are not recomputed unless they have not yet been computed. ' +
- 'Meter zones are normally recomputed on Solve command following a circuit change.';
- OptionHelp[35] := 'Weighting factor for UE/EEN in AutoAdd functions. Defaults to 1.0.' + CRLF + CRLF +
- 'Autoadd mode minimizes' + CRLF + CRLF +
- '(Lossweight * Losses + UEweight * UE). ' + CRLF + CRLF +
- 'If you wish to ignore UE, set to 0. ' +
- 'This applies only when there are EnergyMeter objects. ' +
- 'Otherwise, AutoAdd mode minimizes total system losses.';
- OptionHelp[36] := 'Weighting factor for Losses in AutoAdd functions. Defaults to 1.0.' + CRLF + CRLF +
- 'Autoadd mode minimizes' + CRLF + CRLF +
- '(Lossweight * Losses + UEweight * UE). ' + CRLF + CRLF +
- 'If you wish to ignore Losses, set to 0. ' +
- 'This applies only when there are EnergyMeter objects. ' +
- 'Otherwise, AutoAdd mode minimizes total system losses.';
- OptionHelp[37] := 'Which EnergyMeter register(s) to use for UE in AutoAdd Mode. ' +
- 'May be one or more registers. if more than one, register values are summed together. ' +
- 'Array of integer values > 0. Defaults to 11 (for Load EEN). ' + CRLF + CRLF +
- 'for a list of EnergyMeter register numbers, do the "Show Meters" command after defining a circuit.';
- OptionHelp[38] := 'Which EnergyMeter register(s) to use for Losses in AutoAdd Mode. ' +
- 'May be one or more registers. if more than one, register values are summed together. ' +
- 'Array of integer values > 0. Defaults to 13 (for Zone kWh Losses). ' + CRLF + CRLF +
- 'for a list of EnergyMeter register numbers, do the "Show Meters" command after defining a circuit.';
- OptionHelp[39] := 'Define legal bus voltage bases for this circuit. Enter an array ' +
- 'of the legal voltage bases, in phase-to-phase voltages, for example:' + CRLF + CRLF +
- 'set voltagebases=".208, .480, 12.47, 24.9, 34.5, 115.0, 230.0" ' + CRLF + CRLF +
- 'When the CalcVoltageBases command is issued, a snapshot solution is performed ' +
- 'with no load injections and the bus base voltage is set to the nearest legal voltage base. ' +
- 'The defaults are as shown in the example above.';
- OptionHelp[40] := '{Normal | Newton} Solution algorithm type. Normal is a fixed point iteration ' +
- 'that is a little quicker than the Newton iteration. Normal is adequate for most radial ' +
- 'distribution circuits. Newton is more robust for circuits that are difficult to solve.'
-{$IFDEF DSS_CAPI_PM}
- + 'Diakoptics is used for accelerating the simulation using multicore computers'
-{$ENDIF};
- OptionHelp[41] := '{YES/TRUE | NO/FALSE} Default is "No/False". Specifies whether to use trapezoidal integration for accumulating energy meter registers. ' +
- 'Applies to EnergyMeter and Generator objects. Default method simply multiplies the ' +
- 'present value of the registers times the width of the interval (Euler). ' +
- 'Trapezoidal is more accurate when there are sharp changes in a load shape or unequal intervals. ' +
- 'Trapezoidal is automatically used for ' +
- 'some load-duration curve simulations where the interval size varies considerably. ' +
- 'Keep in mind that for Trapezoidal, you have to solve one more point than the number of intervals. ' +
- 'That is, to do a Daily simulation on a 24-hr load shape, you would set Number=25 to force a solution ' +
- 'at the first point again to establish the last (24th) interval.' + CRLF + CRLF +
- 'Note: Set Mode= resets Trapezoidal to No/False. Set this to Yes/True AFTER setting the Mode option.';
- OptionHelp[42] := 'Array of bus names to include in AutoAdd searches. Or, you can specify a text file holding the names, one to a line, ' +
- 'by using the syntax (file=filename) instead of the actual array elements. ' +
- 'Default is null, which results in the program ' +
- 'using either the buses in the EnergyMeter object zones or, if no EnergyMeters, all the buses, which can ' +
- 'make for lengthy solution times. ' + Crlf + Crlf +
- 'Examples:' + Crlf + CRlf +
- 'Set autobuslist=(bus1, bus2, bus3, ... )' + CRLF +
- 'Set autobuslist=(file=buslist.txt)';
- OptionHelp[43] := '{OFF | STATIC |EVENT | TIME | MULTIRATE} Default is "STATIC". Control mode for the solution. ' +
- 'Set to OFF to prevent controls from changing.' + CRLF +
- 'STATIC = Time does not advance. Control actions are executed in order of shortest time to act ' +
- 'until all actions are cleared from the control queue. Use this mode for power flow solutions which may require several ' +
- 'regulator tap changes per solution.' + CRLF + CRLF +
- 'EVENT = solution is event driven. Only the control actions nearest in time ' +
- 'are executed and the time is advanced automatically to the time of the event. ' + crlf + crlf +
- 'TIME = solution is time driven. Control actions are executed when the time for the pending ' +
- 'action is reached or surpassed.' + CRLF + CRLF +
- 'MULTIRATE = solution is time driven. Control actions are executed when the time for the pending ' +
- 'action is reached or surpassed. In this control mode a solution is performed after each control action' +
- 'is performed to reduce the error accumulated when the time step is to long' + CRLF + CRLF +
- 'Controls may reset and may choose not to act when it comes their time. ' + CRLF +
- 'Use TIME mode when modeling a control externally to the DSS and a solution mode such as ' +
- 'DAILY or DUTYCYCLE that advances time, or set the time (hour and sec) explicitly from the external program. ';
- OptionHelp[44] := '{YES/TRUE | NO/FALSE} Set to YES to trace the actions taken in the control queue. ' +
- 'Creates a file named TRACE_CONTROLQUEUE.CSV in the default directory. ' +
- 'The names of all circuit elements taking an action are logged.';
- OptionHelp[45] := 'Global multiplier for the kW output of every generator in the circuit. Default is 1.0. ' +
- 'Applies to all but Autoadd solution modes. ' +
- 'Ignored for generators designated as Status=Fixed.';
- OptionHelp[46] := 'Default daily load shape name. Default value is "default", which is a 24-hour curve defined when the DSS is started.';
- OptionHelp[47] := 'Default yearly load shape name. Default value is "default", which is a 24-hour curve defined when the DSS is started.';
- OptionHelp[48] := 'Sets the connected kVA allocation factors for all loads in the active circuit to the value given.';
- OptionHelp[49] := '{Multiphase | Positive} Default = Multiphase. Designates whether circuit model is to interpreted as a normal multi-phase ' +
- 'model or a positive-sequence only model';
- OptionHelp[50] := 'Sets the present price signal ($/MWh) for the circuit. Default value is 25.';
- OptionHelp[51] := 'Sets the PRICESHAPE object to use to obtain for price signal. Default is none (null string). If none, ' +
- 'price signal either remains constant or is set by an external process using Set Price= option. ' +
- 'Curve is defined as a PRICESHAPE in actual values (not normalized) and should be defined to correspond to ' +
- 'the type of analysis being performed (daily, yearly, etc.).';
- OptionHelp[52] := 'Set the active terminal of the active circuit element. May also be done with Select command.';
- OptionHelp[53] := 'Default = 60. Set the fundamental frequency for harmonic solution and the default base frequency for all impedance quantities. ' +
- 'Side effect: also changes the value of the solution frequency. Saved as default for next circuit.';
- OptionHelp[54] := '{ALL | (list of harmonics) } Default = ALL. Array of harmonics for which to perform a solution in Harmonics mode. ' +
- 'If ALL, then solution is performed for all harmonics defined in spectra currently being used. ' +
- 'Otherwise, specify a more limited list such as: ' + CRLF + CRLF +
- ' Set Harmonics=(1 5 7 11 13)';
- OptionHelp[55] := 'Max control iterations per solution. Default is 10.';
- OptionHelp[56] := 'Set Active Bus by name. Can also be done with Select and SetkVBase commands and the "Set Terminal=" option. ' +
- 'The bus connected to the active terminal becomes the active bus. See Zsc and Zsc012 commands.';
- OptionHelp[57] := 'Set the data path for files written or read by the DSS.' + CRLF +
- 'Defaults to the user documents folder.' + CRLF +
- 'If the DataPath is not writable, output files will be written to the user application data folder.' + CRLF +
- 'May be Null. On DSS-Extensions, with AllowChangeDir enabled, executes a CHDIR to this path if non-null.' + CRLF +
- 'Does not require a circuit defined.';
- OptionHelp[58] := 'Array of bus names to keep when performing circuit reductions. You can specify a text file holding the names, one to a line, ' +
- 'by using the syntax (file=filename) instead of the actual array elements. ' +
- 'Command is cumulative (reset keeplist first). ' +
- 'Reduction algorithm may keep other buses automatically. ' + Crlf + Crlf +
- 'Examples:' + Crlf + CRlf +
- 'Reset Keeplist (sets all buses to FALSE (no keep))' + CRLF +
- 'Set KeepList=(bus1, bus2, bus3, ... )' + CRLF +
- 'Set KeepList=(file=buslist.txt)';
- OptionHelp[59] := '{ Default or [null] | Shortlines [Zmag=nnn] | MergeParallel | BreakLoops | Switches | Ends | Laterals} Strategy for reducing feeders. ' +
- 'Default is to eliminate all dangling end buses and buses without load, caps, or taps. ' + CRLF +
- '"Shortlines [Zmag=0.02]" merges short branches with impedance less than Zmag (default = 0.02 ohms) ' + CRLF +
- '"MergeParallel" merges lines that have been found to be in parallel ' + CRLF +
- '"Breakloops" disables one of the lines at the head of a loop. ' + CRLF +
- '"Ends" eliminates dangling ends only.' + CRLF +
- '"Switches" merges switches with downline lines and eliminates dangling switches.' + CRLF +
- '"Laterals [Keepload=Yes*/No]" uses the Remove command to eliminate 1-phase laterals and optionally lump the load back to the 2- or 3-phase feeder (default behavior). ' + CRLF + CRLF +
- 'Marking buses with "Keeplist" will prevent their elimination.';
- OptionHelp[60] := '{YES/TRUE | NO/FALSE} Default = no. Set for keeping demand interval data for daily, yearly, etc, simulations. ' +
- 'Side Effect: Resets all meters!!!';
- OptionHelp[61] := 'Sets the Normal rating of all lines to a specified percent of the emergency rating. Note: This action takes place immediately. ' +
- 'Only the in-memory value is changed for the duration of the run.';
- OptionHelp[62] := '{YES/TRUE | NO/FALSE} Default = FALSE. Set to Yes/True if you wish a separate demand interval (DI) file written ' +
- 'for each meter. Otherwise, only the totalizing meters are written.';
- OptionHelp[63] := 'Name of case for yearly simulations with demand interval data. ' +
- 'Becomes the name of the subdirectory under which all the year data are stored. ' +
- 'Default = circuit name ' + CRLF + CRLF +
- 'Side Effect: Sets the prefix for output files';
- OptionHelp[64] := 'Number code for node marker on circuit plots. Number from 0 to 47. Default is 16 (open circle). 24 is solid circle. Try other values for other symbols. See also Nodewidth';
- OptionHelp[65] := 'Width of node marker. Default=1. See MarkerCode';
- OptionHelp[66] := '{YES/TRUE | NO/FALSE} Default = FALSE. Significant solution events are added to the Event Log, primarily for debugging.';
- OptionHelp[67] := '{YES/TRUE | NO/FALSE} Default = FALSE. Opens DSSRecorder.DSS in DSS install folder and enables recording of all commands that come through ' +
- 'the text command interface. Closed by either setting to NO/FALSE or exiting the program. ' +
- 'When closed by this command, the file name can be found in the Result. Does not require a circuit defined.';
- OptionHelp[68] := '{YES/TRUE | NO/FALSE} Default = FALSE. For yearly solution mode, sets overload reporting on/off. DemandInterval must be set to true for this to have effect.';
- OptionHelp[69] := '{YES/TRUE | NO/FALSE} Default = FALSE. For yearly solution mode, sets voltage exception reporting on/off. DemandInterval must be set to true for this to have effect.';
- OptionHelp[70] := 'Sets the CFactors for for all loads in the active circuit to the value given.';
- OptionHelp[71] := '{YES/TRUE | NO/FALSE} Default = FALSE. If YES/TRUE will automatically show the results of an Export Command after it is written.';
- OptionHelp[72] := 'Default is 2. Maximum number of iterations for load allocations for each time the AllocateLoads or Estimate command is given.';
- OptionHelp[73] := 'Set Default Base Frequency, Hz. Side effect: Sets solution Frequency and default Circuit Base Frequency. This value is saved when the DSS closes down.';
- OptionHelp[74] := '{YES/TRUE | NO/FALSE} Default is NO. Mark lines that are switches or are isolated with a symbol. See SwitchMarkerCode.';
- OptionHelp[75] := 'Numeric marker code for lines with switches or are isolated from the circuit. Default is 4. See markswitches option.';
- OptionHelp[76] := 'Default is 1.0. Relative size (a multiplier applied to default size) of daisy circles on daisy plot.';
- OptionHelp[77] := '{YES/TRUE | NO/FALSE} Default is NO. Mark transformer locations with a symbol. See TransMarkerCode. ' +
- 'The coordinate of one of the buses for winding 1 or 2 must be defined for the symbol to show';
- OptionHelp[78] := 'Numeric marker code (0..47 see Users Manual) for transformers. Default is 35. See markstransformers option.';
- OptionHelp[79] := 'Size of transformer marker. Default is 1.';
- OptionHelp[80] := '={Daily | Yearly | Duty | None*} Default loadshape class to use for mode=time and mode=dynamic simulations. Loads and generators, etc., will follow ' +
- 'this shape as time is advanced. Default value is None. That is, Load will not vary with time.';
- OptionHelp[81] := 'One of {Carson | FullCarson | Deri*}. Default is Deri, which is' +
- 'a fit to the Full Carson that works well into high frequencies. ' +
- '"Carson" is the simplified Carson method that is typically used for 50/60 Hz power flow programs. ' +
- 'Applies only to Line objects that use LineGeometry objects to compute impedances.';
- OptionHelp[82] := '{YES/TRUE | NO/FALSE} Default = FALSE. When set to TRUE/YES, clears the query log file and thereafter appends ' +
- 'the time-stamped Result string contents to the log file after a query command, ?. ';
- OptionHelp[83] := '{YES/TRUE | NO/FALSE} Default is NO. Mark Capacitor locations with a symbol. See CapMarkerCode. ';
- OptionHelp[84] := '{YES/TRUE | NO/FALSE} Default is NO. Mark Regulator locations with a symbol. See RegMarkerCode. ';
- OptionHelp[85] := '{YES/TRUE | NO/FALSE} Default is NO. Mark PVSystem locations with a symbol. See PVMarkerCode and PVMarkerSize. ';
- OptionHelp[86] := '{YES/TRUE | NO/FALSE} Default is NO. Mark Storage locations with a symbol. See StoreMarkerCode and StoreMarkerSize. ';
- OptionHelp[87] := 'Numeric marker code (0..47 -- see Users Manual) for Capacitors. Default is 38.';
- OptionHelp[88] := 'Numeric marker code (0..47 see Users Manual) for Regulators. Default is 17. (red)';
- OptionHelp[89] := 'Numeric marker code (0..47 see Users Manual) for PVSystems. Default is 15.';
- OptionHelp[90] := 'Numeric marker code (0..47 see Users Manual) for Storage elements. Default is 9.';
- OptionHelp[91] := 'Size of Capacitor marker. Default is 3.';
- OptionHelp[92] := 'Size of Regulator marker. Default is 5.';
- OptionHelp[93] := 'Size of PVsystem marker. Default is 1.';
- OptionHelp[94] := 'Size of Storage marker. Default is 1.';
- OptionHelp[95] := '{YES/TRUE | NO/FALSE} Default is NO. For Harmonic solution, neglect the Load shunt admittance branch that can siphon off some of the Load injection current. ' + CRLF + CRLF +
- 'If YES, the current injected from the LOAD at harmonic frequencies will be nearly ideal.';
- OptionHelp[96] := '{YES/TRUE | NO/FALSE} Default is NO. Mark Fuse locations with a symbol. See FuseMarkerCode and FuseMarkerSize. ';
- OptionHelp[97] := 'Numeric marker code (0..47 see Users Manual) for Fuse elements. Default is 25.';
- OptionHelp[98] := 'Size of Fuse marker. Default is 1.';
- OptionHelp[99] := '{YES/TRUE | NO/FALSE} Default is NO. Mark Recloser locations with a symbol. See RecloserMarkerCode and RecloserMarkerSize. ';
- OptionHelp[100] := 'Numeric marker code (0..47 see Users Manual) for Recloser elements. Default is 17. (color=Lime)';
- OptionHelp[101] := 'Size of Recloser marker. Default is 5.';
- OptionHelp[102] := '{YES/TRUE | NO/FALSE} Default is Yes. Update Windows Registry values upon exiting. You might want to turn this off if you temporarily ' +
- 'change fonts or DefaultBaseFrequency, for example. ' + CRLF + CRLF + 'NOTE: This is a no-op in DSS C-API, as the registry is not used at all.';
- OptionHelp[103] := '{YES/TRUE | NO/FALSE} Default is NO. Mark Relay locations with a symbol. See RelayMarkerCode and RelayMarkerSize. ';
- OptionHelp[104] := 'Numeric marker code (0..47 see Users Manual) for Relay elements. Default is 17. (Color=Lime)';
- OptionHelp[105] := 'Size of Relay marker. Default is 5.';
- OptionHelp[106] := 'The time in microseconds to execute the solve process in the most recent time step or solution (read only)';
- OptionHelp[107] := 'The accumulated time in microseconds to solve the circuit since the last reset. Set this value to reset the accumulator.';
- OptionHelp[108] := 'Process time + meter sampling time in microseconds for most recent time step - (read only)';
- OptionHelp[109] := '{YES/TRUE | NO/FALSE} Overrides default value for sampling EnergyMeter objects at the end of the solution loop. ' +
- 'Normally Time and Duty modes do not automatically sample EnergyMeters whereas Daily, Yearly, M1, M2, M3, LD1 and LD2 modes do. ' +
- 'Use this Option to turn sampling on or off';
- OptionHelp[110] := 'Minimum number of iterations required for a solution. Default is 2.';
- OptionHelp[111] := 'Activates/Deactivates the extended version of the plot command for figures with the DSS Visualization Tool.' + CRLF + CRLF + '**Not supported in DSS Extensions**';
- OptionHelp[112] := 'Keeploads = Y/N option for ReduceOption Laterals option';
- OptionHelp[113] := 'Sets the Zmag option (in Ohms) for ReduceOption Shortlines option. Lines have less line mode impedance are reduced.';
- OptionHelp[114] := 'Enables/disables the seasonal selection of the rating for determining if an element is overloaded. When enabled, the energy meter will' + CRLF +
- 'look for the rating (NormAmps) using the SeasonSignal to eavluate if the element is overloaded';
- OptionHelp[115] := 'Is the name of the XY curve defining the seasonal change when performing QSTS simulations.';
-
-{$IFDEF DSS_CAPI_PM}
- OptionHelp[OPTION_NumCPUs] := 'Delivers the number of threads (CPUs) available on the machine (read Only)';
- OptionHelp[OPTION_NumCores] := 'Delivers the number of physical processors (Cores) available on the computer. If your computers processor has less than 64 cores, this number should be equal to the half of the available CPUs, otherise the number should be the same (Read Only)';
- OptionHelp[OPTION_NumActors] := 'Delivers the number of Actors created by the user, 1 is the default';
- OptionHelp[OPTION_ActiveActor] := 'Gets/Sets the number of the active actor, if the value is * (set active actor=*), the commands send after this instruction will be aplied to all the actors.';
- OptionHelp[OPTION_CPU] := 'Gets/Sets the CPU to be used by the active actor';
- OptionHelp[OPTION_ActorProgress] := 'Gets progress (%) for all the actors when performing a task';
- OptionHelp[OPTION_Parallel] := 'Activates/Deactivates the parallel machine in OpenDSS-PM, if deactivated OpenDSS will behave as the classical version';
- OptionHelp[OPTION_ConcatenateReports] := 'Activates/Deactivates the option for concatenate the reports generated by the existing actors, if Yes, everytime the user' +
- 'a show/export monitor command the report will include the data generated by all the actors, otherwise the report will contain' +
- 'The data generated by the active actor';
- OptionHelp[OPTION_Coverage] := 'Percentage of coverage expected when estimating the longest paths on the circuit for tearing, the default coverage' + CRLF + CRLF +
- 'is the 90% (0.9), this value cannot exceed 1.0. When used with the "Set" command is used for the algorithm for estimating the paths within the circuit' + CRLF + CRLF +
- 'but when the "get" command is used after executing the tear_circuit command it will deliver the actual coverage after running the algorithm';
- OptionHelp[OPTION_Num_SubCircuits] := 'This is the number of subcircuits in which the circuit will be torn when executing the tear_circuit command, by default is the number of local CPUs - 1';
- OptionHelp[OPTION_ADiakoptics] := '{YES/TRUE | NO/FALSE} Activates the A-Diakoptics solution algorithm for using spatial parallelization on the feeder.' + CRLF +
- 'This parameter only affects Actor 1, no matter from which actor is called. When activated (True), OpenDSS will start the ' + CRLF +
- 'initialization routine for the A-Diakoptics solution mode';
- OptionHelp[OPTION_LinkBranches] := 'Returns the names of the link branches used for tearing the circuit after initializing using set ADiakoptics = True. Using this instruction will set the Active Actor = 1' + CRLF +
- 'If ADiakoptics is not initialized, this isntruction will return an error message';
-{$ENDIF}
+ ExecOption[i] := name;
+ end;
end;
-//----------------------------------------------------------------------------
-function DoSetCmd_NoCircuit(DSS: TDSSContext): Boolean; // Set Commands that do not require a circuit
-//----------------------------------------------------------------------------
+function DoSetCmd_NoCircuit({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext): Boolean; // Set Commands that do not require a circuit
// This is for setting global options that do not require an active circuit
-
var
ParamPointer: Integer;
ParamName: String;
Param: String;
{$IFDEF DSS_CAPI_PM}
- PMParent: TDSSContext;
+ PMParent, DSS: TDSSContext;
begin
- PMParent := DSS.GetPrime();
+ PMParent := MainDSS.GetPrime();
+ DSS := MainDSS.ActiveChild;
{$ELSE}
begin
{$ENDIF}
@@ -512,11 +235,11 @@ function DoSetCmd_NoCircuit(DSS: TDSSContext): Boolean; // Set Commands that do
if Length(ParamName) = 0 then
Inc(ParamPointer)
else
- ParamPointer := OptionList.GetCommand(ParamName);
+ ParamPointer := DSS.DSSExecutive.OptionList.GetCommand(ParamName);
case ParamPointer of
0:
- DoSimpleMsg(DSS, 'Unknown parameter "' + ParamName + '" for Set Command ', 130);
+ DoSimpleMsg(DSS, 'Unknown parameter "%s" for Set Command', [ParamName], 130);
15:
DefaultEditor := Param; // 'Editor='
57:
@@ -526,20 +249,20 @@ function DoSetCmd_NoCircuit(DSS: TDSSContext): Boolean; // Set Commands that do
73:
DSS.DefaultBaseFreq := DSS.Parser.DblValue;
102:
- DoSimpleMsg(DSS, 'This is not supported in DSS Extensions.', 302);
+ DoSimpleMsg(DSS, _('This is not supported in DSS Extensions.'), 302);
111:
- DoSimpleMsg(DSS, 'This is not supported in DSS Extensions.', 302);
+ DoSimpleMsg(DSS, _('This is not supported in DSS Extensions.'), 302);
{$IFDEF DSS_CAPI_PM}
- OPTION_ActiveActor:
+ ord(Opt.ActiveActor):
if DSS.Parser.StrValue = '*' then
begin
PMParent.AllActors := TRUE;
PMParent.ActiveChildIndex := 0;
- PMParent.ActiveChild := DSS;
+ PMParent.ActiveChild := PMParent;
end
else
begin
- if ((DSS.Parser.IntValue > 0) and (DSS.Parser.IntValue <= (high(PMParent.Children) + 1))) then
+ if (DSS.Parser.IntValue > 0) and (DSS.Parser.IntValue <= PMParent.NumOfActors) then
begin
PMParent.ActiveChildIndex := DSS.Parser.IntValue - 1;
PMParent.ActiveChild := PMParent.Children[PMParent.ActiveChildIndex];
@@ -547,31 +270,28 @@ function DoSetCmd_NoCircuit(DSS: TDSSContext): Boolean; // Set Commands that do
end
else
begin
- DoSimpleMsg(DSS, 'The actor does not exist', 7002);
+ DoSimpleMsg(DSS, _('The actor does not exist'), 7002);
end;
end;
- end;
- OPTION_CPU:
- begin
+ ord(Opt.CPU):
if DSS.Parser.IntValue < CPU_Cores then
begin
DSS.CPU := DSS.Parser.IntValue;
-//TODO if DSS.ActorHandle <> NIL then
-//TODO DSS.ActorHandle.CPU := ActorCPU;
+ if DSS.ActorThread <> NIL then
+ DSS.ActorThread.CPU := DSS.CPU;
end
else
begin
- DoSimpleMsg(DSS, 'The CPU does not exist', 7003);
+ DoSimpleMsg(DSS, _('The CPU does not exist'), 7003);
end;
- end;
- OPTION_Parallel:
+ ord(Opt.Parallel):
PMParent.Parallel_enabled := InterpretYesNo(Param);
- OPTION_ConcatenateReports:
+ ord(Opt.ConcatenateReports):
PMParent.ConcatenateReports := InterpretYesNo(Param);
{$ENDIF} //DSS_CAPI_PM
else
begin
- DoSimpleMsg(DSS, 'You must create a new circuit object first: "new circuit.mycktname" to execute this Set command.', 301);
+ DoSimpleMsg(DSS, _('You must create a new circuit object first: "new circuit.mycktname" to execute this Set command.'), 301);
Result := FALSE; // Indicate that we could not process all set command
Exit;
end;
@@ -583,26 +303,32 @@ function DoSetCmd_NoCircuit(DSS: TDSSContext): Boolean; // Set Commands that do
end;
-//----------------------------------------------------------------------------
-function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
-//----------------------------------------------------------------------------
-
+function DoSetCmd({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext; SolveOption: Integer): Integer;
// Set DSS Options
// Solve Command is re-routed here first to set options beFORe solving
-
var
ParamPointer: Integer;
ParamName: String;
Param: String;
TestLoadShapeObj: TLoadShapeObj;
{$IFDEF DSS_CAPI_PM}
- PMParent: TDSSContext;
+ PMParent, DSS: TDSSContext;
begin
- PMParent := DSS.GetPrime();
+ PMParent := MainDSS.GetPrime();
+ DSS := MainDSS.ActiveChild;
{$ELSE}
begin
{$ENDIF}
Result := 0;
+
+ if DSS.ActiveCircuit = NIL then
+ begin
+ if not DoSetCmd_NoCircuit(DSS) then
+ Result := 1;
+
+ Exit;
+ end;
+
// Continue parsing command line
ParamPointer := 0;
ParamName := DSS.Parser.NextParam;
@@ -612,11 +338,11 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
if Length(ParamName) = 0 then
Inc(ParamPointer)
else
- ParamPointer := OptionList.GetCommand(ParamName);
+ ParamPointer := DSS.DSSExecutive.OptionList.GetCommand(ParamName);
case ParamPointer of
0:
- DoSimpleMsg(DSS, 'Unknown parameter "' + ParamName + '" for Set Command ', 130);
+ DoSimpleMsg(DSS, 'Unknown parameter "%s" for Set Command', [ParamName], 130);
1, 12:
SetObjectClass(DSS, Param);
2, 13:
@@ -639,10 +365,10 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
Solution.DynaVars.h := InterpretTimeStepSize(DSS, Param);
Solution.IntervalHrs := Solution.DynaVars.h/3600.0;
end;
- 8:
- DSS.ActiveCircuit.solution.Mode := InterpretSolveMode(Param); // see DSSGlobals
+ ord(Opt.Mode):
+ DSS.ActiveCircuit.solution.Mode := TSolveMode(DSS.SolveModeEnum.StringToOrdinal(Param)); // see DSSGlobals
9:
- DSS.ActiveCircuit.solution.RandomType := InterpretRandom(Param);
+ DSS.ActiveCircuit.solution.RandomType := DSS.RandomModeEnum.StringToOrdinal(Param);
10:
DSS.ActiveCircuit.solution.NumberOfTimes := DSS.Parser.IntValue;
11:
@@ -658,7 +384,7 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
19:
with DSS.ActiveCircuit.solution do
begin
- DefaultLoadModel := InterpretLoadModel(DSS, Param); // for reverting to last on specified
+ DefaultLoadModel := DSS.DefaultLoadModelEnum.StringToOrdinal(Param); // for reverting to last on specified
LoadModel := DefaultLoadModel;
end;
20:
@@ -681,7 +407,7 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
LoadDurCurve := Param;
LoadDurCurveObj := DSS.LoadShapeClass.Find(Param);
if LoadDurCurveObj = NIL then
- DoSimpleMsg(DSS, 'Load-Duration Curve not found.', 131);
+ DoSimpleMsg(DSS, _('Load-Duration Curve not found.'), 131);
end;
28:
with DSS.ActiveCircuit do
@@ -696,7 +422,7 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
31:
DSS.ActiveCircuit.AutoAddObj.CapkVAR := DSS.Parser.DblValue;
32:
- DSS.ActiveCircuit.AutoAddObj.AddType := InterpretAddType(Param);
+ DSS.ActiveCircuit.AutoAddObj.AddType := DSS.AddTypeEnum.StringToOrdinal(Param);
33:
DSS.ActiveCircuit.DuplicatesAllowed := InterpretYesNo(Param);
34:
@@ -712,7 +438,7 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
39:
DSS.DSSExecutive.DoLegalVoltageBases;
40:
- DSS.ActiveCircuit.Solution.Algorithm := InterpretSolveAlg(Param);
+ DSS.ActiveCircuit.Solution.Algorithm := DSS.SolveAlgEnum.StringToOrdinal(Param);
41:
DSS.ActiveCircuit.TrapezoidalIntegration := InterpretYesNo(Param);
42:
@@ -720,7 +446,7 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
43:
with DSS.ActiveCircuit.Solution do
begin
- ControlMode := InterpretControlMode(Param);
+ ControlMode := DSS.ControlModeEnum.StringToOrdinal(Param);
DefaultControlMode := ControlMode; // always revert to last one specified in a script
end;
44:
@@ -742,7 +468,7 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
48:
DSS.DSSExecutive.DoSetAllocationFactors(DSS.Parser.DblValue);
49:
- DSS.ActiveCircuit.PositiveSequence := InterpretCktModel(Param);
+ DSS.ActiveCircuit.PositiveSequence := Boolean(DSS.CktModelEnum.StringToOrdinal(Param));
50:
DSS.ActiveCircuit.PriceSignal := DSS.Parser.DblValue;
51:
@@ -751,7 +477,7 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
PriceCurve := Param;
PriceCurveObj := DSS.PriceShapeClass.Find(Param);
if PriceCurveObj = NIL then
- DoSimpleMsg(DSS, 'Priceshape.' + param + ' not found.', 132);
+ DoSimpleMsg(DSS, 'Priceshape.%s not found.', [param], 132);
end;
52:
with DSS.ActiveCircuit do
@@ -826,9 +552,9 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
79:
DSS.ActiveCircuit.TransMarkerSize := DSS.Parser.IntValue;
80:
- DSS.ActiveCircuit.ActiveLoadShapeClass := InterpretLoadShapeClass(Param);
+ DSS.ActiveCircuit.ActiveLoadShapeClass := DSS.LoadShapeClassEnum.StringToOrdinal(Param);
81:
- DSS.DefaultEarthModel := InterpretEarthModel(Param);
+ DSS.DefaultEarthModel := DSS.EarthModelEnum.StringToOrdinal(Param);
82:
begin
DSS.LogQueries := InterpretYesNo(Param);
@@ -874,7 +600,7 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
101:
DSS.ActiveCircuit.RecloserMarkerSize := DSS.Parser.IntValue;
102:
- DoSimpleMsg(DSS, 'This is not supported in DSS Extensions.', 309);
+ DoSimpleMsg(DSS, _('This is not supported in DSS Extensions.'), 309);
103:
DSS.ActiveCircuit.MarkRelays := InterpretYesNo(Param);
104:
@@ -888,7 +614,7 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
110:
DSS.ActiveCircuit.solution.MinIterations := DSS.Parser.IntValue;
111:
- DoSimpleMsg(DSS, 'This is not supported in DSS Extensions.', 303);
+ DoSimpleMsg(DSS, _('This is not supported in DSS Extensions.'), 303);
112:
DSS.ActiveCircuit.ReduceLateralsKeepLoad := InterpretYesNo(Param);
113:
@@ -898,63 +624,53 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
115:
DSS.SeasonSignal := Param;
{$IFDEF DSS_CAPI_PM}
- OPTION_ActiveActor:
- begin
+ ord(Opt.ActiveActor):
if DSS.Parser.StrValue = '*' then
begin
PMParent.AllActors := TRUE;
PMParent.ActiveChildIndex := 0;
- PMParent.ActiveChild := PMParent.Children[PMParent.ActiveChildIndex];
+ PMParent.ActiveChild := PMParent;
end
else
begin
if (DSS.Parser.IntValue > 0) and (DSS.Parser.IntValue <= PMParent.NumOfActors) then
begin
- PMParent.AllActors := FALSE;
PMParent.ActiveChildIndex := DSS.Parser.IntValue - 1;
PMParent.ActiveChild := PMParent.Children[PMParent.ActiveChildIndex];
+ PMParent.AllActors := FALSE;
end
else
begin
- DoSimpleMsg(DSS, 'The actor does not exist', 7002);
+ DoSimpleMsg(DSS, _('The actor does not exist'), 7002);
end;
end;
- end;
- OPTION_CPU:
- begin
+ ord(Opt.CPU):
if DSS.Parser.IntValue < CPU_Cores then
begin
DSS.CPU := DSS.Parser.IntValue;
-//TODO if DSS.ActorHandle <> NIL then
-//TODO DSS.ActorHandle.CPU := DSS.CPU;
+ if DSS.ActorThread <> NIL then
+ DSS.ActorThread.CPU := DSS.CPU;
end
else
begin
- DoSimpleMsg(DSS, 'The CPU does not exist', 7003);
+ DoSimpleMsg(DSS, _('The CPU does not exist'), 7003);
end;
- end;
- OPTION_Parallel:
- begin
+ ord(Opt.Parallel):
PMParent.Parallel_enabled := InterpretYesNo(Param);
- end;
- OPTION_ConcatenateReports:
- begin
+ ord(Opt.ConcatenateReports):
PMParent.ConcatenateReports := InterpretYesNo(Param);
- end;
- OPTION_Coverage:
- begin
+{$ENDIF}
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ ord(Opt.Coverage):
DSS.ActiveCircuit.Coverage := DSS.Parser.DblValue;
- end;
- OPTION_Num_SubCircuits:
- begin
+ ord(Opt.Num_SubCircuits):
DSS.ActiveCircuit.Num_SubCkts := DSS.Parser.IntValue;
- end;
- OPTION_ADiakoptics:
+ ord(Opt.ADiakoptics):
begin
if InterpretYesNo(Param) then
ADiakopticsInit(DSS) // Initalizes the parallel environment if enabled
else
- DSS.ADiakoptics := FALSE;
+ DSS.ActiveCircuit.Solution.ADiakoptics := FALSE;
end;
{$ENDIF}
else
@@ -965,12 +681,6 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
case ParamPointer of
3, 4:
DSS.ActiveCircuit.Solution.Update_dblHour;
- // Update IntervalHrs for devices that integrate
- 7, 18:
- DSS.ActiveCircuit.Solution.IntervalHrs := DSS.ActiveCircuit.Solution.DynaVars.h / 3600.0;
-
- //TODO: check in v8
- //ActiveCircuit.Solution.Update_dblHour;
end;
ParamName := DSS.Parser.NextParam;
@@ -979,45 +689,40 @@ function DoSetCmd(DSS: TDSSContext; SolveOption: Integer): Integer;
if SolveOption = 1 then
DSS.DSSExecutive.DoSolveCmd;
-
end;
-
-//----------------------------------------------------------------------------
-function DoGetCmd(DSS: TDSSContext): Integer;
-
+function DoGetCmd({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext): Integer;
// Get DSS Options Reguest and put it in Global Result string
// may be retrieved by Result property of the DSSText interface
-
var
ParamPointer, i: Integer;
ParamName: String;
Param: String;
{$IFDEF DSS_CAPI_PM}
- PMParent: TDSSContext;
+ PMParent, DSS: TDSSContext;
begin
- PMParent := DSS.GetPrime();
+ PMParent := MainDSS.GetPrime();
+ DSS := MainDSS.ActiveChild;
{$ELSE}
begin
{$ENDIF}
Result := 0;
try
-
DSS.GlobalResult := ''; //initialize for appending
- // Continue parsing command line
+ // Continue parsing command line
ParamName := DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
- // there will be no named paramters in this command and the params
- // themselves will be the parameter name to return
+ // there will be no named paramters in this command and the params
+ // themselves will be the parameter name to return
while Length(Param) > 0 do
begin
- ParamPointer := OptionList.GetCommand(Param);
+ ParamPointer := DSS.DSSExecutive.OptionList.GetCommand(Param);
case ParamPointer of
0:
- DoSimpleMsg(DSS, 'Unknown parameter "' + ParamName + '" for Get Command ', 133);
+ DoSimpleMsg(DSS, 'Unknown parameter "%s" for Get Command', [ParamName], 133);
1, 12:
AppendGlobalResult(DSS, DSS.ActiveCircuit.ActiveCktElement.DSSClassName);
2, 13:
@@ -1032,10 +737,10 @@ function DoGetCmd(DSS: TDSSContext): Integer;
AppendGlobalResult(DSS, DSS.ActiveCircuit.solution.Frequency);
7, 18:
AppendGlobalResult(DSS, DSS.ActiveCircuit.solution.DynaVars.h);
- 8:
- AppendGlobalResult(DSS, GetSolutionModeID(DSS));
+ ord(Opt.Mode):
+ AppendGlobalResult(DSS, DSS.SolveModeEnum.OrdinalToString(ord(DSS.ActiveCircuit.Solution.mode)));
9:
- AppendGlobalResult(DSS, GetRandomModeID(DSS));
+ AppendGlobalResult(DSS, DSS.RandomModeEnum.OrdinalToString(DSS.ActiveCircuit.Solution.RandomType));
10:
AppendGlobalResult(DSS, DSS.ActiveCircuit.solution.NumberOfTimes);
11:
@@ -1049,7 +754,7 @@ function DoGetCmd(DSS: TDSSContext): Integer;
17:
AppendGlobalResult(DSS, DSS.ActiveCircuit.solution.MaxIterations);
19:
- AppendGlobalResult(DSS, GetLoadModel(DSS));
+ AppendGlobalResult(DSS, DSS.DefaultLoadModelEnum.OrdinalToString(DSS.ActiveCircuit.Solution.LoadModel));
20:
AppendGlobalResult(DSS, DSS.ActiveCircuit.LoadMultiplier);
21:
@@ -1106,12 +811,7 @@ function DoGetCmd(DSS: TDSSContext): Integer;
DSS.GlobalResult := DSS.GlobalResult + ')';
end;
40:
- case DSS.ActiveCircuit.Solution.Algorithm of
- NORMALSOLVE:
- AppendGlobalResult(DSS, 'normal');
- NEWTONSOLVE:
- AppendGlobalResult(DSS, 'newton');
- end;
+ AppendGlobalResult(DSS, DSS.SolveAlgEnum.OrdinalToString(DSS.ActiveCircuit.Solution.Algorithm));
41:
AppendGlobalResult(DSS, DSS.ActiveCircuit.TrapezoidalIntegration);
42:
@@ -1119,7 +819,7 @@ function DoGetCmd(DSS: TDSSContext): Integer;
for i := 1 to Count do
AppendGlobalResult(DSS, NameOfIndex(i));
43:
- AppendGlobalResult(DSS, GetControlModeID(DSS));
+ AppendGlobalResult(DSS, DSS.ControlModeEnum.OrdinalToString(DSS.ActiveCircuit.Solution.Controlmode));
44:
AppendGlobalResult(DSS, DSS.ActiveCircuit.ControlQueue.traceLog);
45:
@@ -1131,10 +831,7 @@ function DoGetCmd(DSS: TDSSContext): Integer;
48:
AppendGlobalResult(DSS, 'Get function not applicable.');
49:
- if DSS.ActiveCircuit.positiveSequence then
- AppendGlobalResult(DSS, 'positive')
- else
- AppendGlobalResult(DSS, 'multiphase');
+ AppendGlobalResult(DSS, DSS.CktModelEnum.OrdinalToString(Integer(DSS.ActiveCircuit.positiveSequence)));
50:
AppendGlobalResult(DSS, DSS.ActiveCircuit.PriceSignal);
51:
@@ -1206,9 +903,9 @@ function DoGetCmd(DSS: TDSSContext): Integer;
79:
AppendGlobalResult(DSS, DSS.ActiveCircuit.TransMarkerSize);
80:
- AppendGlobalResult(DSS, GetActiveLoadShapeClass(DSS));
+ AppendGlobalResult(DSS, DSS.LoadShapeClassEnum.OrdinalToString(DSS.ActiveCircuit.ActiveLoadShapeClass));
81:
- AppendGlobalResult(DSS, GetEarthModel(DSS.DefaultEarthModel));
+ AppendGlobalResult(DSS, DSS.EarthModelEnum.OrdinalToString(DSS.DefaultEarthModel));
82:
AppendGlobalResult(DSS, DSS.LogQueries);
83:
@@ -1279,48 +976,43 @@ function DoGetCmd(DSS: TDSSContext): Integer;
AppendGlobalResult(DSS, DSS.SeasonSignal);
{$IFDEF DSS_CAPI_PM}
- OPTION_NumCPUs:
+ ord(Opt.NumCPUs):
AppendGlobalResult(DSS, Format('%d', [CPU_Cores]));
- OPTION_NumCores:
- AppendGlobalResult(DSS, Format('%-g', [CPU_Cores / 2])); //TODO: fix -- some people do disable hyperthreading on Intel CPUs nowadays
- OPTION_NumActors:
+ ord(Opt.NumCores):
+ AppendGlobalResult(DSS, Format('%-g', [CPU_Cores / 2])); //TODO: fix -- some people do disable hyperthreading on Intel CPUs nowadays, some CPUs don't have it
+ ord(Opt.NumActors):
AppendGlobalResult(DSS, Format('%d', [high(PMParent.Children) + 1]));
- OPTION_ActiveActor:
+ ord(Opt.ActiveActor):
begin
if PMParent.AllActors then
AppendGlobalResult(DSS, 'All')
else
AppendGlobalResult(DSS, Format('%d', [PMParent.ActiveChildIndex + 1]));
end;
- OPTION_CPU:
+ ord(Opt.CPU):
AppendGlobalResult(DSS, Format('%d', [PMParent.ActiveChild.CPU]));
- OPTION_ActorProgress:
+ ord(Opt.ActorProgress):
{$IFNDEF FPC}
ScriptEd.UpdateProgressSummary
{$ENDIF}
;
- OPTION_Parallel:
- if PMParent.parallel_enabled then
- AppendGlobalResult(DSS, 'Yes')
- else
- AppendGlobalResult(DSS, 'No');
- OPTION_ConcatenateReports:
- if PMParent.ConcatenateReports then
- AppendGlobalResult(DSS, 'Yes')
- else
- AppendGlobalResult(DSS, 'No');
- OPTION_Coverage:
+ ord(Opt.Parallel):
+ AppendGlobalResult(DSS, PMParent.parallel_enabled);
+ ord(Opt.ConcatenateReports):
+ AppendGlobalResult(DSS, PMParent.ConcatenateReports);
+ ord(Opt.NUMANodes):
+ DoSimpleMsg(DSS, _('This is not supported in DSS Extensions.'), 303); //TODO: looks like the official version has this hardcoded
+{$ENDIF} //DSS_CAPI_PM
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ ord(Opt.Coverage):
AppendGlobalResult(DSS, Format('%-g', [DSS.ActiveCircuit.Actual_Coverage]));
- OPTION_Num_SubCircuits:
+ ord(Opt.Num_SubCircuits):
AppendGlobalResult(DSS, Format('%d', [DSS.ActiveCircuit.Num_SubCkts]));
- OPTION_ADiakoptics:
- if PMParent.ADiakoptics then
- AppendGlobalResult(DSS, 'Yes')
- else
- AppendGlobalResult(DSS, 'No');
- OPTION_LinkBranches:
+ ord(Opt.ADiakoptics):
+ AppendGlobalResult(DSS, PMParent.ActiveCircuit.Solution.ADiakoptics);
+ ord(Opt.LinkBranches):
begin
- if PMParent.ADiakoptics then
+ if PMParent.ActiveCircuit.Solution.ADiakoptics then
begin
for i := 1 to High(PMParent.ActiveCircuit.Link_Branches) do
AppendGlobalResult(DSS, PMParent.ActiveCircuit.Link_Branches[i]);
@@ -1328,35 +1020,31 @@ function DoGetCmd(DSS: TDSSContext): Integer;
else
AppendGlobalResult(DSS, 'Initialize A-Diakoptics first!');
end;
-{$ENDIF} //DSS_CAPI_PM
-
+{$ENDIF}
else
// Ignore excess parameters
end;
ParamName := DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
- end; {WHILE}
+ end; // WHILE
except
AppendGlobalResult(DSS, '***Error***');
end;
-
end;
-//----------------------------------------------------------------------------
-function DoGetCmd_NoCircuit(DSS: TDSSContext): Boolean;
-
+function DoGetCmd_NoCircuit({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext): Boolean;
// Get DSS Options Reguest and put it in Global Result string
// may be retrieved by Result property of the DSSText interface
-
var
// ParamName: String;
Param: String;
{$IFDEF DSS_CAPI_PM}
ParamPointer: Integer;
- PMParent: TDSSContext;
+ PMParent, DSS: TDSSContext;
begin
- PMParent := DSS.GetPrime();
+ PMParent := MainDSS.GetPrime();
+ DSS := MainDSS.ActiveChild;
{$ELSE}
begin
{$ENDIF}
@@ -1373,34 +1061,30 @@ function DoGetCmd_NoCircuit(DSS: TDSSContext): Boolean;
while Length(Param) > 0 do
begin
{$IFDEF DSS_CAPI_PM}
- ParamPointer := OptionList.GetCommand(Param);
+ ParamPointer := DSS.DSSExecutive.OptionList.GetCommand(Param);
case ParamPointer of
- OPTION_NumCPUs:
+ ord(Opt.NumCPUs):
AppendGlobalResult(DSS, Format('%d', [CPU_Cores]));
- OPTION_NumCores:
+ ord(Opt.NumCores):
AppendGlobalResult(DSS, Format('%-g', [CPU_Cores / 2])); //TODO: fix -- some people do disable hyperthreading on Intel CPUs nowadays
- OPTION_NumActors:
+ ord(Opt.NumActors):
AppendGlobalResult(DSS, Format('%d', [high(PMParent.Children) + 1]));
- OPTION_ActiveActor:
+ ord(Opt.NUMANodes):
+ ;
+ ord(Opt.ActiveActor):
if PMParent.AllActors then
AppendGlobalResult(DSS, 'All')
else
AppendGlobalResult(DSS, Format('%d', [DSS.Parent.ActiveChildIndex + 1]));
- OPTION_CPU:
+ ord(Opt.CPU):
AppendGlobalResult(DSS, Format('%d', [PMParent.ActiveChild.CPU]));
- OPTION_Parallel:
- if PMParent.parallel_enabled then
- AppendGlobalResult(DSS, 'Yes')
- else
- AppendGlobalResult(DSS, 'No');
- OPTION_ConcatenateReports:
- if PMParent.ConcatenateReports then
- AppendGlobalResult(DSS, 'Yes')
- else
- AppendGlobalResult(DSS, 'No');
+ ord(Opt.Parallel):
+ AppendGlobalResult(DSS, PMParent.parallel_enabled);
+ ord(Opt.ConcatenateReports):
+ AppendGlobalResult(DSS, PMParent.ConcatenateReports)
else
begin
- DoSimpleMsg(DSS, 'You must create a new circuit object first: "new circuit.mycktname" to execute this Set command.', 301);
+ DoSimpleMsg(DSS, _('You must create a new circuit object first: "new circuit.mycktname" to execute this Set command.'), 301);
Result := FALSE; // Indicate that we could not process all set command
Exit;
end;
@@ -1408,39 +1092,31 @@ function DoGetCmd_NoCircuit(DSS: TDSSContext): Boolean;
{ParamName :=} DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
{$ELSE}
- DoSimpleMsg(DSS, 'You must create a new circuit object first: "new circuit.mycktname" to execute this Set command.', 301);
+ DoSimpleMsg(DSS, _('You must create a new circuit object first: "new circuit.mycktname" to execute this Set command.'), 301);
Result := FALSE; // Indicate that we could not process all set command
Exit;
{$ENDIF} // DSS_CAPI_PM
end; {WHILE}
except
- AppendGlobalResult(DSS, '***Error***');
+ AppendGlobalResult(DSS, _('***Error***'));
end;
-
end;
procedure DisposeStrings;
var
i: Integer;
-
begin
for i := 1 to NumExecOptions do
begin
ExecOption[i] := '';
- OptionHelp[i] := '';
end;
-
end;
-
initialization
-
DefineOptions;
finalization
-
DisposeStrings;
-
end.
diff --git a/src/Executive/Executive.pas b/src/Executive/Executive.pas
index c3cd9d797..66f3f963b 100644
--- a/src/Executive/Executive.pas
+++ b/src/Executive/Executive.pas
@@ -6,44 +6,16 @@
----------------------------------------------------------
}
-{ Change Log
-
- 8/12/99 Added Show Zone Help string
-
- 10/11/99 Added Dump Commands option. Moved ExecCommand into Public area.
- 10/12/99 ADded new AutoAdd options and revised behavior of New Command.
- 10/14/99 Added UE weighting option
- Fixed Redirect/Compile to change default directory.
- 11/2/99 Added message in Open and Close cmd for ckt element not found.
- 12/3/99 Fixed bug in command parser - needed quotes when rebuilding command line
- 12/6/99 Merged Set and Solve commands
- 1-14-00 Added Get Command
- Added LossWeight, UEreg, lossreg properties
- 2-20-00 Revised Helpform so that help strings won't go away after Clear
- 3-2-00 Repaired some places where re-parsing would mess up on names with blanks
- 3-10-00 Added FileEdit and Export commands
- 3-20-00 Added DefaultDaily and DefaultYearly Options
- 4-17-00 Moved bulk of functions to ExecHelper
- Added AllocateLoads Command and AllocationFactors option
- 8-23-00 Added Price Signal Option
- 9-18-00 Fixed Dump Command Help
- 9-20-00 Added Dynamic Mode
- 10-3-00 Removed test for comment since '//' is now done in the Parser
- 5/22/01 Changed behavior of Compile and Redirect with respect to directory changes.
- 5/30/01 Add Set maxControlIterations
- 7/19/01 Added Totals command, Capacity Command
- 8/1/01 Revise the way the Capacity Command works
- 9/12/02 Added Classes and UserClasses
- 2/4/03 Added Set Bus=
- Added Zsc, Zsc012.
- Changed way Voltages command works
-
-}
-
interface
USES
- Classes, DSSPointerList, Command, DSSClass;
+ Classes,
+ DSSPointerList,
+ Command,
+ Contnrs,
+ DSSClass,
+ CAPI_Utils,
+ CAPI_Types;
TYPE
TExecutive = class(TObject)
@@ -55,41 +27,80 @@ TExecutive = class(TObject)
FUNCTION Get_LastError:String;
FUNCTION Get_ErrorResult:Integer;
-
function Get_Command: String;
- procedure Set_Command(const Value: String);
- procedure Set_RecorderOn(const Value: Boolean);
-
+
+ procedure Set_RecorderOn(const Value: Boolean);
public
DSS: TDSSContext;
+ CommandList: TCommandList;
+ OptionList: TCommandList;
+
constructor Create(dssContext: TDSSContext);
destructor Destroy; override;
PROCEDURE CreateDefaultDSSItems;
- Procedure Write_to_RecorderFile(const s:string);
+ Procedure Write_to_RecorderFile(const s:String);
- Procedure Clear;
+ Procedure Clear(Resetting: Boolean = True);
{$IFDEF DSS_CAPI_PM}
Procedure ClearAll;
{$ENDIF}
+
+ procedure Set_Command(const Value: String); overload;
+ procedure Set_Command(const Value: String; LineNum: Integer); overload;
+
Property Command:String read Get_Command write Set_Command;
Property Error:Integer read Get_ErrorResult;
Property LastError:String read Get_LastError;
Property RecorderOn:Boolean Read FRecorderOn write Set_RecorderOn;
+ // ZIP functions
+ procedure ZipOpen(ZipFileName: String);
+ procedure ZipClose();
+ procedure ZipRedirect(FileInZip: String);
+ procedure ZipExtract(var ResultPtr: PByte; ResultCount: PAPISize; FileInZip: String);
+ function ZipHashes(var Hashes: TFPHashList): Boolean;
+ function InZip: Boolean;
+ function CurrentZipFileName: String;
+ procedure SetInZipPath(path: String);
+ function GetZipStream(fn: String): TStream;
end;
implementation
-
-USES ExecCommands, ExecOptions,
- {ExecHelper,} DSSClassDefs, DSSGlobals, ParserDel, SysUtils,
+USES BufStream, ExecCommands, ExecOptions,
+ ExecHelper, DSSClassDefs, DSSGlobals, ParserDel, SysUtils,
Utilities, Solution, DSSHelper,
- {$IFDEF FPC} CmdForms{$ELSE} DSSForms{$ENDIF};
+ CmdForms,
+ Zipper,
+ StrUtils;
+
+type
+ TDSSUnZipper = class(TUnZipper)
+ public
+ Enabled: Boolean;
+ ziphash: TFPHashList;
+ dataStream: TStream;
+
+ constructor Create(fn: String);
+ destructor Destroy; override;
+ procedure DoCreateOutZipStream(sender: TObject; var stream: TStream; it: TFullZipFileEntry);
+ procedure DoDoneOutZipStream(sender: TObject; var stream: TStream; it: TFullZipFileEntry);
+ function GetFile(fn: String): TStream;
+ procedure PrepareHashmap();
+ end;
+function TExecutive.InZip: Boolean;
+begin
+ Result := (DSS.unzipper <> NIL) and (TDSSUnZipper(DSS.unzipper).Enabled);
+end;
+
+function TExecutive.CurrentZipFileName: String;
+begin
+ Result := TDSSUnZipper(DSS.unzipper).FileName;
+end;
-//----------------------------------------------------------------------------
Constructor TExecutive.Create(dssContext: TDSSContext);
Begin
Inherited Create;
@@ -102,7 +113,7 @@ implementation
// Exec options
OptionList := TCommandList.Create(ExecOption);
- {Instantiate All DSS Classe Definitions, Intrinsic and User-defined}
+ // Instantiate All DSS Classe Definitions, Intrinsic and User-defined
CreateDSSClasses(DSS); // in DSSGlobals
DSS.Circuits := TDSSPointerList.Create(2); // default buffer for 2 active circuits
@@ -115,94 +126,85 @@ implementation
FRecorderOn := FALSE;
FrecorderFile := '';
- {Override Locale defaults so that CSV files get written properly}
+ // Override Locale defaults so that CSV files get written properly
FormatSettings.DecimalSeparator := '.';
FormatSettings.ThousandSeparator := ',';
+end;
-End;
-
-
-//----------------------------------------------------------------------------
-Destructor TExecutive.Destroy;
-
-Begin
- If RecorderOn Then
+destructor TExecutive.Destroy;
+begin
+ if RecorderOn then
RecorderOn := FALSE;
- ClearAllCircuits(DSS);
-
- CommandList.Free;
- OptionList.Free;
- DSS.Circuits.Free;
-
- DisposeDSSClasses(DSS);
+ Clear(False);
+
+ CommandList.Free;
+ OptionList.Free;
+ DSS.Circuits.Free;
- Inherited Destroy;
-End;
+ inherited Destroy;
+end;
-//----------------------------------------------------------------------------
FUNCTION TExecutive.Get_LastError:String;
-
Begin
Result := DSS.LastErrorMessage;
End;
-//----------------------------------------------------------------------------
FUNCTION TExecutive.Get_ErrorResult:Integer;
Begin
Result := DSS.ErrorNumber;
End;
-
-//----------------------------------------------------------------------------
PROCEDURE TExecutive.CreateDefaultDSSItems;
-
-{Create default loadshapes, growthshapes, and other general DSS objects
- used by all circuits.
-}
-Begin
-
-{ this load shape used for generator dispatching, etc. Loads may refer to it, also.}
- Command := 'new loadshape.default npts=24 1.0 mult=(.677 .6256 .6087 .5833 .58028 .6025 .657 .7477 .832 .88 .94 .989 .985 .98 .9898 .999 1 .958 .936 .913 .876 .876 .828 .756)';
- IF DSS.CmdResult = 0 THEN
- Begin
- Command := 'new growthshape.default 2 year="1 20" mult=(1.025 1.025)'; // 20 years at 2.5%
- Command := 'new spectrum.default 7 Harmonic=(1 3 5 7 9 11 13) %mag=(100 33 20 14 11 9 7) Angle=(0 0 0 0 0 0 0)';
- Command := 'new spectrum.defaultload 7 Harmonic=(1 3 5 7 9 11 13) %mag=(100 1.5 20 14 1 9 7) Angle=(0 180 180 180 180 180 180)';
- Command := 'new spectrum.defaultgen 7 Harmonic=(1 3 5 7 9 11 13) %mag=(100 5 3 1.5 1 .7 .5) Angle=(0 0 0 0 0 0 0)';
- Command := 'new spectrum.defaultvsource 1 Harmonic=(1 ) %mag=(100 ) Angle=(0 ) ';
- Command := 'new spectrum.linear 1 Harmonic=(1 ) %mag=(100 ) Angle=(0 ) ';
- Command := 'new spectrum.pwm6 13 Harmonic=(1 3 5 7 9 11 13 15 17 19 21 23 25) %mag=(100 4.4 76.5 62.7 2.9 24.8 12.7 0.5 7.1 8.4 0.9 4.4 3.3) Angle=(-103 -5 28 -180 -33 -59 79 36 -253 -124 3 -30 86)';
- Command := 'new spectrum.dc6 10 Harmonic=(1 3 5 7 9 11 13 15 17 19) %mag=(100 1.2 33.6 1.6 0.4 8.7 1.2 0.3 4.5 1.3) Angle=(-75 28 156 29 -91 49 54 148 -57 -46)';
- Command := 'New TCC_Curve.A 5 c_array=(1, 2.5, 4.5, 8.0, 14.) t_array=(0.15 0.07 .05 .045 .045) ';
- Command := 'New TCC_Curve.D 5 c_array=(1, 2.5, 4.5, 8.0, 14.) t_array=(6 0.7 .2 .06 .02)';
- Command := 'New TCC_Curve.TLink 7 c_array=(2 2.1 3 4 6 22 50) t_array=(300 100 10.1 4.0 1.4 0.1 0.02)';
- Command := 'New TCC_Curve.KLink 6 c_array=(2 2.2 3 4 6 30) t_array=(300 20 4 1.3 0.41 0.02)';
- Command := 'New "TCC_Curve.uv1547" npts=2 C_array=(0.5, 0.9, ) T_array=(0.166, 2, )';
- Command := 'New "TCC_Curve.ov1547" npts=2 C_array=(1.1, 1.2, ) T_array=(2, 0.166, )';
- Command := 'New "TCC_Curve.mod_inv" npts=15 C_array=(1.1, 1.3, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50, 100, ) T_array=(27.1053, 9.9029, 6.439, 3.8032, 2.4322, 1.9458, 1.6883, 1.5255, 1.4117, 1.3267, 1.2604, 1.2068, 0.9481, 0.7468, 0.6478, )';
- Command := 'New "TCC_Curve.very_inv" npts=15 C_array=(1.1, 1.3, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50, 100, ) T_array=(93.872, 28.9113, 16.179, 7.0277, 2.9423, 1.7983, 1.3081, 1.0513, 0.8995, 0.8023, 0.7361, 0.6891, 0.5401, 0.4988, 0.493, )';
- Command := 'New "TCC_Curve.ext_inv" npts=15 C_array=(1.1, 1.3, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50, 100, ) T_array=(134.4074, 40.9913, 22.6817, 9.5217, 3.6467, 2.0017, 1.2967, 0.9274, 0.7092, 0.5693, 0.4742, 0.4065, 0.1924, 0.133, 0.1245, )';
- Command := 'New "TCC_Curve.definite" npts=3 C_array=(1, 1.001, 100, ) T_array=(300, 1, 1, )';
- End;
-
-
-End;
-
+// Create default loadshapes, growthshapes, and other general DSS objects
+// used by all circuits.
+begin
+ // this load shape used for generator dispatching, etc. Loads may refer to it, also.
+ Command := 'new loadshape.default npts=24 1.0 mult=(.677 .6256 .6087 .5833 .58028 .6025 .657 .7477 .832 .88 .94 .989 .985 .98 .9898 .999 1 .958 .936 .913 .876 .876 .828 .756)';
+ IF DSS.CmdResult <> 0 THEN
+ Exit;
+
+ Set_Command('new growthshape.default 2 year="1 20" mult=(1.025 1.025)'); // 20 years at 2.5%
+ Set_Command('new spectrum.default 7 Harmonic=(1 3 5 7 9 11 13) %mag=(100 33 20 14 11 9 7) Angle=(0 0 0 0 0 0 0)');
+ Set_Command('new spectrum.defaultload 7 Harmonic=(1 3 5 7 9 11 13) %mag=(100 1.5 20 14 1 9 7) Angle=(0 180 180 180 180 180 180)');
+ Set_Command('new spectrum.defaultgen 7 Harmonic=(1 3 5 7 9 11 13) %mag=(100 5 3 1.5 1 .7 .5) Angle=(0 0 0 0 0 0 0)');
+ Set_Command('new spectrum.defaultvsource 1 Harmonic=(1 ) %mag=(100 ) Angle=(0 ) ');
+ Set_Command('new spectrum.linear 1 Harmonic=(1 ) %mag=(100 ) Angle=(0 ) ');
+ Set_Command('new spectrum.pwm6 13 Harmonic=(1 3 5 7 9 11 13 15 17 19 21 23 25) %mag=(100 4.4 76.5 62.7 2.9 24.8 12.7 0.5 7.1 8.4 0.9 4.4 3.3) Angle=(-103 -5 28 -180 -33 -59 79 36 -253 -124 3 -30 86)');
+ Set_Command('new spectrum.dc6 10 Harmonic=(1 3 5 7 9 11 13 15 17 19) %mag=(100 1.2 33.6 1.6 0.4 8.7 1.2 0.3 4.5 1.3) Angle=(-75 28 156 29 -91 49 54 148 -57 -46)');
+ DSS.SpectrumClass.BindDefaults();
+ Set_Command('New TCC_Curve.A 5 c_array=(1, 2.5, 4.5, 8.0, 14.) t_array=(0.15 0.07 .05 .045 .045) ');
+ Set_Command('New TCC_Curve.D 5 c_array=(1, 2.5, 4.5, 8.0, 14.) t_array=(6 0.7 .2 .06 .02)');
+ Set_Command('New TCC_Curve.TLink 7 c_array=(2 2.1 3 4 6 22 50) t_array=(300 100 10.1 4.0 1.4 0.1 0.02)');
+ Set_Command('New TCC_Curve.KLink 6 c_array=(2 2.2 3 4 6 30) t_array=(300 20 4 1.3 0.41 0.02)');
+ Set_Command('New "TCC_Curve.uv1547" npts=2 C_array=(0.5, 0.9, ) T_array=(0.166, 2, )');
+ Set_Command('New "TCC_Curve.ov1547" npts=2 C_array=(1.1, 1.2, ) T_array=(2, 0.166, )');
+ Set_Command('New "TCC_Curve.mod_inv" npts=15 C_array=(1.1, 1.3, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50, 100, ) T_array=(27.1053, 9.9029, 6.439, 3.8032, 2.4322, 1.9458, 1.6883, 1.5255, 1.4117, 1.3267, 1.2604, 1.2068, 0.9481, 0.7468, 0.6478, )');
+ Set_Command('New "TCC_Curve.very_inv" npts=15 C_array=(1.1, 1.3, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50, 100, ) T_array=(93.872, 28.9113, 16.179, 7.0277, 2.9423, 1.7983, 1.3081, 1.0513, 0.8995, 0.8023, 0.7361, 0.6891, 0.5401, 0.4988, 0.493, )');
+ Set_Command('New "TCC_Curve.ext_inv" npts=15 C_array=(1.1, 1.3, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50, 100, ) T_array=(134.4074, 40.9913, 22.6817, 9.5217, 3.6467, 2.0017, 1.2967, 0.9274, 0.7092, 0.5693, 0.4742, 0.4065, 0.1924, 0.133, 0.1245, )');
+ Set_Command('New "TCC_Curve.definite" npts=3 C_array=(1, 1.001, 100, ) T_array=(300, 1, 1, )');
+end;
function TExecutive.Get_Command: String;
begin
Result := DSS.LastCmdLine;
end;
+procedure TExecutive.Set_Command(const Value: String); overload;
+begin
+ Set_Command(Value, -1)
+end;
-procedure TExecutive.Set_Command(const Value: String);
+procedure TExecutive.Set_Command(const Value: String; LineNum: Integer);
{$IFDEF DSS_CAPI_PM}
var
- PMParent: TDSSContext;
idx: Integer;
+ PMParent, ChDSS: TDSSContext;
begin
PMParent := DSS.GetPrime();
+ ChDSS := DSS.ActiveChild;
+ if ChDSS = NIL then
+ ChDSS := DSS;
{$ELSE}
begin
{$ENDIF}
@@ -213,40 +215,72 @@ procedure TExecutive.Set_Command(const Value: String);
begin
//TODO: if in the future the commands are processed in threads, this would need a lock, and
// maybe allow certain commands only in the DSSPrime instance to simplify things
- if PMParent.AllActors then
- ProcessCommand(PMParent.Children[idx], Value);
+ if not PMParent.AllActors then
+ begin
+ Exit;
+ end;
+
+ ChDSS := PMParent.Children[idx];
+ PMParent.ActiveChild := ChDSS;
+ PMParent.ActiveChildIndex := idx;
+ ProcessCommand(ChDSS, Value);
end;
+ PMParent.ActiveChild := PMParent;
+ PMParent.ActiveChildIndex := 0;
end
else
{$ENDIF}
- ProcessCommand(DSS, Value);
+ ProcessCommand({$IFDEF DSS_CAPI_PM}ChDSS{$ELSE}DSS{$ENDIF}, Value);
end;
-procedure TExecutive.Clear;
+procedure TExecutive.Clear(Resetting: Boolean = True);
begin
- IF (DSS.NumCircuits > 0) OR (DSS_CAPI_LEGACY_MODELS <> DSS_CAPI_LEGACY_MODELS_PREV) THEN
- Begin
+ if (DSS.NumCircuits > 0) OR (DSS_CAPI_LEGACY_MODELS <> DSS_CAPI_LEGACY_MODELS_PREV) then
+ begin
+{$IFDEF DSS_CAPI_PM}
+ // In case the actor hasn't been destroyed
+ if DSS.ActorThread <> NIL then
+ begin
+ DSS.ActorThread.Send_Message(TActorMessage.EXIT_ACTOR);
+ DSS.ActorThread.WaitFor();
+ DSS.ActorThread.Free();
+ DSS.ActorThread := NIL;
+ end;
+{$ENDIF}
if DSS.DIFilesAreOpen then
DSS.EnergyMeterClass.CloseAllDIFiles;
- {First get rid of all existing stuff}
- ClearAllCircuits(DSS);
+ // First get rid of all existing stuff
+ ClearAllCircuits_SingleContext(DSS);
DisposeDSSClasses(DSS);
+ if Resetting then
+ begin
+ // Now, Start over
+ CreateDSSClasses(DSS);
+ CreateDefaultDSSItems;
+ end;
+ end
+ else if not Resetting then
+ DisposeDSSClasses(DSS);
+
+ if not Resetting then
+ Exit;
- {Now, Start over}
- CreateDSSClasses(DSS);
- CreateDefaultDSSItems;
- End;
-
- DSS.DefaultEarthModel := DERI;
- DSS.LogQueries := FALSE;
+ DSS.DefaultEarthModel := DERI;
+ DSS.LogQueries := FALSE;
DSS.MaxAllocationIterations := 2;
- {Prepare for new variables}
+ // Prepare for new variables
DSS.ParserVars.Free;
DSS.ParserVars := TParserVar.Create(100); // start with space for 100 variables
DSS.Parser.SetVars(DSS.ParserVars);
DSS.AuxParser.SetVars(DSS.ParserVars);
+ DSS.PropParser.SetVars(DSS.ParserVars);
+
+ {$IFNDEF FPC}
+ if not IsDLL then
+ ControlPanel.UpdateElementBox;
+ {$ENDIF}
end;
{$IFDEF DSS_CAPI_PM}
@@ -258,9 +292,7 @@ procedure TExecutive.ClearAll;
PMParent := DSS.GetPrime();
for i := 1 to high(PMParent.Children) do
- begin
PMParent.Children[i].Free;
- end;
SetLength(PMParent.Children, 1);
PMParent.ActiveChildIndex := 0;
PMParent.ActiveChild := PMParent;
@@ -274,13 +306,13 @@ procedure TExecutive.Set_RecorderOn(const Value: Boolean);
Begin
If Not FRecorderOn Then
Begin
- FRecorderFile := DSS.OutputDirectory + 'DSSRecorder.DSS' ;
- RecorderFile := TFileStream.Create(FRecorderFile, fmCreate);
+ FRecorderFile := DSS.OutputDirectory + 'DSSRecorder.dss' ;
+ RecorderFile := TBufferedFileStream.Create(FRecorderFile, fmCreate);
End
else
begin
RecorderFile.Free();
- RecorderFile := TFileStream.Create(FRecorderFile, fmCreate);
+ RecorderFile := TBufferedFileStream.Create(FRecorderFile, fmCreate);
end;
End
Else If FRecorderOn Then
@@ -291,10 +323,256 @@ procedure TExecutive.Set_RecorderOn(const Value: Boolean);
FRecorderOn := Value;
end;
-procedure TExecutive.Write_to_RecorderFile(const s: string);
+procedure TExecutive.Write_to_RecorderFile(const s: String);
begin
FSWriteln(Recorderfile, S);
end;
+function TExecutive.ZipHashes(var Hashes: TFPHashList): Boolean;
+var
+ unzipper: TDSSUnZipper = NIL;
+begin
+ Result := False;
+ if DSS.unzipper = NIL then
+ Exit;
+
+ unzipper := TDSSUnZipper(DSS.unzipper);
+ Hashes := unzipper.ziphash;
+ if Hashes <> NIL then
+ Result := True;
+end;
+
+constructor TDSSUnZipper.Create(fn: String);
+begin
+ //TODO: test on PM scenarios (multiple threads processing the same ZIP).
+ // If it doesn't work, we need to use OnOpenInputStream and replace the
+ // input stream with one of our own.
+ inherited Create;
+ FileName := fn;
+ dataStream := NIL;
+ OnCreateStream := DoCreateOutZipStream;
+ OnDoneStream := DoDoneOutZipStream;
+ ziphash := TFPHashList.Create();
+end;
+
+procedure TDSSUnZipper.PrepareHashmap();
+var
+ i: Integer;
+begin
+ Examine();
+ for i := 0 to Entries.Count - 1 do
+ begin
+ if Length(Entries[i].ArchiveFileName) > 255 then
+ begin
+ raise Exception.Create('ZIP archive contains file names longer than 255 chars. This is currently unsupported.');
+ end;
+ ziphash.Add(Entries[i].ArchiveFileName, Pointer(i + 1));
+ end;
+end;
+
+destructor TDSSUnZipper.Destroy;
+begin
+ ziphash.Free;
+ inherited Destroy;
+end;
+
+function TDSSUnZipper.GetFile(fn: String): TStream;
+var
+ i: Integer;
+begin
+ i := Integer(ziphash.Find(fn)) - 1;
+ Result := NIL;
+ if i < 0 then
+ Exit;
+
+ try
+ OpenInput();
+ UnZipOneFile(Entries[i]);
+ Result := dataStream;
+
+ finally
+ CloseInput();
+ end;
+end;
+
+procedure TDSSUnZipper.DoCreateOutZipStream(sender: TObject; var stream: TStream; it: TFullZipFileEntry);
+begin
+ stream := TMemoryStream.Create;
+ dataStream := stream;
+end;
+
+procedure TDSSUnZipper.DoDoneOutZipStream(sender: TObject; var stream: TStream; it: TFullZipFileEntry);
+begin
+ stream.Position := 0;
+end;
+
+function TExecutive.GetZipStream(fn: String): TStream;
+var
+ unzipper: TDSSUnZipper;
+ cwd: String;
+ efn: String = '';
+begin
+ Result := NIL;
+ try
+ if Length(fn) > 1 then
+ begin
+ // If there's a leading slash, remove it an use the rest directly
+ while (fn[1] = '\') or (fn[1] = '/') do
+ fn := Copy(fn, 2, Length(fn) - 1);
+
+ efn := fn;
+ end
+ else
+ begin
+ // Without a slash, assume relative path to current file (in ZIP)
+ efn := DSS.inZipPath + fn
+ end;
+
+ unzipper := TDSSUnZipper(DSS.unzipper);
+
+ // Try the given path as is
+ try
+ Result := unzipper.GetFile(efn);
+ except
+ on E: Exception do
+ begin
+ end;
+ end;
+
+ // Try to get a simpler version (expand dots, etc.)
+ if Result = NIL then
+ begin
+ efn := ExpandFileName(DSS.inZipPath + fn);
+ cwd := GetCurrentDir() + PathDelim;
+ efn := ExtractRelativePath(cwd, efn);
+ Result := unzipper.GetFile(efn);
+ end;
+
+ // Try switching backslashes<->slashes
+ if Result = NIL then
+ begin
+ efn := ReplaceStr(efn, '/', '\');
+ Result := unzipper.GetFile(efn);
+ if Result = NIL then
+ begin
+ efn := ReplaceStr(efn, '\', '/');
+ Result := unzipper.GetFile(efn);
+ end;
+ end;
+
+ except
+ on E: Exception do
+ begin
+ end;
+ end;
+ if Result = NIL then
+ begin
+ if fn <> efn then
+ raise Exception.Create(Format(_('Could not read file "%s" ("%s") from ZIP "%s".'), [fn, efn, CurrentZipFileName]))
+ else
+ raise Exception.Create(Format(_('Could not read file "%s" from ZIP "%s".'), [fn, CurrentZipFileName]));
+ end;
+end;
+
+procedure TExecutive.SetInZipPath(path: String);
+begin
+ if path = DSS.inZipPath then
+ Exit;
+
+ if Length(Path) <> 0 then
+ begin
+ if (Path[High(Path)] <> '/') and (Path[High(Path)] <> '\') then
+ begin
+ Path := Path + PathDelim;
+ end;
+
+ while (Path[1] = '\') or (Path[1] = '/') do
+ Path := Copy(Path, 2, Length(Path) - 1);
+
+ end;
+ DSS.inZipPath := path;
+end;
+
+procedure TExecutive.ZipOpen(ZipFileName: String);
+var
+ unzipper: TDSSUnZipper = NIL;
+begin
+ if (not DSS_CAPI_ALLOW_CHANGE_DIR) then
+ ZipFileName := ExpandFileName(AdjustInputFilePath(DSS, ZipFileName));
+
+ try
+ unzipper := TDSSUnZipper.Create(ZipFileName);
+ unzipper.PrepareHashmap();
+ except
+ on E: Exception do
+ begin
+ DoSimpleMsg(DSS, 'Error preparing ZIP "%s": %s', [ZipFileName, E.message], 4016);
+ FreeAndNil(unzipper);
+ end;
+ end;
+ if DSS.unzipper <> NIL then
+ DSS.unzipper.Free();
+
+ DSS.unzipper := unzipper;
+end;
+
+procedure TExecutive.ZipClose();
+begin
+ FreeAndNil(DSS.unzipper);
+end;
+
+procedure TExecutive.ZipRedirect(FileInZip: String);
+var
+ u: TDSSUnZipper = NIL;
+begin
+ try
+ u := TDSSUnZipper(DSS.unzipper);
+ u.Enabled := True;
+ SetInZipPath('');
+
+ DSS.Redirect_Abort := False;
+ DSS.SolutionAbort := False;
+
+ // Do the actual redirect to the file, while wrapping streams
+ // and loading inputs from the UnZipper.
+ Set_Command(Format('redirect "%s"', [FileInZip]));
+ except
+ on E: Exception do
+ begin
+ DoSimpleMsg(DSS, 'Error reading script "%s" from ZIP "%s": %s', [FileInZip, CurrentZipFileName(), E.message], 4016);
+ Exit;
+ end;
+ end;
+
+ u.Enabled := False;
+
+ if DSS.ErrorNumber = 0 then
+ Exit;
+
+ DSS.LastErrorMessage := DSS.LastErrorMessage + CRLF +
+ Format(_('[ZIP file: "%s"]'), [CurrentZipFileName()]);
+end;
+
+procedure TExecutive.ZipExtract(var ResultPtr: PByte; ResultCount: PAPISize; FileInZip: String);
+var
+ Fstream: TStream = NIL;
+begin
+ try
+ Fstream := GetZipStream(FileInZip);
+ DSS_RecreateArray_PByte(ResultPtr, ResultCount, Fstream.Size);
+ Fstream.ReadBuffer(ResultPtr^, ResultCount[0]);
+ FreeAndNil(Fstream);
+ except
+ on E: Exception do
+ begin
+ DoSimpleMsg(DSS, 'File "%s" could not be extracted: %s', [FileInZip, E.Message], 2203);
+ DSS.LastErrorMessage := DSS.LastErrorMessage + CRLF +
+ Format(_('[ZIP file: "%s"]'), [CurrentZipFileName()]);
+ FreeAndNil(Fstream);
+ Exit;
+ end;
+ end;
+end;
+
end.
diff --git a/src/Executive/ExportOptions.pas b/src/Executive/ExportOptions.pas
index 9ae474cf3..46929ac94 100644
--- a/src/Executive/ExportOptions.pas
+++ b/src/Executive/ExportOptions.pas
@@ -13,15 +13,84 @@ interface
Command,
DSSClass;
-const
- NumExportOptions = 61;
+type
+{$SCOPEDENUMS ON}
+ TExportOption = (
+ INVALID = 0,
+ Voltages = 1,
+ SeqVoltages = 2,
+ Currents = 3,
+ SeqCurrents = 4,
+ Estimation = 5,
+ Capacity = 6,
+ Overloads = 7,
+ Unserved = 8,
+ Powers = 9,
+ SeqPowers = 10,
+ Faultstudy = 11,
+ Generators = 12,
+ Loads = 13,
+ Meters = 14,
+ Monitors = 15,
+ Yprims = 16,
+ Y = 17,
+ seqz = 18,
+ P_byphase = 19,
+ CIM100Fragments = 20,
+ CIM100 = 21,
+ CDPSMAsset = 22,
+ Buscoords = 23,
+ Losses = 24,
+ Uuids = 25,
+ Counts = 26,
+ Summary = 27,
+ CDPSMElec = 28,
+ CDPSMGeo = 29,
+ CDPSMTopo = 30,
+ CDPSMStateVar = 31,
+ Profile = 32,
+ EventLog = 33,
+ AllocationFactors = 34,
+ VoltagesElements = 35,
+ GICMvars = 36,
+ BusReliability = 37,
+ BranchReliability = 38,
+ NodeNames = 39,
+ Taps = 40,
+ NodeOrder = 41,
+ ElemCurrents = 42,
+ ElemVoltages = 43,
+ ElemPowers = 44,
+ Result = 45,
+ YNodeList = 46,
+ YVoltages = 47,
+ YCurrents = 48,
+ PVSystem_Meters = 49,
+ Storage_Meters = 50,
+ Sections = 51,
+ ErrorLog = 52,
+ IncMatrix = 53,
+ IncMatrixRows = 54,
+ IncMatrixCols = 55,
+ BusLevels = 56,
+ Laplacian = 57
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
+ ,
+ ZLL = 58,
+ ZCC = 59,
+ Contours = 60,
+ Y4 = 61
+{$ENDIF}
+ );
+{$SCOPEDENUMS OFF}
-function DoExportCmd(DSS: TDSSContext): Integer;
+const
+ NumExportOptions = ord(High(TExportOption));
+function DoExportCmd({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext): Integer;
var
- ExportOption,
- ExportHelp: array[1..NumExportOptions] of String;
+ ExportOption: array[1..NumExportOptions] of String;
ExportCommands: TCommandList;
implementation
@@ -36,7 +105,8 @@ implementation
ExportCIMXML,
Utilities,
NamedObject,
- DSSHelper;
+ DSSHelper,
+ TypInfo;
function AssignNewUUID(val: String): TUuid;
begin
@@ -46,145 +116,16 @@ function AssignNewUUID(val: String): TUuid;
end;
procedure DefineOptions;
-
+var
+ info: Pointer;
+ i: Integer;
begin
-
- ExportOption[1] := 'Voltages';
- ExportOption[2] := 'SeqVoltages';
- ExportOption[3] := 'Currents';
- ExportOption[4] := 'SeqCurrents';
- ExportOption[5] := 'Estimation';
- ExportOption[6] := 'Capacity';
- ExportOption[7] := 'Overloads';
- ExportOption[8] := 'Unserved';
- ExportOption[9] := 'Powers';
- ExportOption[10] := 'SeqPowers';
- ExportOption[11] := 'Faultstudy';
- ExportOption[12] := 'Generators';
- ExportOption[13] := 'Loads';
- ExportOption[14] := 'Meters';
- ExportOption[15] := 'Monitors';
- ExportOption[16] := 'Yprims';
- ExportOption[17] := 'Y';
- ExportOption[18] := 'seqz';
- ExportOption[19] := 'P_byphase';
- ExportOption[20] := 'CIM100Fragments';
- ExportOption[21] := 'CIM100';
- ExportOption[22] := 'CDPSMAsset';
- ExportOption[23] := 'Buscoords';
- ExportOption[24] := 'Losses';
- ExportOption[25] := 'Uuids';
- ExportOption[26] := 'Counts';
- ExportOption[27] := 'Summary';
- ExportOption[28] := 'CDPSMElec';
- ExportOption[29] := 'CDPSMGeo';
- ExportOption[30] := 'CDPSMTopo';
- ExportOption[31] := 'CDPSMStateVar';
- ExportOption[32] := 'Profile';
- ExportOption[33] := 'EventLog';
- ExportOption[34] := 'AllocationFactors';
- ExportOption[35] := 'VoltagesElements';
- ExportOption[36] := 'GICMvars';
- ExportOption[37] := 'BusReliability';
- ExportOption[38] := 'BranchReliability';
- ExportOption[39] := 'NodeNames';
- ExportOption[40] := 'Taps';
- ExportOption[41] := 'NodeOrder';
- ExportOption[42] := 'ElemCurrents';
- ExportOption[43] := 'ElemVoltages';
- ExportOption[44] := 'ElemPowers';
- ExportOption[45] := 'Result';
- ExportOption[46] := 'YNodeList';
- ExportOption[47] := 'YVoltages';
- ExportOption[48] := 'YCurrents';
- ExportOption[49] := 'PVSystem_Meters';
- ExportOption[50] := 'Storage_Meters';
- ExportOption[51] := 'Sections';
- ExportOption[52] := 'ErrorLog';
- ExportOption[53] := 'IncMatrix';
- ExportOption[54] := 'IncMatrixRows';
- ExportOption[55] := 'IncMatrixCols';
- ExportOption[56] := 'BusLevels';
- ExportOption[57] := 'Laplacian';
- ExportOption[58] := 'ZLL';
- ExportOption[59] := 'ZCC';
- ExportOption[60] := 'Contours';
- ExportOption[61] := 'Y4';
-
- ExportHelp[1] := '(Default file = EXP_VOLTAGES.CSV) Voltages to ground by bus/node.';
- ExportHelp[2] := '(Default file = EXP_SEQVOLTAGES.CSV) Sequence voltages.';
- ExportHelp[3] := '(Default file = EXP_CURRENTS.CSV) Currents in each conductor of each element.';
- ExportHelp[4] := '(Default file = EXP_SEQCURRENTS.CSV) Sequence currents in each terminal of 3-phase elements.';
- ExportHelp[5] := '(Default file = EXP_ESTIMATION.CSV) Results of last estimation.';
- ExportHelp[6] := '(Default file = EXP_CAPACITY.CSV) Capacity report.';
- ExportHelp[7] := '(Default file = EXP_OVERLOADS.CSV) Overloaded elements report.';
- ExportHelp[8] := '(Default file = EXP_UNSERVED.CSV) [UEonly] [Filename] Report on elements that are unserved due to violation of ratings.';
- ExportHelp[9] := '(Default file = EXP_POWERS.CSV) [MVA] [Filename] Powers (kVA by default) into each terminal of each element.';
- ExportHelp[10] := '(Default file = EXP_SEQPOWERS.CSV) Sequence powers into each terminal of 3-phase elements.';
- ExportHelp[11] := '(Default file = EXP_FAULTS.CSV) results of a fault study.';
- ExportHelp[12] := '(Default file = EXP_GENMETERS.CSV) Present values of generator meters. Adding the switch "/multiple" or "/m" will ' +
- ' cause a separate file to be written for each generator.';
- ExportHelp[13] := '(Default file = EXP_LOADS.CSV) Report on loads from most recent solution.';
- ExportHelp[14] := '(Default file = EXP_METERS.CSV) Energy meter exports. Adding the switch "/multiple" or "/m" will ' +
- ' cause a separate file to be written for each meter.';
- ExportHelp[15] := '(file name is assigned by Monitor export) Monitor values. The argument is the name of the monitor (e.g. Export Monitor XYZ, XYZ is the name of the monitor).' + CRLF +
- 'The argument can be ALL, which means that all the monitors will be exported';
- ExportHelp[16] := '(Default file = EXP_YPRIMS.CSV) All primitive Y matrices.';
- ExportHelp[17] := '(Default file = EXP_Y.CSV) [triplets] [Filename] System Y matrix, defaults to non-sparse format.';
- ExportHelp[18] := '(Default file = EXP_SEQZ.CSV) Equivalent sequence Z1, Z0 to each bus.';
- ExportHelp[19] := '(Default file = EXP_P_BYPHASE.CSV) [MVA] [Filename] Power by phase. Default is kVA.';
- ExportHelp[20] := '(Default file ROOT = CIM100) (IEC 61968-13, CIM100 for unbalanced load flow profile)' + CRLF + ' produces 6 separate files ROOT_FUN.XML for Functional profile,' + CRLF + ' ROOT_EP.XML for Electrical Properties profile,' + CRLF + ' ROOT_TOPO.XML for Topology profile,' + CRLF + ' ROOT_CAT.XML for Asset Catalog profile,' + CRLF + ' ROOT_GEO.XML for Geographical profile and' + CRLF + ' ROOT_SSH.XML for Steady State Hypothesis profile' + CRLF + ' [File=fileroot fid=_uuidstring Substation=subname sid=_uuidstring' + CRLF + ' SubGeographicRegion=subgeoname sgrid=_uuidstring GeographicRegion=geoname rgnid=_uuidstring]';
- ExportHelp[21] := '(Default file = CIM100x.XML) (IEC 61968-13, combined CIM100 for unbalanced load flow profile)' + CRLF + ' [File=filename fid=_uuidstring Substation=subname sid=_uuidstring' + CRLF + ' SubGeographicRegion=subgeoname sgrid=_uuidstring GeographicRegion=geoname rgnid=_uuidstring]';
- ExportHelp[22] := '** Deprecated ** (IEC 61968-13, CDPSM Asset profile)';
- ExportHelp[23] := '[Default file = EXP_BUSCOORDS.CSV] Bus coordinates in csv form.';
- ExportHelp[24] := '[Default file = EXP_LOSSES.CSV] Losses for each element.';
- ExportHelp[25] := '[Default file = EXP_UUIDS.CSV] Uuids for each element. This frees the UUID list after export.';
- ExportHelp[26] := '[Default file = EXP_Counts.CSV] (instance counts for each class)';
- ExportHelp[27] := '[Default file = EXP_Summary.CSV] Solution summary.';
- ExportHelp[28] := '** Deprecated ** (IEC 61968-13, CDPSM Electrical Properties profile)';
- ExportHelp[29] := '** Deprecated ** (IEC 61968-13, CDPSM Geographical profile)';
- ExportHelp[30] := '** Deprecated ** (IEC 61968-13, CDPSM Topology profile)';
- ExportHelp[31] := '** Deprecated ** (IEC 61968-13, CDPSM State Variables profile)';
- ExportHelp[32] := '[Default file = EXP_Profile.CSV] Coordinates, color of each line section in Profile plot. Same options as Plot Profile Phases property.' + CRLF + CRLF +
- 'Example: Export Profile Phases=All [optional file name]';
- ExportHelp[33] := '(Default file = EXP_EventLog.CSV) All entries in the present event log.';
- ExportHelp[34] := 'Exports load allocation factors. File name is assigned.';
- ExportHelp[35] := '(Default file = EXP_VOLTAGES_ELEM.CSV) Voltages to ground by circuit element.';
- ExportHelp[36] := '(Default file = EXP_GIC_Mvar.CSV) Mvar for each GICtransformer object by bus for export to power flow programs ';
- ExportHelp[37] := '(Default file = EXP_BusReliability.CSV) Failure rate, number of interruptions and other reliability data at each bus.';
- ExportHelp[38] := '(Default file = EXP_BranchReliability.CSV) Failure rate, number of interruptions and other reliability data for each PD element.';
- ExportHelp[39] := '(Default file = EXP_NodeNames.CSV) Exports Single-column file of all node names in the active circuit. Useful for making scripts.';
- ExportHelp[40] := '(Default file = EXP_Taps.CSV) Exports the regulator tap report similar to Show Taps.';
- ExportHelp[41] := '(Default file = EXP_NodeOrder.CSV) Exports the present node order for all conductors of all circuit elements';
- ExportHelp[42] := '(Default file = EXP_ElemCurrents.CSV) Exports the current into all conductors of all circuit elements';
- ExportHelp[43] := '(Default file = EXP_ElemVoltages.CSV) Exports the voltages to ground at all conductors of all circuit elements';
- ExportHelp[44] := '(Default file = EXP_elemPowers.CSV) Exports the powers into all conductors of all circuit elements';
- ExportHelp[45] := '(Default file = EXP_Result.CSV) Exports the result of the most recent command.';
- ExportHelp[46] := '(Default file = EXP_YNodeList.CSV) Exports a list of nodes in the same order as the System Y matrix.';
- ExportHelp[47] := '(Default file = EXP_YVoltages.CSV) Exports the present solution complex Voltage array in same order as YNodeList.';
- ExportHelp[48] := '(Default file = EXP_YCurrents.CSV) Exports the present solution complex Current array in same order as YNodeList. This is generally the injection current array';
- ExportHelp[49] := '(Default file = EXP_PVMETERS.CSV) Present values of PVSystem meters. Adding the switch "/multiple" or "/m" will ' +
- ' cause a separate file to be written for each PVSystem.';
- ExportHelp[50] := '(Default file = EXP_STORAGEMETERS.CSV) Present values of Storage meters. Adding the switch "/multiple" or "/m" will ' +
- ' cause a separate file to be written for each Storage device.';
- ExportHelp[51] := '(Default file = EXP_SECTIONS.CSV) Data for each section between overcurrent protection devices. ' + CRLF + CRLF +
- 'Examples: ' + CRLF + ' Export Sections [optional filename]' + CRLF + 'Export Sections meter=M1 [optional filename]';
- ExportHelp[52] := '(Default file = EXP_ErrorLog.TXT) All entries in the present Error log.';
- ExportHelp[53] := 'Exports the Branch-to-Node Incidence matrix calculated for the circuit in compressed coordianted format (Row,Col,Value)';
- ExportHelp[54] := 'Exports the names of the rows (PDElements) used for calculating the Branch-to-Node Incidence matrix for the active circuit';
- ExportHelp[55] := 'Exports the names of the Cols (Buses) used for calculating the Branch-to-Node Incidence matrix for the active circuit';
- ExportHelp[56] := 'Exports the names and the level of each Bus inside the Circuit based on its topology information. The level value defines' +
- 'how far or close is the bus from the circuits backbone (0 means that the bus is at the backbone)';
- ExportHelp[57] := 'Exports the Laplacian matrix calculated using the branch-to-node Incidence matrix in compressed coordinated format (Row,Col,Value)';
- ExportHelp[58] := 'Exports the Link branches matrix (ZLL) calculated after initilizing A-Diakoptics. The output format is compressed coordianted and the values are complex conjugates. If A-Diakoptics is not initialized this command does nothing';
- ExportHelp[59] := 'Exports the connectivity matrix (ZCC) calculated after initilizing A-Diakoptics. The output format is compressed coordianted and the values are complex conjugates. If A-Diakoptics is not initialized this command does nothing';
- ExportHelp[60] := 'Exports the Contours matrix (C) calculated after initilizing A-Diakoptics. The output format is compressed coordianted and the values are integers. If A-Diakoptics is not initialized this command does nothing';
- ExportHelp[61] := 'Exports the inverse of Z4 (ZCC) calculated after initilizing A-Diakoptics. The output format is compressed coordianted and the values are complex conjugates. If A-Diakoptics is not initialized this command does nothing';
+ info := TypeInfo(TExportOption);
+ for i := 1 to NumExportOptions do
+ ExportOption[i] := GetEnumName(info, i);
end;
-//----------------------------------------------------------------------------
-function DoExportCmd(DSS: TDSSContext): Integer;
-
+function DoExportCmd({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext): Integer;
var
ParamName,
Parm1,
@@ -202,9 +143,10 @@ function DoExportCmd(DSS: TDSSContext): Integer;
FdrUuid, SubUuid, SubGeoUuid, RgnUuid: TUuid; // for CIM export
{$IFDEF DSS_CAPI_PM}
InitP, FinalP, idxP: Integer;
- PMParent: TDSSContext;
+ PMParent, DSS: TDSSContext;
begin
- PMParent := DSS.GetPrime();
+ PMParent := MainDSS.GetPrime();
+ DSS := MainDSS.ActiveChild;
{$ELSE}
begin
{$ENDIF}
@@ -215,7 +157,7 @@ function DoExportCmd(DSS: TDSSContext): Integer;
Parm2 := '';
ParamName := DSS.Parser.NextParam;
- Parm1 := LowerCase(DSS.Parser.StrValue);
+ Parm1 := AnsiLowerCase(DSS.Parser.StrValue);
ParamPointer := ExportCommands.Getcommand(Parm1);
{Check commands requiring a solution and abort if no solution or circuit}
@@ -250,7 +192,7 @@ function DoExportCmd(DSS: TDSSContext): Integer;
9, 19:
begin { Trap export powers command and look for MVA/kVA option }
ParamName := DSS.Parser.nextParam;
- Parm2 := LowerCase(DSS.Parser.strvalue);
+ Parm2 := AnsiLowerCase(DSS.Parser.strvalue);
MVAOpt := 0;
if Length(Parm2) > 0 then
if Parm2[1] = 'm' then
@@ -260,7 +202,7 @@ function DoExportCmd(DSS: TDSSContext): Integer;
8:
begin { Trap UE only flag }
ParamName := DSS.Parser.nextParam;
- Parm2 := LowerCase(DSS.Parser.strvalue);
+ Parm2 := AnsiLowerCase(DSS.Parser.strvalue);
UEonlyOpt := FALSE;
if Length(Parm2) > 0 then
if Parm2[1] = 'u' then
@@ -276,7 +218,7 @@ function DoExportCmd(DSS: TDSSContext): Integer;
17:
begin { Trap Sparse Triplet flag }
ParamName := DSS.Parser.nextParam;
- Parm2 := LowerCase(DSS.Parser.strvalue);
+ Parm2 := AnsiLowerCase(DSS.Parser.strvalue);
TripletOpt := FALSE;
if Length(Parm2) > 0 then
if Parm2[1] = 't' then
@@ -285,7 +227,7 @@ function DoExportCmd(DSS: TDSSContext): Integer;
20, 21:
begin {user-supplied substation and regions}
- ParamName := LowerCase(DSS.Parser.nextParam);
+ ParamName := AnsiLowerCase(DSS.Parser.nextParam);
Parm2 := DSS.Parser.strValue;
while Length(ParamName) > 0 do
begin
@@ -312,7 +254,7 @@ function DoExportCmd(DSS: TDSSContext): Integer;
else
if CompareTextShortest(ParamName, 'rg') = 0 then
RgnUuid := AssignNewUUID(Parm2);
- ParamName := LowerCase(DSS.Parser.nextParam);
+ ParamName := AnsiLowerCase(DSS.Parser.nextParam);
Parm2 := DSS.Parser.strValue;
end;
end;
@@ -360,7 +302,7 @@ function DoExportCmd(DSS: TDSSContext): Integer;
if Length(FileName) = 0 then
begin
ParamName := DSS.Parser.NextParam;
- FileName := LowerCase(DSS.Parser.StrValue); // should be full path name to work universally
+ FileName := AnsiLowerCase(DSS.Parser.StrValue); // should be full path name to work universally
end;
DSS.InShowResults := TRUE;
@@ -370,42 +312,42 @@ function DoExportCmd(DSS: TDSSContext): Integer;
begin
case ParamPointer of
1:
- FileName := 'EXP_VOLTAGES.CSV';
+ FileName := 'EXP_VOLTAGES.csv';
2:
- FileName := 'EXP_SEQVOLTAGES.CSV';
+ FileName := 'EXP_SEQVOLTAGES.csv';
3:
- FileName := 'EXP_CURRENTS.CSV';
+ FileName := 'EXP_CURRENTS.csv';
4:
- FileName := 'EXP_SEQCURRENTS.CSV';
+ FileName := 'EXP_SEQCURRENTS.csv';
5:
- FileName := 'EXP_ESTIMATION.CSV'; // Estimation error
+ FileName := 'EXP_ESTIMATION.csv'; // Estimation error
6:
- FileName := 'EXP_CAPACITY.CSV';
+ FileName := 'EXP_CAPACITY.csv';
7:
- FileName := 'EXP_OVERLOADS.CSV';
+ FileName := 'EXP_OVERLOADS.csv';
8:
- FileName := 'EXP_UNSERVED.CSV';
+ FileName := 'EXP_UNSERVED.csv';
9:
- FileName := 'EXP_POWERS.CSV';
+ FileName := 'EXP_POWERS.csv';
10:
- FileName := 'EXP_SEQPOWERS.CSV';
+ FileName := 'EXP_SEQPOWERS.csv';
11:
- FileName := 'EXP_FAULTS.CSV';
+ FileName := 'EXP_FAULTS.csv';
12:
- FileName := 'EXP_GENMETERS.CSV';
+ FileName := 'EXP_GENMETERS.csv';
13:
- FileName := 'EXP_LOADS.CSV';
+ FileName := 'EXP_LOADS.csv';
14:
- FileName := 'EXP_METERS.CSV';
+ FileName := 'EXP_METERS.csv';
{15: Filename is assigned}
16:
- Filename := 'EXP_YPRIM.CSV';
+ Filename := 'EXP_YPRIM.csv';
17:
- Filename := 'EXP_Y.CSV';
+ Filename := 'EXP_Y.csv';
18:
- Filename := 'EXP_SEQZ.CSV';
+ Filename := 'EXP_SEQZ.csv';
19:
- Filename := 'EXP_P_BYPHASE.CSV';
+ Filename := 'EXP_P_BYPHASE.csv';
20:
FileName := 'CIM100.XML';
21:
@@ -413,15 +355,15 @@ function DoExportCmd(DSS: TDSSContext): Integer;
22:
FileName := '';
23:
- FileName := 'EXP_BUSCOORDS.CSV';
+ FileName := 'EXP_BUSCOORDS.csv';
24:
- FileName := 'EXP_LOSSES.CSV';
+ FileName := 'EXP_LOSSES.csv';
25:
- FileName := 'EXP_UUIDS.CSV';
+ FileName := 'EXP_UUIDS.csv';
26:
- FileName := 'EXP_Counts.CSV';
+ FileName := 'EXP_Counts.csv';
27:
- FileName := 'EXP_Summary.CSV';
+ FileName := 'EXP_Summary.csv';
28:
FileName := '';
29:
@@ -431,45 +373,45 @@ function DoExportCmd(DSS: TDSSContext): Integer;
31:
FileName := '';
32:
- FileName := 'EXP_Profile.CSV';
+ FileName := 'EXP_Profile.csv';
33:
- FileName := 'EXP_EventLog.CSV';
+ FileName := 'EXP_EventLog.csv';
34:
- FileName := 'AllocationFactors.Txt';
+ FileName := 'AllocationFactors.txt';
35:
- FileName := 'EXP_VOLTAGES_ELEM.CSV';
+ FileName := 'EXP_VOLTAGES_ELEM.csv';
36:
- FileName := 'EXP_GIC_Mvar.CSV';
+ FileName := 'EXP_GIC_Mvar.csv';
37:
- FileName := 'EXP_BusReliability.CSV';
+ FileName := 'EXP_BusReliability.csv';
38:
- FileName := 'EXP_BranchReliability.CSV';
+ FileName := 'EXP_BranchReliability.csv';
39:
- FileName := 'EXP_NodeNames.CSV';
+ FileName := 'EXP_NodeNames.csv';
40:
- FileName := 'EXP_Taps.CSV';
+ FileName := 'EXP_Taps.csv';
41:
- FileName := 'EXP_NodeOrder.CSV';
+ FileName := 'EXP_NodeOrder.csv';
42:
- FileName := 'EXP_ElemCurrents.CSV';
+ FileName := 'EXP_ElemCurrents.csv';
43:
- FileName := 'EXP_ElemVoltages.CSV';
+ FileName := 'EXP_ElemVoltages.csv';
44:
- FileName := 'EXP_ElemPowers.CSV';
+ FileName := 'EXP_ElemPowers.csv';
45:
- FileName := 'EXP_Result.CSV';
+ FileName := 'EXP_Result.csv';
46:
- FileName := 'EXP_YNodeList.CSV';
+ FileName := 'EXP_YNodeList.csv';
47:
- FileName := 'EXP_YVoltages.CSV';
+ FileName := 'EXP_YVoltages.csv';
48:
- FileName := 'EXP_YCurrents.CSV';
+ FileName := 'EXP_YCurrents.csv';
49:
- FileName := 'EXP_PVMeters.CSV';
+ FileName := 'EXP_PVMeters.csv';
50:
- FileName := 'EXP_STORAGEMeters.CSV';
+ FileName := 'EXP_STORAGEMeters.csv';
51:
- FileName := 'EXP_SECTIONS.CSV';
+ FileName := 'EXP_SECTIONS.csv';
52:
FileName := 'EXP_ErrorLog.txt';
53:
@@ -482,6 +424,7 @@ function DoExportCmd(DSS: TDSSContext): Integer;
FileName := 'Bus_Levels.csv';
57:
FileName := 'Laplacian.csv';
+{$IFDEF DSS_CAPI_PM}
58:
FileName := 'ZLL.csv';
59:
@@ -490,9 +433,9 @@ function DoExportCmd(DSS: TDSSContext): Integer;
FileName := 'C.csv';
61:
FileName := 'Y4.csv';
-
+{$ENDIF}
else
- FileName := 'EXP_VOLTAGES.CSV'; // default
+ FileName := 'EXP_VOLTAGES.csv'; // default
end;
FileName := DSS.OutputDirectory + DSS.CircuitName_ + FileName; // Explicitly define directory
end;
@@ -528,7 +471,7 @@ function DoExportCmd(DSS: TDSSContext): Integer;
ExportMeters(DSS, FileName);
15:
if Length(Parm2) = 0 then
- DoSimpleMsg(DSS, 'Monitor Name Not Specified.' + CRLF + DSS.Parser.CmdString, 251)
+ DoSimpleMsg(DSS, 'Monitor name not specified. %s', [CRLF + DSS.Parser.CmdString], 251)
else
begin
{$IFDEF DSS_CAPI_PM}
@@ -557,7 +500,7 @@ function DoExportCmd(DSS: TDSSContext): Integer;
FileName := DSS.GlobalResult;
end
else
- DoSimpleMsg(DSS, 'Monitor "' + Parm2 + '" not found.' + CRLF + DSS.Parser.CmdString, 250);
+ DoSimpleMsg(DSS, 'Monitor "%s" not found. %s', [Parm2, CRLF + DSS.Parser.CmdString], 250);
end;
{$IFDEF DSS_CAPI_PM}
end
@@ -589,7 +532,7 @@ function DoExportCmd(DSS: TDSSContext): Integer;
FileName := DSS.GlobalResult;
end
else
- DoSimpleMsg(DSS, 'Monitor "' + Parm2 + '" not found.' + CRLF + DSS.Parser.CmdString, 250);
+ DoSimpleMsg(DSS, 'Monitor "%s" not found. %s', [Parm2, CRLF + DSS.Parser.CmdString], 250);
end;
end;
end;
@@ -608,7 +551,7 @@ function DoExportCmd(DSS: TDSSContext): Integer;
21:
DSS.CIMExporter.ExportCDPSM(Filename, Substation, SubGeographicRegion, GeographicRegion, FdrUuid, SubUuid, SubGeoUuid, RgnUuid, TRUE);
22:
- DoSimpleMsg(DSS, 'Asset export no longer supported; use Export CIM100', 252);
+ DoSimpleMsg(DSS, _('Asset export no longer supported; use Export CIM100'), 252);
23:
ExportBusCoords(DSS, Filename);
24:
@@ -620,13 +563,13 @@ function DoExportCmd(DSS: TDSSContext): Integer;
27:
ExportSummary(DSS, Filename);
28:
- DoSimpleMsg(DSS, 'ElectricalProperties export no longer supported; use Export CIM100', 252);
+ DoSimpleMsg(DSS, _('ElectricalProperties export no longer supported; use Export CIM100'), 252);
29:
- DoSimpleMsg(DSS, 'Geographical export no longer supported; use Export CIM100', 252);
+ DoSimpleMsg(DSS, _('Geographical export no longer supported; use Export CIM100'), 252);
30:
- DoSimpleMsg(DSS, 'Topology export no longer supported; use Export CIM100', 252);
+ DoSimpleMsg(DSS, _('Topology export no longer supported; use Export CIM100'), 252);
31:
- DoSimpleMsg(DSS, 'StateVariables export no longer supported; use Export CIM100', 252);
+ DoSimpleMsg(DSS, _('StateVariables export no longer supported; use Export CIM100'), 252);
32:
ExportProfile(DSS, FileName, PhasesToPlot);
33:
@@ -682,6 +625,7 @@ function DoExportCmd(DSS: TDSSContext): Integer;
ExportBusLevels(DSS, Filename);
57:
ExportLaplacian(DSS, FileName);
+{$IFDEF DSS_CAPI_ADIAKOPTICS}
58:
ExportZLL(DSS, Filename);
59:
@@ -690,9 +634,10 @@ function DoExportCmd(DSS: TDSSContext): Integer;
ExportC(DSS, Filename);
61:
ExportY4(DSS, FileName);
+{$ENDIF}
else
// ExportVoltages(DSS, Filename); // default
- DoSimpleMsg(DSS, 'Error: Unknown Export command: "' + parm1 + '"', 24713);
+ DoSimpleMsg(DSS, 'Error: Unknown Export command: "%s"', [parm1], 24713);
AbortExport := TRUE;
end;
@@ -706,7 +651,6 @@ function DoExportCmd(DSS: TDSSContext): Integer;
if DSS.AutoShowExport then
FireOffEditor(DSS, FileName);
end;
-
end;
@@ -718,17 +662,12 @@ procedure DisposeStrings;
for i := 1 to NumExportOptions do
begin
ExportOption[i] := '';
- ExportHelp[i] := '';
end;
-
end;
initialization
-
DefineOptions;
-
- ExportCommands := TCommandList.Create(ExportOption);
- ExportCommands.Abbrev := TRUE;
+ ExportCommands := TCommandList.Create(ExportOption, True);
finalization
diff --git a/src/Executive/PlotOptions.pas b/src/Executive/PlotOptions.pas
index 0412f7497..adf01d413 100644
--- a/src/Executive/PlotOptions.pas
+++ b/src/Executive/PlotOptions.pas
@@ -13,18 +13,44 @@ interface
Command,
DSSClass;
+type
+{$SCOPEDENUMS ON}
+ TPlotOption = (
+ INVALID = 0,
+ typ = 1, // type
+ quantity = 2,
+ max = 3,
+ dots = 4,
+ labels = 5,
+ obj = 6, // object
+ showloops = 7,
+ r3 = 8,
+ r2 = 9,
+ c1 = 10,
+ c2 = 11,
+ c3 = 12,
+ channels = 13,
+ bases = 14,
+ subs = 15,
+ thickness = 16,
+ buslist = 17,
+ min = 18,
+ __3phLinestyle = 19,
+ __1phLinestyle = 20,
+ phases = 21,
+ profilescale = 22,
+ PlotID = 23
+ );
+{$SCOPEDENUMS OFF}
const
- NumPlotOptions = 23;
+ NumPlotOptions = ord(High(TPlotOption));
function DoPlotCmd(DSS: TDSSContext): Integer;
var
-
- PlotOption,
- PlotHelp: array[1..NumPlotOptions] of String;
+ PlotOption: array[1..NumPlotOptions] of String;
PlotCommands: TCommandList;
-
implementation
uses
@@ -36,9 +62,13 @@ implementation
ParserDel,
Utilities,
Circuit,
+ StrUtils,
+ TypInfo,
DSSHelper;
type
+ Opt = TPlotOption;
+
TDSSPlot = class(TObject)
PUBLIC
PlotType: String;
@@ -59,32 +89,19 @@ TDSSPlot = class(TObject)
Color1, Color2, Color3: Integer;
- { Tri-color plots }
+ // Tri-color plots
TriColorMax, TriColorMid: Double;
MaxScaleIsSpecified: Boolean;
MinScaleIsSpecified: Boolean;
-
DaisyBusList: TStringList;
-
MaxLineThickness: Integer;
+ SinglePhLineStyle: Integer;
+ ThreePhLineStyle: Integer;
constructor Create;
end;
-const
- vizCURRENT = 1;
- vizVOLTAGE = 2;
- vizPOWER = 3;
- clBlue = $FF0000;
- clGreen = $008000;
- clRed = $0000FF;
-
-var
- SinglePhLineStyle: Integer = 1;
- ThreePhLineStyle: Integer = 1;
-
-
constructor TDSSPlot.Create;
begin
MaxScale := 0.0; // Find MaxScale
@@ -129,159 +146,41 @@ constructor TDSSPlot.Create;
ThreePhLineStyle := 1;
SinglePhLineStyle := 1;
DaisyBusList := TStringList.Create;
- { Initialize Plotting DLL }
PhasesToPlot := PROFILE3PH;
ProfileScale := PROFILEPUKM;
end;
procedure DefineOptions;
-
+var
+ info: Pointer;
+ i: Integer;
+ name: String;
begin
+ info := TypeInfo(Opt);
+ for i := 1 to NumPlotOptions do
+ begin
+ name := ReplaceStr(GetEnumName(info, i), '__', '');
+ if name = 'typ' then
+ name := name + 'e'
+ else if name = 'obj' then
+ name := 'object';
-
- PlotOption[1] := 'type';
- PlotOption[2] := 'quantity';
- PlotOption[3] := 'max';
- PlotOption[4] := 'dots';
- PlotOption[5] := 'labels';
- PlotOption[6] := 'object';
- PlotOption[7] := 'showloops';
- PlotOption[8] := 'r3';
- PlotOption[9] := 'r2';
- PlotOption[10] := 'c1';
- PlotOption[11] := 'c2';
- PlotOption[12] := 'c3';
- PlotOption[13] := 'channels';
- PlotOption[14] := 'bases';
- PlotOption[15] := 'subs';
- PlotOption[16] := 'thickness';
- PlotOption[17] := 'buslist';
- PlotOption[18] := 'min';
- PlotOption[19] := '3phLinestyle';
- PlotOption[20] := '1phLinestyle';
- PlotOption[21] := 'phases';
- PlotOption[22] := 'profilescale';
- PlotOption[23] := 'PlotID';
-
-
- PlotHelp[1] := 'One of {Circuit | Monitor | Daisy | Zones | AutoAdd | ' +
- 'General (bus data) | Loadshape | Tshape | Priceshape |Profile} ' + CRLF +
- 'A "Daisy" plot is a special circuit plot that places a marker at each Generator location ' +
- 'or at buses in the BusList property, if defined. ' +
- 'A Zones plot shows the meter zones (see help on Object). ' +
- 'Autoadd shows the autoadded generators. General plot shows quantities associated with buses ' +
- 'using gradient colors between C1 and C2. Values are read from a file (see Object). ' +
- 'Loadshape plots the specified loadshape. Examples:' + CRLF + CRLF +
- 'Plot type=circuit quantity=power' + CRLF +
- 'Plot Circuit Losses 1phlinestyle=3' + CRLF +
- 'Plot Circuit quantity=3 object=mybranchdata.csv' + CRLF +
- 'Plot daisy power max=5000 dots=N Buslist=[file=MyBusList.txt]' + CRLF +
- 'Plot General quantity=1 object=mybusdata.csv' + CRLF +
- 'Plot Loadshape object=myloadshape' + CRLF +
- 'Plot Tshape object=mytemperatureshape' + CRLF +
- 'Plot Priceshape object=mypriceshape' + CRLF +
- 'Plot Profile' + CRLF +
- 'Plot Profile Phases=Primary' + CRLF + CRLF +
- 'Additional plots with the OpenDSS Viewer (These plots are enabled with the "OpenDSSViewer" option):' + CRLF +
- '- Plot evolution ! Probabilistic density evolution plot with the line-to-ground magnitude of all load voltages in per unit base.' + CRLF +
- '- Plot energymeter object=system ! System energy meter plot. The "DemandInterval" option is required.' + CRLF +
- '- Plot energymeter object=Totals ! Totals energy meter plot. The "DemandInterval" option is required.' + CRLF +
- '- Plot energymeter object=voltexception ! Voltage exception plot. The "DemandInterval" and "VoltExceptionReport" options are required.' + CRLF +
- '- Plot energymeter object=overloads ! Overload report plot. The "DemandInterval" and "OverloadReport" options are required.' + CRLF +
- '- Plot energymeter object=myMeter ! Energy meter plot. The "DemandInterval" and "DIVerbose" options are required.' + CRLF +
- '- Plot loadshape object=myLoadshape ! Loadshapes with the OpenDSS Viewer functionalities.' + CRLF +
- '- Plot matrix incidence ! Incidence matrix plot (Requires: CalcIncMatrix or CalcIncMatrix_O).' + CRLF +
- '- Plot matrix laplacian ! Laplacian matrix plot (Requires: CalcLaplacian).' + CRLF +
- '- Plot monitor object=myMonitor ! Monitors with the OpenDSS Viewer functionalities. All channels are included in this plot.' + CRLF +
- '- Plot phasevoltage object=myMeter ! Phase voltage plot associated to an energy meter. The "DemandInterval", "DIVerbose" options and the "PhaseVoltageReport" parameter are required.' + CRLF +
- '- Plot profile ! 3D and 2D versions of the voltage profile.' + CRLF +
- '- Plot scatter ! Scatter plot with geovisualization of line-to-ground bus voltage magnitudes in per unit.';
- PlotHelp[2] := 'One of {Voltage | Current | Power | Losses | Capacity | (Value Index for General, AutoAdd, or Circuit[w/ file]) }';
- PlotHelp[3] := 'Enter 0 (the default value) or the value corresponding to max scale or line thickness in the circuit plots. ' +
- 'Power and Losses in kW. Also, use this to specify the max value corresponding to color C2 in General plots.';
- PlotHelp[4] := 'Yes or No*. Places a marker on the circuit plot at the bus location. See Set Markercode under options.';
- PlotHelp[5] := 'Yes or No*. If yes, bus labels (abbreviated) are printed on the circuit plot.';
- PlotHelp[6] := 'Object to be plotted. One of [Meter Name (zones plot) | Monitor Name | LoadShape Name | File Name for General bus data | File Name Circuit branch data]';
- PlotHelp[7] := '{Yes | No*} Shows loops on Circuit plot. Requires an EnergyMeter to be defined.';
- PlotHelp[8] := 'pu value for tri-color plot max range [default=.85 of max scale]. Corresponds to color C3.';
- PlotHelp[9] := 'pu value for tri-color plot mid range [default=.50 of max scale]. Corresponds to color C2.';
- PlotHelp[10] := 'RGB color number or standard color name for color C1. This is the default color for circuit plots. Default is blue. See options in the Plot menu.' + CRLF + CRLF +
- 'Standard color names are: ' + CRLF + CRLF +
- ' Black ' + CRLF +
- ' Maroon ' + CRLF +
- ' Green ' + CRLF +
- ' Olive ' + CRLF +
- ' Navy ' + CRLF +
- ' Purple ' + CRLF +
- ' Teal ' + CRLF +
- ' Gray ' + CRLF +
- ' Silver ' + CRLF +
- ' Red ' + CRLF +
- ' Lime ' + CRLF +
- ' Yellow ' + CRLF +
- ' Blue ' + CRLF +
- ' Fuchsia' + CRLF +
- ' Aqua ' + CRLF +
- ' LtGray ' + CRLF +
- ' DkGray ' + CRLF +
- ' White ';
- PlotHelp[11] := 'RGB color number or standard color name for color C2. Used for gradients and tricolor plots such as circuit voltage.' + CRLF + CRLF +
- 'See Help on C1 for list of standard color names.';
- PlotHelp[12] := 'RGB color number or standard color name for color C3. Used for gradients and tricolor plots such a circuit voltage.' + CRLF + CRLF +
- 'See Help on C1 for list of standard color names.';
- PlotHelp[13] := 'Array of channel numbers for monitor plot. Example' + CRLF + CRLF +
- 'Plot Type=Monitor Object=MyMonitor Channels=[1, 3, 5]' + CRLF + CRLF +
- 'Do "Show Monitor MyMonitor" to see channel definitions.';
- PlotHelp[14] := 'Array of base values for each channel for monitor plot. Useful for creating per unit plots. Default is 1.0 for each channel. Set Base= property after defining channels.' + CRLF + CRLF +
- 'Plot Type=Monitor Object=MyMonitor Channels=[1, 3, 5] Bases=[2400 2400 2400]' + CRLF + CRLF +
- 'Do "Show Monitor MyMonitor" to see channel range and definitions.';
- ;
- PlotHelp[15] := '{Yes | No*} Displays a marker at each transformer declared to be a substation. ' +
- 'At least one bus coordinate must be defined for the transformer. ' +
- 'See MarkTransformer and TransMarkerCode options.';
- PlotHelp[16] := 'Max thickness allowed for lines in circuit plots (default=7).';
- PlotHelp[17] := '{Array of Bus Names | File=filename } This is for the Daisy plot. ' + CRLF + CRLF +
- 'Plot daisy power max=5000 dots=N Buslist=[file=MyBusList.txt]' + CRLF + CRLF +
- 'A "daisy" marker is plotted for ' +
- 'each bus in the list. Bus name may be repeated, which results in multiple markers distributed around the bus location. ' +
- 'This gives the appearance of a daisy if there are several symbols at a bus. Not needed for plotting active generators.';
- PlotHelp[18] := 'Enter 0 (the default value) or the value corresponding to min value corresponding to color C1 in General bus data plots.';
- PlotHelp[19] := 'Line style for drawing 3-phase lines. A number in the range of [1..7].Default is 1 (solid). Use 3 for dotted; 2 for dashed.';
- PlotHelp[20] := 'Line style for drawing 1-phase lines. A number in the range of [1..7].Default is 1 (solid). Use 3 for dotted; 2 for dashed.';
- PlotHelp[21] := '{default* | ALL | PRIMARY | LL3ph | LLALL | LLPRIMARY | (phase number)} For Profile plot. Specify which phases you want plotted.' + CRLF + CRLF +
- 'default = plot only nodes 1-3 at 3-phase buses (default)' + CRLF +
- 'ALL = plot all nodes' + CRLF +
- 'PRIMARY = plot all nodes -- primary only (voltage > 1kV)' + CRLF +
- 'LL3ph = 3-ph buses only -- L-L voltages)' + CRLF +
- 'LLALL = plot all nodes -- L-L voltages)' + CRLF +
- 'LLPRIMARY = plot all nodes -- L-L voltages primary only)' + CRLF +
- '(phase number) = plot all nodes on selected phase' + CRLF + CRLF +
- 'Note: Only nodes downline from an energy meter are plotted.';
- PlotHelp[22] := 'PUKM | 120KFT, default is PUKM' + CRLF +
- 'PUKM = per-unit voltage vs. distance in km' + CRLF +
- '120KFT = voltage on 120-V base vs. distance in kft.';
- PlotHelp[23] := 'Plot identifier for dynamic updates of "profile" and "scatter" plots in the OpenDSS Viewer (See "plot type" for more details).' +
- 'When multiple "plot" commands are executed with the same PlotID, the same figure will be updated with the most recent simulation results.' + CRLF + CRLF +
- 'This identifier could be declared as an integer number or a string without spaces.' + CRLF + CRLF +
- 'Example:' + CRLF + CRLF +
- 'set OpenDSSViewer=true ! OpenDSS Viewer enabled' + CRLF +
- 'solve' + CRLF +
- 'plot scatter PlotID=plotA !Generates a new scatter plot' + CRLF +
- 'solve' + CRLF +
- 'plot scatter PlotID=plotB !Generates a new scatter plot' + CRLF +
- 'solve' + CRLF +
- 'plot scatter PlotID=plotA !Updates the data in plotA' + CRLF +
- 'solve' + CRLF +
- 'plot scatter PlotID=plotA !Updates the data in plotA' + CRLF;
-
+ PlotOption[i] := name;
+ end;
end;
+function ColorToHTML(const c: Integer): String;
+begin
+ Result := (
+ '#' +
+ IntToHex((c and clRed), 2) +
+ IntToHex((c and clLime) shr 8, 2) +
+ IntToHex((c and clBlue) shr 16, 2)
+ );
+end;
-//----------------------------------------------------------------------------
function DoPlotCmd(DSS: TDSSContext): Integer;
-{
- Parse plot options and feed the callback function, if any
-}
+// Parse plot options and feed the callback function, if any
var
ParamName, Param: String;
ParamPointer, i: Integer;
@@ -293,7 +192,7 @@ function DoPlotCmd(DSS: TDSSContext): Integer;
jsonBases: TJSONArray = NIL;
jsonChannels: TJSONArray = NIL;
plotParamsStr: String;
- Parser: TParser;
+ Parser: TDSSParser;
ActiveCircuit: TDSSCircuit;
begin
Result := 0;
@@ -315,19 +214,19 @@ function DoPlotCmd(DSS: TDSSContext): Integer;
ActiveCircuit := DSS.ActiveCircuit;
DSSPlotObj := TDSSPlot.Create;
- {Get next parameter on command line}
+ // Get next parameter on command line
ParamPointer := 0;
- ParamName := Uppercase(Parser.NextParam);
- Param := Uppercase(Parser.StrValue);
+ ParamName := AnsiUpperCase(Parser.NextParam);
+ Param := AnsiUpperCase(Parser.StrValue);
while Length(Param) > 0 do
begin
- {Interpret Parameter}
+ // Interpret Parameter
if (Length(ParamName) = 0) then
Inc(ParamPointer)
else
ParamPointer := PlotCommands.Getcommand(ParamName);
- {Check options requiring a solution and abort if no solution or circuit}
+ // Check options requiring a solution and abort if no solution or circuit
case ParamPointer of
1:
case Param[1] of
@@ -336,19 +235,18 @@ function DoPlotCmd(DSS: TDSSContext): Integer;
begin
if not assigned(ActiveCircuit) then
begin
- DoSimpleMsg(DSS, 'No circuit created.', 24731);
+ DoSimpleMsg(DSS, _('No circuit created.'), 24731);
Exit;
end;
if not assigned(ActiveCircuit.Solution) or not assigned(ActiveCircuit.Solution.NodeV) then
begin
- DoSimpleMsg(DSS, 'The circuit must be solved before you can do this.', 24732);
+ DoSimpleMsg(DSS, _('The circuit must be solved before you can do this.'), 24732);
Exit;
end;
end;
end;
end;
-
with DSSPlotObj do
case ParamPointer of
@@ -357,7 +255,7 @@ function DoPlotCmd(DSS: TDSSContext): Integer;
'A':
begin
PlotType := 'AutoAddLogPlot';
- ObjectName := DSS.CircuitName_ + 'AutoAddLog.CSV';
+ ObjectName := DSS.CircuitName_ + 'AutoAddLog.csv';
ValueIndex := 2;
end;
'C':
@@ -460,7 +358,8 @@ function DoPlotCmd(DSS: TDSSContext): Integer;
12:
Color3 := InterpretColorName(DSS, Param);
13:
- begin {Channel definitions for Plot Monitor}
+ begin
+ // Channel definitions for Plot Monitor
NumChannels := Parser.ParseAsVector(51, PDoubleArray(@DblBuffer)); // allow up to 50 channels
if NumChannels > 0 then
begin // Else take the defaults
@@ -488,18 +387,16 @@ function DoPlotCmd(DSS: TDSSContext): Integer;
if Parser.IntValue > 0 then
MaxLineThickness := Parser.IntValue;
17:
- InterpretTStringListArray(DSS, Param, DaisyBusList); {read in Bus list}
+ InterpretTStringListArray(DSS, Param, DaisyBusList); // read in Bus list
18:
begin
MinScale := Parser.DblValue;
MinScaleIsSpecified := TRUE; // Indicate the user wants a particular value
end;
19:
- ThreePhLineStyle := Parser.IntValue
- ;
+ ThreePhLineStyle := Parser.IntValue;
20:
- SinglePhLineStyle := Parser.IntValue
- ;
+ SinglePhLineStyle := Parser.IntValue;
21:
begin // Parse off phase(s) to plot
PhasesToPlot := PROFILE3PH; // the default
@@ -536,8 +433,8 @@ function DoPlotCmd(DSS: TDSSContext): Integer;
end;
- ParamName := Uppercase(Parser.NextParam);
- Param := Uppercase(Parser.StrValue);
+ ParamName := AnsiUpperCase(Parser.NextParam);
+ Param := AnsiUpperCase(Parser.StrValue);
end;
if not ActiveCircuit.Issolved then
@@ -576,15 +473,15 @@ function DoPlotCmd(DSS: TDSSContext): Integer;
'Bases', jsonBases,
'SinglePhLineStyle', SinglePhLineStyle,
'ThreePhLineStyle', ThreePhLineStyle,
- // TODO: convert to HTML?
- 'Color1', Color1,
- 'Color2', Color2,
- 'Color3', Color3,
+ 'Color1', ColorToHTML(Color1),
+ 'Color2', ColorToHTML(Color2),
+ 'Color3', ColorToHTML(Color3),
'TriColorMax', TriColorMax,
'TriColorMid', TriColorMid,
'MaxScaleIsSpecified', MaxScaleIsSpecified,
'MinScaleIsSpecified', MinScaleIsSpecified,
'DaisyBusList', jsonDaisyBusList,
+ 'DaisySize', DSS.DaisySize,
'MaxLineThickness', MaxLineThickness,
'Markers', TJSONObject.Create(
[
@@ -623,6 +520,7 @@ function DoPlotCmd(DSS: TDSSContext): Integer;
DSS.DSSPlotCallback(DSS, PChar(plotParamsStr));
finally
FreeAndNil(plotParams);
+ FreeAndNil(DSSPlotObj);
end;
end;
@@ -630,26 +528,17 @@ function DoPlotCmd(DSS: TDSSContext): Integer;
procedure DisposeStrings;
var
i: Integer;
-
begin
for i := 1 to NumPlotOptions do
- begin
PlotOption[i] := '';
- PlotHelp[i] := '';
- end;
-
end;
initialization
-
DefineOptions;
-
- PlotCommands := TCommandList.Create(PlotOption);
- PlotCommands.Abbrev := TRUE;
+ PlotCommands := TCommandList.Create(PlotOption, True);
finalization
-
DisposeStrings;
PlotCommands.Free;
end.
diff --git a/src/Executive/ShowOptions.pas b/src/Executive/ShowOptions.pas
index 9e98043fd..b4b7a6776 100644
--- a/src/Executive/ShowOptions.pas
+++ b/src/Executive/ShowOptions.pas
@@ -13,15 +13,53 @@ interface
Command,
DSSClass;
-const
- NumShowOptions = 34;
-
-function DoShowCmd(DSS: TDSSContext): Integer;
+type
+{$SCOPEDENUMS ON}
+ TShowOption = (
+ INVALID = 0,
+ autoadded = 1,
+ buses = 2,
+ currents = 3,
+ convergence = 4,
+ elements = 5,
+ faults = 6,
+ isolated = 7,
+ generators = 8,
+ meters = 9,
+ monitor = 10,
+ panel = 11,
+ powers = 12,
+ voltages = 13,
+ zone = 14,
+ taps = 15,
+ overloads = 16,
+ unserved = 17,
+ eventlog = 18,
+ variables = 19,
+ ratings = 20,
+ loops = 21,
+ losses = 22,
+ busflow = 23,
+ lineconstants = 24,
+ yprim = 25,
+ y = 26,
+ controlqueue = 27,
+ topology = 28,
+ mismatch = 29,
+ kvbasemismatch = 30,
+ deltaV = 31,
+ QueryLog = 32,
+ Controlled = 33,
+ Result = 34
+ );
+{$SCOPEDENUMS OFF}
+const
+ NumShowOptions = Ord(High(TShowOption));
+function DoShowCmd({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext): Integer;
var
- ShowOption,
- ShowHelp: array[1..NumShowOptions] of String;
+ ShowOption: array[1..NumShowOptions] of String;
ShowCommands: TCommandList;
implementation
@@ -35,128 +73,22 @@ implementation
sysutils,
CmdForms,
LineUnits,
- DSSHelper;
+ DSSHelper,
+ TypInfo;
procedure DefineOptions;
-
+var
+ info: Pointer;
+ i: Integer;
begin
-
- ShowOption[1] := 'autoadded';
- ShowOption[2] := 'buses';
- ShowOption[3] := 'currents';
- ShowOption[4] := 'convergence';
- ShowOption[5] := 'elements';
- ShowOption[6] := 'faults';
- ShowOption[7] := 'isolated';
- ShowOption[8] := 'generators';
- ShowOption[9] := 'meters';
- ShowOption[10] := 'monitor';
- ShowOption[11] := 'panel';
- ShowOption[12] := 'powers';
- ShowOption[13] := 'voltages';
- ShowOption[14] := 'zone';
- ShowOption[15] := 'taps';
- ShowOption[16] := 'overloads';
- ShowOption[17] := 'unserved';
- ShowOption[18] := 'eventlog';
- ShowOption[19] := 'variables';
- ShowOption[20] := 'ratings';
- ShowOption[21] := 'loops';
- ShowOption[22] := 'losses';
- ShowOption[23] := 'busflow';
- ShowOption[24] := 'lineconstants';
- ShowOption[25] := 'yprim';
- ShowOption[26] := 'y';
- ShowOption[27] := 'controlqueue';
- ShowOption[28] := 'topology';
- ShowOption[29] := 'mismatch';
- ShowOption[30] := 'kvbasemismatch';
- ShowOption[31] := 'deltaV';
- ShowOption[32] := 'QueryLog';
- ShowOption[33] := 'Controlled';
- ShowOption[34] := 'Result';
-
-
- ShowHelp[1] := 'Shows auto added capacitors or generators. See AutoAdd solution mode.';
- ShowHelp[2] := 'Report showing all buses and nodes currently defined.';
- ShowHelp[3] := 'Report showing currents from most recent solution. syntax: ' + CRLF + CRLF +
- 'Show Currents [[residual=]yes|no*] [Seq* | Elements]' + CRLF + CRLF +
- 'If "residual" flag is yes, the sum of currents in all conductors is reported. ' +
- 'Default is to report Sequence currents; otherwise currents in all conductors are reported.';
- ShowHelp[4] := 'Report on the convergence of each node voltage.';
- ShowHelp[5] := 'Shows names of all elements in circuit or all elements of a specified class. Syntax: ' + CRLF + CRLF +
- 'Show ELements [Classname] ' + CRLF + CRLF +
- 'Useful for creating scripts that act on selected classes of elements. ';
- ShowHelp[6] := 'After fault study solution, shows fault currents.';
- ShowHelp[7] := 'Report showing buses and elements that are isolated from the main source.';
- ShowHelp[8] := 'Report showing generator elements currently defined and the values of the energy meters ' + CRLF +
- 'associated with each generator.';
- ShowHelp[9] := 'Shows the present values of the registers in the EnergyMeter elements.';
- ShowHelp[10] := 'Shows the contents of a selected monitor. Syntax: ' + CRLF + CRLF +
- ' Show Monitor monitorname';
- ShowHelp[11] := 'Shows control panel. (not necessary for standalone version)';
- ShowHelp[12] := 'Report on powers flowing in circuit from most recent solution. ' + CRLF +
- 'Powers may be reported in kVA or MVA and in sequence quantities or in every ' +
- 'conductor of each element. Syntax:' + CRLF + CRLF +
- 'Show Powers [MVA|kVA*] [Seq* | Elements]' + CRLF + CRLF +
- 'Sequence powers in kVA is the default. Examples:' + CRLF + CRLF +
- 'Show powers' + CRLF +
- 'Show power kva element' + CRLF +
- 'Show power mva elem';
- ShowHelp[13] := 'Reports voltages from most recent solution. Voltages are reported with respect to ' + CRLF +
- 'system reference (Node 0) by default (LN option), but may also be reported Line-Line (LL option).' + CRLF +
- 'The voltages are normally reported by bus/node, but may also be reported by circuit element. Syntax:' + CRLF + CRLF +
- 'Show Voltages [LL |LN*] [Seq* | Nodes | Elements]' + CRLF + CRLF +
- 'Show Voltages' + crlf +
- 'Show Voltage LN Nodes' + CRLF +
- 'Show Voltages LL Nodes' + CRLF +
- 'Show Voltage LN Elem';
- ShowHelp[14] := 'Shows the zone for a selected EnergyMeter element. Shows zone either in ' +
- 'a text file or in a graphical tree view.' + CRLF + CRLF +
- 'Show Zone energymetername [Treeview]';
- ShowHelp[15] := 'Shows the regulator/LTC taps from the most recent solution.';
- ShowHelp[16] := 'Shows overloaded power delivery elements.';
- ShowHelp[17] := 'Shows loads that are "unserved". That is, loads for which the voltage is too low, ' +
- 'or a branch on the source side is overloaded. If UEonly is specified, shows only those loads ' +
- 'in which the emergency rating has been exceeded. Syntax:' + CRLF + CRLF +
- 'Show Unserved [UEonly] (unserved loads)';
- ShowHelp[18] := 'Shows the present event log. (Regulator tap changes, capacitor switching, etc.)';
- ShowHelp[19] := 'Shows internal state variables of devices (Power conversion elements) that report them.';
- ShowHelp[20] := 'Shows ratings of power delivery elements.';
- ShowHelp[21] := 'Shows closed loops detected by EnergyMeter elements that are possibly unwanted. Otherwise, loops are OK.';
- ShowHelp[22] := 'Reports losses in each element and in the entire circuit.';
- ShowHelp[23] := 'Creates a report showing power and current flows as well as voltages around a selected bus. Syntax:' + CRLF + CRLF +
- 'Show BUSFlow busname [MVA|kVA*] [Seq* | Elements]' + CRLF + CRLF +
- 'Show busflow busxxx kVA elem' + CRLF +
- 'Show busflow busxxx MVA seq' + CRLF + CRLF +
- 'NOTE: The Show menu will prompt you for these values.';
- ShowHelp[24] := 'Creates two report files for the line constants (impedances) of every LINEGEOMETRY element currently defined. ' +
- 'One file shows the main report with the matrices. The other file contains corresponding LINECODE ' +
- 'definitions that you may use in subsequent simulations. Syntax:' + CRLF + CRLF +
- 'Show LIneConstants [frequency] [none|mi|km|kft|m|me|ft|in|cm] [rho]' + CRLF + CRLF +
- 'Specify the frequency, length units and earth resistivity (meter-ohms). Examples:' + CRLF + CRLF +
- 'Show Lineconstants 60 kft 100' + CRLF +
- 'Show Linecon 50 km 1000';
- ShowHelp[25] := 'Show the primitive admittance (y) matrix for the active element.';
- ShowHelp[26] := 'Show the system Y matrix. Could be a large file!';
- ShowHelp[27] := 'Shows the present contents of the control queue.';
- ShowHelp[28] := 'Shows the topology as seen by the SwtControl elements.';
- ShowHelp[29] := 'Shows the current mismatches at each node in amperes and percent of max currents at node.';
- ShowHelp[30] := 'Creates a report of Load and Generator elements for which the base voltage does not match the Bus base voltage. ' +
- 'Scripts for correcting the voltage base are suggested.';
- ShowHelp[31] := 'Show voltages ACROSS each 2-terminal element, phase-by-phase. ';
- ShowHelp[32] := 'Show Query Log file. ';
- ShowHelp[33] := 'Show Controlled elements and the names of the controls connected to them in CSV format.';
- ShowHelp[34] := 'Show last result (in @result variable).';
-
+ info := TypeInfo(TShowOption);
+ for i := 1 to NumShowOptions do
+ ShowOption[i] := GetEnumName(info, i);
end;
-
-//----------------------------------------------------------------------------
-function DoShowCmd(DSS: TDSSContext): Integer;
-
+function DoShowCmd({$IFDEF DSS_CAPI_PM}MainDSS{$ELSE}DSS{$ENDIF}: TDSSContext): Integer;
var
- {ParamName,} Param, Filname: String;
+ Param, Filname: String;
ParamPointer: Integer;
pMon: TMonitorObj;
@@ -169,38 +101,39 @@ function DoShowCmd(DSS: TDSSContext): Integer;
Units: Integer;
Rho_line: Double;
{$IFDEF DSS_CAPI_PM}
- PMParent: TDSSContext;
InitP, FinalP, idxP: Integer; // Variables added to concatenate the results in OpenDSS-PM
+ PMParent, DSS: TDSSContext;
begin
- PMParent := DSS.GetPrime();
+ PMParent := MainDSS.GetPrime();
+ DSS := MainDSS.ActiveChild;
{$ELSE}
begin
{$ENDIF}
Result := 0;
- {ParamName :=} DSS.Parser.NextParam;
- Param := LowerCase(DSS.Parser.StrValue);
+ DSS.Parser.NextParam;
+ Param := AnsiLowerCase(DSS.Parser.StrValue);
ParamPointer := ShowCommands.Getcommand(Param);
if ParamPointer = 0 then
begin
- DoSimpleMsg(DSS, 'Error: Unknown Show Command:"' + Param + '"', 24700);
+ DoSimpleMsg(DSS, 'Error: Unknown Show Command:"%s"', [Param], 24700);
Exit;
// ParamPointer := 13; {voltages}
end;
- {Check commands requiring a solution and abort if no solution or circuit}
+ // Check commands requiring a solution and abort if no solution or circuit
case ParamPointer of
4, 6, 8..10, 12, 13..17, 19..23, 29..31:
begin
if not assigned(DSS.ActiveCircuit) then
begin
- DoSimpleMsg(DSS, 'No circuit created.', 24701);
+ DoSimpleMsg(DSS, _('No circuit created.'), 24701);
Exit;
end;
if not assigned(DSS.ActiveCircuit.Solution) or not assigned(DSS.ActiveCircuit.Solution.NodeV) then
begin
- DoSimpleMsg(DSS, 'The circuit must be solved before you can do this.', 24702);
+ DoSimpleMsg(DSS, _('The circuit must be solved before you can do this.'), 24702);
Exit;
end;
end;
@@ -211,17 +144,17 @@ function DoShowCmd(DSS: TDSSContext): Integer;
case ParamPointer of
1:
begin {Autoadded}
- FireOffEditor(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'AutoAddedGenerators.Txt');
- FireOffEditor(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'AutoAddedCapacitors.Txt');
+ FireOffEditor(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'AutoAddedGenerators.txt');
+ FireOffEditor(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'AutoAddedCapacitors.txt');
end;
2:
- ShowBuses(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Buses.Txt');
+ ShowBuses(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Buses.txt');
3:
begin
ShowOptionCode := 0;
ShowResid := FALSE;
- {ParamName :=} DSS.Parser.NextParam; // Look for residual
- Param := Uppercase(DSS.Parser.StrValue);
+ DSS.Parser.NextParam; // Look for residual
+ Param := AnsiUpperCase(DSS.Parser.StrValue);
// logic handles show curr y|n|T elements or show curr elements
if (Length(Param) > 0) then
case Param[1] of
@@ -232,8 +165,8 @@ function DoShowCmd(DSS: TDSSContext): Integer;
'E':
ShowOptionCode := 1;
end;
- {ParamName :=} DSS.Parser.NextParam; // Look for another param
- Param := Uppercase(DSS.Parser.StrValue);
+ DSS.Parser.NextParam; // Look for another param
+ Param := AnsiUpperCase(DSS.Parser.StrValue);
if (Length(Param) > 0) then
case Param[1] of
'E':
@@ -247,30 +180,30 @@ function DoShowCmd(DSS: TDSSContext): Integer;
else
FilName := 'ERROR';
end;
- ShowCurrents(DSS, DSS.OutputDirectory + DSS.CircuitName_ + FilName + '.Txt', ShowResid, ShowOptionCode);
+ ShowCurrents(DSS, DSS.OutputDirectory + DSS.CircuitName_ + FilName + '.txt', ShowResid, ShowOptionCode);
end;
4:
- DSS.ActiveCircuit.Solution.WriteConvergenceReport(DSS.OutputDirectory + DSS.CircuitName_ + 'Convergence.TXT');
+ DSS.ActiveCircuit.Solution.WriteConvergenceReport(DSS.OutputDirectory + DSS.CircuitName_ + 'Convergence.txt');
5:
begin
- {ParamName :=} DSS.Parser.NextParam; // Look for another param
- Param := LowerCase(DSS.Parser.StrValue);
- ShowElements(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Elements.Txt', Param);
+ DSS.Parser.NextParam; // Look for another param
+ Param := AnsiLowerCase(DSS.Parser.StrValue);
+ ShowElements(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Elements.txt', Param);
end;
6:
- ShowFaultStudy(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'FaultStudy.Txt');
+ ShowFaultStudy(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'FaultStudy.txt');
7:
- ShowIsolated(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Isolated.Txt');
+ ShowIsolated(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Isolated.txt');
8:
- ShowGenMeters(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'GenMeterOut.Txt');
+ ShowGenMeters(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'GenMeterOut.txt');
9:
- ShowMeters(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'EMout.Txt');
+ ShowMeters(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'EMout.txt');
10:
begin // Show Monitor
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
if Length(Param) = 0 then
- DoSimpleMsg(DSS, 'Monitor Name Not Specified.' + CRLF + DSS.Parser.CmdString, 249)
+ DoSimpleMsg(DSS, 'Monitor Name Not Specified. %s', [CRLF + DSS.Parser.CmdString], 249)
else
begin
{$IFDEF DSS_CAPI_PM}
@@ -281,7 +214,7 @@ function DoShowCmd(DSS: TDSSContext): Integer;
if pMon <> NIL then
pMon.TranslateToCSV(TRUE)
else
- DoSimpleMsg(DSS, 'Monitor "' + param + '" not found.' + CRLF + DSS.Parser.CmdString, 248);
+ DoSimpleMsg(DSS, 'Monitor "%s" not found. %s', [param, CRLF + DSS.Parser.CmdString], 248);
{$IFDEF DSS_CAPI_PM}
end
else
@@ -294,21 +227,21 @@ function DoShowCmd(DSS: TDSSContext): Integer;
if pMon <> NIL then
pMon.TranslateToCSV((idxP = FinalP))
else
- DoSimpleMsg(DSS, 'Monitor "' + param + '" not found.' + CRLF + DSS.Parser.CmdString, 248);
+ DoSimpleMsg(DSS, 'Monitor "%s" not found. %s', [param, CRLF + DSS.Parser.CmdString], 248);
end;
end;
{$ENDIF}
end;
end;
11:
- DoSimpleMsg(DSS, 'Command "show panel" is not supported in DSS Extensions.', 999);
+ DoSimpleMsg(DSS, _('Command "show panel" is not supported in DSS Extensions.'), 999);
12:
begin
ShowOptionCode := 0;
MVAOpt := 0;
FilName := 'Power';
- {Paramname :=} DSS.Parser.nextParam;
- Param := LowerCase(DSS.Parser.strvalue);
+ DSS.Parser.nextParam;
+ Param := AnsiLowerCase(DSS.Parser.strvalue);
if Length(Param) > 0 then
case Param[1] of
'm':
@@ -316,8 +249,8 @@ function DoShowCmd(DSS: TDSSContext): Integer;
'e':
ShowOptionCode := 1;
end;
- {Paramname :=} DSS.Parser.nextParam;
- Param := LowerCase(DSS.Parser.strvalue);
+ DSS.Parser.nextParam;
+ Param := AnsiLowerCase(DSS.Parser.strvalue);
if Length(Param) > 0 then
if Param[1] = 'e' then
ShowOptionCode := 1;
@@ -337,7 +270,7 @@ function DoShowCmd(DSS: TDSSContext): Integer;
LLOpt := FALSE; // Line-Line voltage option
ShowOptionCode := 0;
{Check for LL or LN option}
- {Paramname :=} DSS.Parser.nextParam;
+ DSS.Parser.nextParam;
Param := DSS.Parser.strvalue;
FilName := 'VLN';
@@ -348,8 +281,8 @@ function DoShowCmd(DSS: TDSSContext): Integer;
FilName := 'VLL';
end;
{Check for Seq | nodes | elements}
- {Paramname :=} DSS.Parser.nextParam;
- Param := UpperCase(DSS.Parser.strvalue);
+ DSS.Parser.nextParam;
+ Param := AnsiUpperCase(DSS.Parser.strvalue);
if Length(Param) > 0 then
case Param[1] of
'N':
@@ -365,45 +298,45 @@ function DoShowCmd(DSS: TDSSContext): Integer;
else
FilName := FilName + '_seq';
end;
- ShowVoltages(DSS, DSS.OutputDirectory + DSS.CircuitName_ + FilName + '.Txt', LLopt, ShowOptionCode);
+ ShowVoltages(DSS, DSS.OutputDirectory + DSS.CircuitName_ + FilName + '.txt', LLopt, ShowOptionCode);
end;
14:
- ShowMeterZone(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'ZoneOut.Txt');
+ ShowMeterZone(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'ZoneOut.txt');
15:
- ShowRegulatorTaps(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'RegTaps.Txt');
+ ShowRegulatorTaps(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'RegTaps.txt');
16:
- ShowOverloads(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Overload.Txt');
+ ShowOverloads(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Overload.txt');
17:
begin
- {ParamName :=} DSS.Parser.NextParam;
+ DSS.Parser.NextParam;
Param := DSS.Parser.StrValue;
if Length(Param) > 0 then
- ShowUnserved(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Unserved.Txt', TRUE)
+ ShowUnserved(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Unserved.txt', TRUE)
else
- ShowUnserved(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Unserved.Txt', FALSE);
+ ShowUnserved(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Unserved.txt', FALSE);
end;
18:
- ShowEventLog(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'EventLog.Txt');// ShowMessageForm(EventStrings);
+ ShowEventLog(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'EventLog.txt');// ShowMessageForm(EventStrings);
19:
- ShowVariables(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Variables.Txt');
+ ShowVariables(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Variables.txt');
20:
- ShowRatings(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'RatingsOut.Txt');
+ ShowRatings(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'RatingsOut.txt');
21:
- ShowLoops(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Loops.Txt');
+ ShowLoops(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Loops.txt');
22:
- ShowLosses(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Losses.Txt');
+ ShowLosses(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Losses.txt');
23:
begin // Show Bus Power Report
ShowOptionCode := 0;
MVAOpt := 0;
- {ParamName :=} DSS.Parser.nextParam; // Get busname
+ DSS.Parser.nextParam; // Get busname
Busname := DSS.Parser.strvalue;
if Length(BusName) > 0 then
FilName := BusName
else
FilName := 'BusPower';
- {ParamName :=} DSS.Parser.nextParam;
- Param := LowerCase(DSS.Parser.strvalue);
+ DSS.Parser.nextParam;
+ Param := AnsiLowerCase(DSS.Parser.strvalue);
if Length(Param) > 0 then
case Param[1] of
'm':
@@ -411,8 +344,8 @@ function DoShowCmd(DSS: TDSSContext): Integer;
'e':
ShowOptionCode := 1;
end;
- {ParamName :=} DSS.Parser.nextParam;
- Param := LowerCase(DSS.Parser.strvalue);
+ DSS.Parser.nextParam;
+ Param := AnsiLowerCase(DSS.Parser.strvalue);
if Length(Param) > 0 then
if Param[1] = 'e' then
ShowOptionCode := 1;
@@ -432,13 +365,13 @@ function DoShowCmd(DSS: TDSSContext): Integer;
Freq := DSS.DefaultBaseFreq; // Default
Units := UNITS_KFT; // 'kft'; // default
Rho_line := 100.0;
- {ParamName :=} DSS.Parser.nextparam;
+ DSS.Parser.nextparam;
if Length(DSS.Parser.strvalue) > 0 then
Freq := DSS.Parser.dblvalue;
- {ParamName :=} DSS.Parser.nextparam;
+ DSS.Parser.nextparam;
if Length(DSS.Parser.strvalue) > 0 then
Units := GetUnitsCode(DSS.Parser.strvalue);
- {ParamName :=} DSS.Parser.nextparam;
+ DSS.Parser.nextparam;
if Length(DSS.Parser.strvalue) > 0 then
Rho_line := DSS.Parser.dblValue;
ShowLineConstants(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'LineConstants.txt', freq, units, Rho_line);
@@ -461,23 +394,20 @@ function DoShowCmd(DSS: TDSSContext): Integer;
28:
ShowTopology(DSS, DSS.OutputDirectory + DSS.CircuitName_);
29:
- ShowNodeCurrentSum(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'NodeMismatch.Txt');
+ ShowNodeCurrentSum(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'NodeMismatch.txt');
30:
- ShowkVBaseMismatch(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'kVBaseMismatch.Txt');
+ ShowkVBaseMismatch(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'kVBaseMismatch.txt');
31:
- ShowDeltaV(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'DeltaV.Txt');
+ ShowDeltaV(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'DeltaV.txt');
32:
FireOffEditor(DSS, DSS.QueryLogFileName);
33:
- ShowControlledElements(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'ControlledElements.CSV');
+ ShowControlledElements(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'ControlledElements.csv');
34:
- ShowResult(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Result.CSV');
- else
+ ShowResult(DSS, DSS.OutputDirectory + DSS.CircuitName_ + 'Result.csv');
end;
-
DSS.InShowResults := FALSE;
-
end;
procedure DisposeStrings;
@@ -488,17 +418,12 @@ procedure DisposeStrings;
for i := 1 to NumShowOptions do
begin
ShowOption[i] := '';
- ShowHelp[i] := '';
end;
-
end;
initialization
-
DefineOptions;
-
- ShowCommands := TCommandList.Create(ShowOption);
- ShowCommands.Abbrev := TRUE;
+ ShowCommands := TCommandList.Create(ShowOption, True);
finalization
diff --git a/src/General/CNData.pas b/src/General/CNData.pas
index 0dd636fac..7898e5f87 100644
--- a/src/General/CNData.pas
+++ b/src/General/CNData.pas
@@ -17,56 +17,51 @@ interface
CableData;
type
+{$SCOPEDENUMS ON}
+ TCNDataProp = (
+ INVALID = 0,
+ k = 1,
+ DiaStrand = 2,
+ GmrStrand = 3,
+ Rstrand = 4
+ );
+{$SCOPEDENUMS OFF}
+
TCNData = class(TCableData)
- PRIVATE
- function Get_Code: String; // Returns active line code string
- procedure Set_Code(const Value: String); // sets the active CNData
PROTECTED
- procedure DefineProperties;
- function MakeLike(const CNName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
- // Set this property to point ActiveCNDataObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TCNDataObj = class(TCableDataObj)
-{$IFDEF DSS_CAPI}
PUBLIC
-{$ELSE}
- PRIVATE
-{$ENDIF}
FkStrand: Integer;
FDiaStrand: Double;
FGmrStrand: Double;
FRStrand: Double;
- PUBLIC
constructor Create(ParClass: TDSSClass; const CNDataName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
property NStrand: Integer READ FkStrand;
property DiaStrand: Double READ FDiaStrand;
property GmrStrand: Double READ FGmrStrand;
property RStrand: Double READ FRStrand;
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
end;
implementation
uses
- ParserDel,
DSSGlobals,
DSSClassDefs,
Sysutils,
- Ucomplex,
+ UComplex, DSSUcomplex,
Arraydef,
LineUnits,
Utilities,
@@ -74,20 +69,20 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TCNDataObj;
+ TProp = TCNDataProp;
const
- NumPropsThisClass = 4;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-constructor TCNData.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TCNData.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'CNData';
- DSSClassType := DSS_OBJECT;
- ActiveElement := 0;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, DSS_OBJECT, 'CNData');
end;
destructor TCNData.Destroy;
@@ -96,157 +91,81 @@ destructor TCNData.Destroy;
end;
procedure TCNData.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
NumProperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
- PropertyName[1] := 'k';
- PropertyName[2] := 'DiaStrand';
- PropertyName[3] := 'GmrStrand';
- PropertyName[4] := 'Rstrand';
+ // integer properties
+ PropertyType[ActiveProperty + ord(TProp.k)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ActiveProperty + ord(TProp.k)] := ptruint(@obj.FkStrand);
- PropertyHelp[1] := 'Number of concentric neutral strands; default is 2';
- PropertyHelp[2] := 'Diameter of a concentric neutral strand; same units as core conductor radius; no default.';
- PropertyHelp[3] := 'Geometric mean radius of a concentric neutral strand; same units as core conductor GMR; defaults to 0.7788 * CN strand radius.';
- PropertyHelp[4] := 'AC resistance of a concentric neutral strand; same units as core conductor resistance; no default.';
+ // double properties (default type)
+ PropertyOffset[ActiveProperty + ord(TProp.DiaStrand)] := ptruint(@obj.FDiaStrand);
+ PropertyOffset[ActiveProperty + ord(TProp.GmrStrand)] := ptruint(@obj.FGmrStrand);
+ PropertyOffset[ActiveProperty + ord(TProp.Rstrand)] := ptruint(@obj.FRStrand);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-end;
-
-function TCNData.NewObject(const ObjName: String): Integer;
-begin
- DSS.ActiveDSSObject := TCNDataObj.Create(Self, ObjName);
- Result := AddObjectToList(DSS.ActiveDSSObject);
+ inherited DefineProperties;
end;
-function TCNData.Edit: Integer;
+function TCNData.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
+ Obj: TObj;
begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveConductorDataObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActiveConductorDataObj;
- with TCNDataObj(DSS.ActiveConductorDataObj) do
- begin
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 101);
- 1:
- FkStrand := Parser.IntValue;
- 2:
- FDiaStrand := Parser.DblValue;
- 3:
- FGmrStrand := Parser.DblValue;
- 4:
- FRStrand := Parser.DblValue;
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveConductorDataObj, ParamPointer - NumPropsThisClass)
- end;
-
- {Set defaults}
- case ParamPointer of
- 2:
- if FGmrStrand <= 0.0 then
- FGmrStrand := 0.7788 * 0.5 * FDiaStrand;
- end;
-
- {Check for critical errors}
- case ParamPointer of
- 1:
- if (FkStrand < 2) then
- DoSimpleMsg('Error: Must have at least 2 concentric neutral strands for CNData ' + Name, 999);
- 2:
- if (FDiaStrand <= 0.0) then
- DoSimpleMsg('Error: Neutral strand diameter must be positive for CNData ' + Name, 999);
- 3:
- if (FGmrStrand <= 0.0) then
- DoSimpleMsg('Error: Neutral strand GMR must be positive for CNData ' + Name, 999);
- end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-function TCNData.MakeLike(const CNName: String): Integer;
-var
- OtherData: TCNDataObj;
- i: Integer;
+procedure TCNDataObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- Result := 0;
- OtherData := Find(CNName);
- if OtherData <> NIL then
- with TCNDataObj(DSS.ActiveConductorDataObj) do
- begin
- FkStrand := OtherData.FkStrand;
- FDiaStrand := OtherData.FDiaStrand;
- FGmrStrand := OtherData.FGmrStrand;
- FRStrand := OtherData.FRStrand;
- ClassMakeLike(OtherData);
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherData.PropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Concentric Neutral MakeLike: "' + CNName + '" Not Found.', 102);
-end;
-
-function TCNData.Get_Code: String; // Returns active line code string
-begin
- Result := TCNDataObj(ElementList.Active).Name;
+ // Set defaults
+ case Idx of
+ 2:
+ if FGmrStrand <= 0.0 then
+ FGmrStrand := 0.7788 * 0.5 * FDiaStrand;
+ end;
+ // Check for critical errors
+ case Idx of
+ 1:
+ if (FkStrand < 2) then
+ DoSimpleMsg('Error: Must have at least 2 concentric neutral strands for CNData %s', [Name], 999);
+ 2:
+ if (FDiaStrand <= 0.0) then
+ DoSimpleMsg('Error: Neutral strand diameter must be positive for CNData %s', [Name], 999);
+ 3:
+ if (FGmrStrand <= 0.0) then
+ DoSimpleMsg('Error: Neutral strand GMR must be positive for CNData %s', [Name], 999);
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-procedure TCNData.Set_Code(const Value: String); // sets the active CNData
+procedure TCNDataObj.MakeLike(OtherPtr: Pointer);
var
- CNDataObj: TCNDataObj;
+ Other: TObj;
begin
- DSS.ActiveConductorDataObj := NIL;
- CNDataObj := ElementList.First;
- while CNDataObj <> NIL do
- begin
- if CompareText(CNDataObj.Name, Value) = 0 then
- begin
- DSS.ActiveConductorDataObj := CNDataObj;
- Exit;
- end;
- CNDataObj := ElementList.Next;
- end;
- DoSimpleMsg('CNData: "' + Value + '" not Found.', 103);
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FkStrand := Other.FkStrand;
+ FDiaStrand := Other.FDiaStrand;
+ FGmrStrand := Other.FGmrStrand;
+ FRStrand := Other.FRStrand;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TCNData Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TCNDataObj.Create(ParClass: TDSSClass; const CNDataName: String);
begin
inherited Create(ParClass, CNDataName);
- Name := LowerCase(CNDataName);
+ Name := AnsiLowerCase(CNDataName);
DSSObjType := ParClass.DSSClassType;
FkStrand := 2;
FDiaStrand := -1.0;
FGmrStrand := -1.0;
FRStrand := -1.0;
- InitPropertyValues(0);
end;
destructor TCNDataObj.Destroy;
@@ -254,37 +173,4 @@ destructor TCNDataObj.Destroy;
inherited destroy;
end;
-procedure TCNDataObj.DumpProperties(F: TFileStream; Complete: Boolean);
-var
- i: Integer;
-begin
- inherited DumpProperties(F, Complete);
- with ParentClass do
- begin
- for i := 1 to NumProperties do
- begin
- FSWrite(F, '~ ' + PropertyName^[i] + '=');
- case i of
- 1:
- FSWriteln(F, Format('%d', [FkStrand]));
- 2:
- FSWriteln(F, Format('%.6g', [FDiaStrand]));
- 3:
- FSWriteln(F, Format('%.6g', [FGmrStrand]));
- 4:
- FSWriteln(F, Format('%.6g', [FRStrand]));
- end;
- end;
- end;
-end;
-
-procedure TCNDataObj.InitPropertyValues(ArrayOffset: Integer);
-begin
- PropertyValue[1] := '2';
- PropertyValue[2] := '-1';
- PropertyValue[3] := '-1';
- PropertyValue[4] := '-1';
- inherited InitPropertyValues(ArrayOffset + NumPropsThisClass);
-end;
-
-end.
+end.
\ No newline at end of file
diff --git a/src/General/CNLineConstants.pas b/src/General/CNLineConstants.pas
index b3cafbd58..a4ebd4e12 100644
--- a/src/General/CNLineConstants.pas
+++ b/src/General/CNLineConstants.pas
@@ -11,7 +11,7 @@ interface
uses
Arraydef,
Ucmatrix,
- Ucomplex,
+ UComplex, DSSUcomplex,
LineUnits,
LineConstants,
CableConstants;
@@ -34,7 +34,6 @@ TCNLineConstants = class(TCableConstants)
procedure Set_DiaStrand(i, units: Integer; const Value: Double);
procedure Set_GmrStrand(i, units: Integer; const Value: Double);
procedure Set_RStrand(i, units: Integer; const Value: Double);
- PROTECTED
PUBLIC
procedure Calc(f: Double; EarthModel: Integer); OVERRIDE;
@@ -132,7 +131,7 @@ procedure TCNLineConstants.Calc(f: Double; EarthModel: Integer);
FYCMatrix.Clear;
// add concentric neutrals to the end of conductor list; they are always reduced
- N := FNumConds + FNumPhases;
+ N := FNumConds + FNPhases;
Zmat := TCMatrix.CreateMatrix(N);
{For less than 1 kHz use GMR to better match published data}
@@ -149,26 +148,26 @@ procedure TCNLineConstants.Calc(f: Double; EarthModel: Integer);
if PowerFreq then
begin // for less than 1 kHz, use published GMR
Zi.im := 0.0;
- Zspacing := CmulReal(Lfactor, ln(1.0 / FGMR^[i])); // use GMR
+ Zspacing := Lfactor * ln(1.0 / FGMR^[i]); // use GMR
end
else
begin
- Zspacing := CmulReal(Lfactor, ln(1.0 / Fradius^[i]));
+ Zspacing := Lfactor * ln(1.0 / Fradius^[i]);
end;
- Zmat.SetElement(i, i, Cadd(Zi, Cadd(Zspacing, Get_Ze(i, i, EarthModel))));
+ Zmat.SetElement(i, i, Zi + Zspacing + Get_Ze(i, i, EarthModel));
end;
// CN self impedances
- for i := 1 to FNumPhases do
+ for i := 1 to FNPhases do
begin
ResCN := FRstrand^[i] / FkStrand^[i];
RadCN := 0.5 * (FDiaCable^[i] - FDiaStrand^[i]);
GmrCN := Power(FGmrStrand^[i] * FkStrand^[i] * Power(RadCN, FkStrand^[i] - 1.0),
1.0 / FkStrand^[i]);
- Zspacing := CMulReal(Lfactor, ln(1.0 / GmrCN));
+ Zspacing := Lfactor * ln(1.0 / GmrCN);
Zi := cmplx(ResCN, 0.0);
idxi := i + FNumConds;
- Zmat.SetElement(idxi, idxi, Cadd(Zi, Cadd(Zspacing, Get_Ze(i, i, EarthModel))));
+ Zmat.SetElement(idxi, idxi, Zi + Zspacing + Get_Ze(i, i, EarthModel));
end;
// Mutual Impedances - between CN cores and bare neutrals
@@ -177,19 +176,19 @@ procedure TCNLineConstants.Calc(f: Double; EarthModel: Integer);
for j := 1 to i - 1 do
begin
Dij := sqrt(sqr(Fx^[i] - Fx^[j]) + sqr(Fy^[i] - Fy^[j]));
- Zmat.SetElemSym(i, j, Cadd(Cmulreal(Lfactor, ln(1.0 / Dij)), Get_Ze(i, j, EarthModel)));
+ Zmat.SetElemSym(i, j, Lfactor * ln(1.0 / Dij) + Get_Ze(i, j, EarthModel));
end;
end;
// Mutual Impedances - CN to other CN, cores, and bare neutrals
- for i := 1 to FNumPhases do
+ for i := 1 to FNPhases do
begin
idxi := i + FNumConds;
for j := 1 to i - 1 do
begin // CN to other CN
idxj := j + FNumConds;
Dij := sqrt(sqr(Fx^[i] - Fx^[j]) + sqr(Fy^[i] - Fy^[j]));
- Zmat.SetElemSym(idxi, idxj, Cadd(Cmulreal(Lfactor, ln(1.0 / Dij)), Get_Ze(i, j, EarthModel)));
+ Zmat.SetElemSym(idxi, idxj, Lfactor * ln(1.0 / Dij) + Get_Ze(i, j, EarthModel));
end;
for j := 1 to FNumConds do
begin // CN to cores and bare neutrals
@@ -205,7 +204,7 @@ procedure TCNLineConstants.Calc(f: Double; EarthModel: Integer);
Dij := Power(Power(Dij, FkStrand^[i]) - Power(RadCN, FkStrand^[i]),
1.0 / FkStrand^[i]);
end;
- Zmat.SetElemSym(idxi, idxj, Cadd(Cmulreal(Lfactor, ln(1.0 / Dij)), Get_Ze(i, j, EarthModel)));
+ Zmat.SetElemSym(idxi, idxj, Lfactor * ln(1.0 / Dij) + Get_Ze(i, j, EarthModel));
end;
end;
@@ -221,7 +220,7 @@ procedure TCNLineConstants.Calc(f: Double; EarthModel: Integer);
// for shielded cables, build the capacitance matrix directly
// assumes the insulation may lie between semicon layers
- for i := 1 to FNumPhases do
+ for i := 1 to FNPhases do
begin
Yfactor := twopi * e0 * FEpsR^[i] * Fw; // includes frequency so C==>Y
RadOut := 0.5 * FDiaIns^[i];
diff --git a/src/General/CableConstants.pas b/src/General/CableConstants.pas
index de163ac4e..f52391953 100644
--- a/src/General/CableConstants.pas
+++ b/src/General/CableConstants.pas
@@ -11,7 +11,7 @@ interface
uses
Arraydef,
Ucmatrix,
- Ucomplex,
+ UComplex, DSSUcomplex,
LineUnits,
LineConstants;
@@ -103,13 +103,13 @@ function TCableConstants.ConductorsInSameSpace(var ErrorMessage: String): Boolea
*)
for i := 1 to FNumConds do
begin
- if i <= FNumPhases then
+ if i <= FNPhases then
Ri := FRadius^[i]
else
Ri := 0.5 * FDiaCable^[i];
for j := i + 1 to FNumConds do
begin
- if j <= FNumPhases then
+ if j <= FNPhases then
Rj := FRadius^[j]
else
Rj := 0.5 * FDiaCable^[j];
diff --git a/src/General/CableData.pas b/src/General/CableData.pas
index 98ec59077..c29268b3e 100644
--- a/src/General/CableData.pas
+++ b/src/General/CableData.pas
@@ -16,52 +16,53 @@ interface
ConductorData;
type
- TCableData = class(TConductorData)
- PRIVATE
+{$SCOPEDENUMS ON}
+ TCableDataProp = (
+ INVALID = 0,
+ EpsR = 1,
+ InsLayer = 2,
+ DiaIns = 3,
+ DiaCable = 4
+ );
+{$SCOPEDENUMS OFF}
+ TCableData = class(TConductorData)
PROTECTED
- procedure CountProperties;
- procedure DefineProperties;
- procedure ClassEdit(const ActiveObj: Pointer; const ParamPointer: Integer);
- procedure ClassMakeLike(const OtherObj: Pointer);
+ PropertyOffset_CableData: Integer;
+
+ procedure CountPropertiesAndAllocate; override;
+ procedure DefineProperties; override;
PUBLIC
- NumCableClassProps: Integer;
- constructor Create(dssContext: TDSSContext);
+ constructor Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
destructor Destroy; OVERRIDE;
end;
TCableDataObj = class(TConductorDataObj)
-{$IFDEF DSS_CAPI}
PUBLIC
-{$ELSE}
- PRIVATE
-{$ENDIF}
FEpsR: Double;
// next 3 use parent RadiusUnits
FInsLayer: Double;
FDiaIns: Double;
FDiaCable: Double;
- PUBLIC
+
constructor Create(ParClass: TDSSClass; const CableDataName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherObj: Pointer); override;
property EpsR: Double READ FEpsR;
property DiaIns: Double READ FDiaIns;
property DiaCable: Double READ FDiaCable;
property InsLayer: Double READ FInsLayer;
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
end;
implementation
uses
- ParserDel,
DSSGlobals,
DSSClassDefs,
Sysutils,
- Ucomplex,
+ UComplex, DSSUcomplex,
Arraydef,
LineUnits,
Utilities,
@@ -69,11 +70,21 @@ implementation
DSSObjectHelper,
TypInfo;
-constructor TCableData.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+type
+ TObj = TCableDataObj;
+ TProp = TCableDataProp;
+const
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
+
+constructor TCableData.Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
begin
- NumCableClassProps := 4;
- inherited Create(dssContext);
- DSSClassType := DSS_OBJECT;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
+
+ inherited Create(dssContext, DSSClsType or DSS_OBJECT, DSSClsName);
+ ClassParents.Add('CableData');
end;
destructor TCableData.Destroy;
@@ -81,91 +92,66 @@ destructor TCableData.Destroy;
inherited Destroy;
end;
-procedure TCableData.CountProperties;
+procedure TCableData.CountPropertiesAndAllocate;
begin
- NumProperties := NumProperties + NumCableClassProps;
- inherited CountProperties;
+ NumProperties := NumProperties + NumPropsThisClass;
+ inherited CountPropertiesAndAllocate;
end;
procedure TCableData.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
- PropertyName^[ActiveProperty + 1] := 'EpsR';
- PropertyName^[ActiveProperty + 2] := 'InsLayer';
- PropertyName^[ActiveProperty + 3] := 'DiaIns';
- PropertyName^[ActiveProperty + 4] := 'DiaCable';
-
- PropertyHelp^[ActiveProperty + 1] := 'Insulation layer relative permittivity; default is 2.3.';
- PropertyHelp^[ActiveProperty + 2] := 'Insulation layer thickness; same units as radius; no default. ' +
- 'With DiaIns, establishes inner radius for capacitance calculation.';
- PropertyHelp^[ActiveProperty + 3] := 'Diameter over insulation layer; same units as radius; no default. ' +
- 'Establishes outer radius for capacitance calculation.';
- PropertyHelp^[ActiveProperty + 4] := 'Diameter over cable; same units as radius; no default.';
-
- ActiveProperty := ActiveProperty + NumCableClassProps;
+ PopulatePropertyNames(ActiveProperty, NumPropsThisClass, PropInfo, False, 'CableData');
+
+ PropertyOffset_CableData := ActiveProperty;
+
+ // double properties (default type)
+ PropertyOffset[ActiveProperty + ord(TProp.EpsR)] := ptruint(@obj.FEpsR);
+ PropertyOffset[ActiveProperty + ord(TProp.InsLayer)] := ptruint(@obj.FInsLayer);
+ PropertyOffset[ActiveProperty + ord(TProp.DiaIns)] := ptruint(@obj.FDiaIns);
+ PropertyOffset[ActiveProperty + ord(TProp.DiaCable)] := ptruint(@obj.FDiaCable);
+
+ ActiveProperty := ActiveProperty + NumPropsThisClass;
inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TCableData.ClassEdit(const ActiveObj: Pointer; const ParamPointer: Integer);
+procedure TCableDataObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- // continue parsing with contents of Parser
- if ParamPointer > 0 then
- with TCableDataObj(ActiveObj) do
- begin
- case ParamPointer of
- 1:
- FEpsR := Parser.Dblvalue;
- 2:
- FInsLayer := Parser.DblValue;
- 3:
- FDiaIns := Parser.DblValue;
- 4:
- FDiaCable := Parser.DblValue;
- else
- inherited ClassEdit(ActiveObj, ParamPointer - NumCableClassProps)
- end;
- {Check for critical errors}
- case ParamPointer of
- 1:
- if (FEpsR < 1.0) then
- DoSimpleMsg('Error: Insulation permittivity must be greater than one for CableData ' + Name, 999);
- 2:
- if (FInsLayer <= 0.0) then
- DoSimpleMsg('Error: Insulation layer thickness must be positive for CableData ' + Name, 999);
- 3:
- if (FDiaIns <= 0.0) then
- DoSimpleMsg('Error: Diameter over insulation layer must be positive for CableData ' + Name, 999);
- 4:
- if (FDiaCable <= 0.0) then
- DoSimpleMsg('Error: Diameter over cable must be positive for CableData ' + Name, 999);
- end;
- end;
+ // Check for critical errors
+ case (Idx - (ParentClass as TCableData).PropertyOffset_CableData) of
+ ord(TProp.EpsR):
+ if (FEpsR < 1.0) then
+ DoSimpleMsg('Error: Insulation permittivity must be greater than one for CableData %s', [Name], 999);
+ ord(TProp.InsLayer):
+ if (FInsLayer <= 0.0) then
+ DoSimpleMsg('Error: Insulation layer thickness must be positive for CableData %s', [Name], 999);
+ ord(TProp.DiaIns):
+ if (FDiaIns <= 0.0) then
+ DoSimpleMsg('Error: Diameter over insulation layer must be positive for CableData %s', [Name], 999);
+ ord(TProp.DiaCable):
+ if (FDiaCable <= 0.0) then
+ DoSimpleMsg('Error: Diameter over cable must be positive for CableData %s', [Name], 999);
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-procedure TCableData.ClassMakeLike(const OtherObj: Pointer);
+procedure TCableDataObj.MakeLike(OtherObj: Pointer);
var
- OtherCableData: TCableDataObj;
+ Other: TObj;
begin
- OtherCableData := TCableDataObj(OtherObj);
- with TCableDataObj(ActiveDSSObject) do
- begin
- FEpsR := OtherCableData.FEpsR;
- FInsLayer := OtherCableData.FInsLayer;
- FDiaIns := OtherCableData.FDiaIns;
- FDiaCable := OtherCableData.FDiaCable;
- end;
- inherited ClassMakeLike(OtherObj);
+ inherited MakeLike(OtherObj);
+ Other := TCableDataObj(OtherObj);
+ FEpsR := Other.FEpsR;
+ FInsLayer := Other.FInsLayer;
+ FDiaIns := Other.FDiaIns;
+ FDiaCable := Other.FDiaCable;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TConductorData Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TCableDataObj.Create(ParClass: TDSSClass; const CableDataName: String);
-
begin
inherited Create(ParClass, CableDataName);
- Name := LowerCase(CableDataName);
+ Name := AnsiLowerCase(CableDataName);
DSSObjType := ParClass.DSSClassType;
FEpsR := 2.3;
@@ -174,43 +160,9 @@ constructor TCableDataObj.Create(ParClass: TDSSClass; const CableDataName: Strin
FDiaCable := -1.0;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TCableDataObj.Destroy;
begin
inherited destroy;
end;
-procedure TCableDataObj.DumpProperties(F: TFileStream; Complete: Boolean);
-var
- i: Integer;
-begin
- inherited DumpProperties(F, Complete);
- with ParentClass do
- begin
- for i := 1 to NumProperties do
- begin
- FSWrite(F, '~ ' + PropertyName^[i] + '=');
- case i of
- 1:
- FSWriteln(F, Format('%.3g', [FEpsR]));
- 2:
- FSWriteln(F, Format('%.6g', [FInsLayer]));
- 3:
- FSWriteln(F, Format('%.6g', [FDiaIns]));
- 4:
- FSWriteln(F, Format('%.6g', [FDiaCable]));
- end;
- end;
- end;
-end;
-
-procedure TCableDataObj.InitPropertyValues(ArrayOffset: Integer);
-begin
- PropertyValue[ArrayOffset + 1] := '2.3';
- PropertyValue[ArrayOffset + 2] := '-1';
- PropertyValue[ArrayOffset + 3] := '-1';
- PropertyValue[ArrayOffset + 4] := '-1';
- inherited InitPropertyValues(ArrayOffset + 4);
-end;
-
-end.
+end.
\ No newline at end of file
diff --git a/src/General/ConductorData.pas b/src/General/ConductorData.pas
index d6637a758..6c1cbe0d6 100644
--- a/src/General/ConductorData.pas
+++ b/src/General/ConductorData.pas
@@ -9,16 +9,15 @@
interface
-{The ConductorData object is a general DSS object used by all circuits
- as a reference for obtaining line impedances.
-
- The values are set by the normal New and Edit procedures for any DSS object.
-
- The values are retrieved by setting the Code Property in the ConductorData Class.
- This sets the active ConductorData object to be the one referenced by the Code Property;
-
- Then the values of that code can be retrieved via the public variables.
- }
+// The ConductorData object is a general DSS object used by all circuits
+// as a reference for obtaining line impedances.
+//
+// The values are set by the normal New and Edit procedures for any DSS object.
+//
+// The values are retrieved by setting the Code Property in the ConductorData Class.
+// This sets the active ConductorData object to be the one referenced by the Code Property;
+//
+// Then the values of that code can be retrieved via the public variables.
uses
Classes,
@@ -28,31 +27,43 @@ interface
ArrayDef;
type
+{$SCOPEDENUMS ON}
+ TConductorDataProp = (
+ INVALID = 0,
+ Rdc = 1,
+ Rac = 2,
+ Runits = 3,
+ GMRac = 4,
+ GMRunits = 5,
+ radius = 6,
+ radunits = 7,
+ normamps = 8,
+ emergamps = 9,
+ diam = 10,
+ Seasons = 11,
+ Ratings = 12,
+ Capradius = 13
+ );
+{$SCOPEDENUMS OFF}
+
ConductorChoice = (Overhead, ConcentricNeutral, TapeShield, Unknown);
ConductorChoiceArray = array[1..100] of ConductorChoice;
pConductorChoiceArray = ^ConductorChoiceArray;
TConductorData = class(TDSSClass)
- PRIVATE
-
PROTECTED
- procedure CountProperties;
- procedure DefineProperties;
- procedure ClassEdit(const ActiveObj: Pointer; const ParamPointer: Integer);
- procedure ClassMakeLike(const OtherObj: Pointer);
+ PropertyOffset_ConductorData: Integer;
+
+ procedure CountPropertiesAndAllocate; override;
+ procedure DefineProperties; override;
PUBLIC
- NumConductorClassProps: Integer;
- constructor Create(dssContext: TDSSContext);
+ constructor Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
destructor Destroy; OVERRIDE;
end;
TConductorDataObj = class(TDSSObject)
-{$IFDEF DSS_CAPI}
PUBLIC
-{$ELSE}
- PRIVATE
-{$ENDIF}
FRDC: Double;
FR60: Double;
FGMR60: Double;
@@ -61,7 +72,6 @@ TConductorDataObj = class(TDSSObject)
FGMRUnits: Integer;
FResistanceUnits: Integer;
FRadiusUnits: Integer;
- PUBLIC
NormAmps: Double;
EmergAmps: Double;
NumAmpRatings: Integer;
@@ -69,6 +79,8 @@ TConductorDataObj = class(TDSSObject)
constructor Create(ParClass: TDSSClass; const ConductorDataName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherObj: Pointer); override;
property Rdc: Double READ FRDC;
property Rac: Double READ FR60;
@@ -78,10 +90,6 @@ TConductorDataObj = class(TDSSObject)
property ResUnits: Integer READ FresistanceUnits;
property RadiusUnits: Integer READ FradiusUnits;
property GMRUnits: Integer READ FGMRUnits;
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
end;
TConductorDataArray = array[1..100] of TConductorDataObj;
@@ -90,26 +98,31 @@ TConductorDataObj = class(TDSSObject)
implementation
uses
- ParserDel,
DSSGlobals,
DSSClassDefs,
Sysutils,
- Ucomplex,
+ UComplex, DSSUcomplex,
LineUNits,
Utilities,
DSSHelper,
DSSObjectHelper,
TypInfo;
+type
+ TObj = TConductorDataObj;
+ TProp = TConductorDataProp;
const
- LineUnitsHelp = '{mi|kft|km|m|Ft|in|cm|mm} Default=none.';
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TConductorData.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TConductorData.Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
begin
- NumConductorClassProps := 13;
- inherited Create(dssContext);
- DSSClassType := DSS_OBJECT;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
+
+ inherited Create(dssContext, DSSClsType or DSS_OBJECT, DSSClsName);
+ ClassParents.Add('ConductorData');
end;
destructor TConductorData.Destroy;
@@ -117,168 +130,131 @@ destructor TConductorData.Destroy;
inherited Destroy;
end;
-procedure TConductorData.CountProperties;
+procedure TConductorData.CountPropertiesAndAllocate;
begin
- NumProperties := NumProperties + NumConductorClassProps;
- inherited CountProperties;
+ NumProperties := NumProperties + NumPropsThisClass;
+ inherited CountPropertiesAndAllocate;
end;
procedure TConductorData.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
- PropertyName^[ActiveProperty + 1] := 'Rdc';
- PropertyName^[ActiveProperty + 2] := 'Rac';
- PropertyName^[ActiveProperty + 3] := 'Runits';
- PropertyName^[ActiveProperty + 4] := 'GMRac';
- PropertyName^[ActiveProperty + 5] := 'GMRunits';
- PropertyName^[ActiveProperty + 6] := 'radius';
- PropertyName^[ActiveProperty + 7] := 'radunits';
- PropertyName^[ActiveProperty + 8] := 'normamps';
- PropertyName^[ActiveProperty + 9] := 'emergamps';
- PropertyName^[ActiveProperty + 10] := 'diam';
- PropertyName^[ActiveProperty + 11] := 'Seasons';
- PropertyName^[ActiveProperty + 12] := 'Ratings';
- PropertyName^[ActiveProperty + 13] := 'Capradius';
-
- PropertyHelp^[ActiveProperty + 1] := 'dc Resistance, ohms per unit length (see Runits). Defaults to Rac/1.02 if not specified.';
- PropertyHelp^[ActiveProperty + 2] := 'Resistance at 60 Hz per unit length. Defaults to 1.02*Rdc if not specified.';
- PropertyHelp^[ActiveProperty + 3] := 'Length units for resistance: ohms per ' + LineUnitsHelp;
- PropertyHelp^[ActiveProperty + 4] := 'GMR at 60 Hz. Defaults to .7788*radius if not specified.';
- PropertyHelp^[ActiveProperty + 5] := 'Units for GMR: ' + LineUnitsHelp;
- PropertyHelp^[ActiveProperty + 6] := 'Outside radius of conductor. Defaults to GMR/0.7788 if not specified.';
- PropertyHelp^[ActiveProperty + 7] := 'Units for outside radius: ' + LineUnitsHelp;
- PropertyHelp^[ActiveProperty + 8] := 'Normal ampacity, amperes. Defaults to Emergency amps/1.5 if not specified.';
- PropertyHelp^[ActiveProperty + 9] := 'Emergency ampacity, amperes. Defaults to 1.5 * Normal Amps if not specified.';
- PropertyHelp^[ActiveProperty + 10] := 'Diameter; Alternative method for entering radius.';
- PropertyHelp^[ActiveProperty + 11] := 'Defines the number of ratings to be defined for the wire, to be used only when defining seasonal ratings using the "Ratings" property.';
- PropertyHelp^[ActiveProperty + 12] := 'An array of ratings to be used when the seasonal ratings flag is True. It can be used to insert' +
- CRLF + 'multiple ratings to change during a QSTS simulation to evaluate different ratings in lines.';
- PropertyHelp^[ActiveProperty + 13] := 'Equivalent conductor radius for capacitance calcs. Specify this for bundled conductors. Defaults to same value as radius.';
-
- ActiveProperty := ActiveProperty + NumConductorClassProps;
+ PopulatePropertyNames(ActiveProperty, NumPropsThisClass, PropInfo, False, 'ConductorData');
+
+ PropertyOffset_ConductorData := ActiveProperty;
+ // enums
+ PropertyType[ActiveProperty + ord(TProp.Runits)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ActiveProperty + ord(TProp.Runits)] := ptruint(@obj.FresistanceUnits);
+ PropertyOffset2[ActiveProperty + ord(TProp.Runits)] := PtrInt(DSS.UnitsEnum);
+
+ PropertyType[ActiveProperty + ord(TProp.GMRunits)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ActiveProperty + ord(TProp.GMRunits)] := ptruint(@obj.FGMRUnits);
+ PropertyOffset2[ActiveProperty + ord(TProp.GMRunits)] := PtrInt(DSS.UnitsEnum);
+
+ PropertyType[ActiveProperty + ord(TProp.radunits)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ActiveProperty + ord(TProp.radunits)] := ptruint(@obj.FRadiusUnits);
+ PropertyOffset2[ActiveProperty + ord(TProp.radunits)] := PtrInt(DSS.UnitsEnum);
+
+ // double arrays
+ PropertyType[ActiveProperty + ord(TProp.Ratings)] := TPropertyType.DoubleDArrayProperty;
+ PropertyOffset[ActiveProperty + ord(TProp.Ratings)] := ptruint(@obj.AmpRatings);
+ PropertyOffset2[ActiveProperty + ord(TProp.Ratings)] := ptruint(@obj.NumAmpRatings);
+
+ // double properties (default type)
+ PropertyOffset[ActiveProperty + ord(TProp.Rdc)] := ptruint(@obj.FRDC);
+ PropertyOffset[ActiveProperty + ord(TProp.Rac)] := ptruint(@obj.FR60);
+ PropertyOffset[ActiveProperty + ord(TProp.normamps)] := ptruint(@obj.NormAmps);
+ PropertyOffset[ActiveProperty + ord(TProp.emergamps)] := ptruint(@obj.EmergAmps);
+
+ PropertyOffset[ActiveProperty + ord(TProp.GMRac)] := ptruint(@obj.FGMR60);
+ PropertyOffset[ActiveProperty + ord(TProp.radius)] := ptruint(@obj.Fradius);
+ PropertyOffset[ActiveProperty + ord(TProp.Capradius)] := ptruint(@obj.Fcapradius60);
+
+ PropertyFlags[ActiveProperty + ord(TProp.GMRac)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+ PropertyFlags[ActiveProperty + ord(TProp.radius)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+ PropertyFlags[ActiveProperty + ord(TProp.Capradius)] := [TPropertyFlag.NonZero];
+
+ // scaled double
+ PropertyOffset[ActiveProperty + ord(TProp.diam)] := ptruint(@obj.Fradius);
+ PropertyScale[ActiveProperty + ord(TProp.diam)] := 1.0 / 2.0;
+ PropertyFlags[ActiveProperty + ord(TProp.diam)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero, TPropertyFlag.Redundant];
+ PropertyRedundantWith[ActiveProperty + ord(TProp.diam)] := ActiveProperty + ord(TProp.radius);
+
+ // integer properties
+ PropertyType[ActiveProperty + ord(TProp.Seasons)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ActiveProperty + ord(TProp.Seasons)] := ptruint(@obj.NumAmpRatings);
+
+ ActiveProperty := ActiveProperty + NumPropsThisClass;
inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TConductorData.ClassEdit(const ActiveObj: Pointer; const ParamPointer: Integer);
+procedure TConductorDataObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- Param: String;
+ Idx2: Integer;
begin
- // continue parsing with contents of Parser
- if ParamPointer > 0 then
- with TConductorDataObj(ActiveObj) do
+ Idx2 := Idx - (ParentClass as TConductorData).PropertyOffset_ConductorData;
+ case Idx2 of
+ ord(TProp.Rdc):
+ if FR60 < 0.0 then
+ FR60 := 1.02 * FRDC;
+ ord(TProp.Rac):
+ if FRDC < 0.0 then
+ FRDC := FR60 / 1.02;
+ ord(TProp.GMRac):
begin
- case ParamPointer of
- 1:
- FRDC := Parser.Dblvalue;
- 2:
- FR60 := Parser.DblValue;
- 3:
- FresistanceUnits := GetUnitsCode(Parser.StrValue);
- 4:
- FGMR60 := Parser.DblValue;
- 5:
- FGMRUnits := GetUnitsCode(Parser.StrValue);
- 6:
- Fradius := Parser.DblValue;
- 7:
- FRadiusUnits := GetUnitsCode(Parser.StrValue);
- 8:
- NormAmps := Parser.DblValue;
- 9:
- EmergAmps := Parser.DblValue;
- 10:
- Fradius := Parser.DblValue / 2.0;
- 11:
- begin
- NumAmpRatings := Parser.IntValue;
- setlength(AmpRatings, NumAmpRatings);
- end;
- 12:
- begin
- setlength(AmpRatings, NumAmpRatings);
- Param := Parser.StrValue;
- NumAmpRatings := InterpretDblArray(Param, NumAmpRatings, pointer(AmpRatings));
- end;
- 13:
- Fcapradius60 := Parser.DblValue;
- else
- inherited ClassEdit(ActiveObj, ParamPointer - NumConductorClassProps)
- end;
- {Set defaults}
- case ParamPointer of
- 1:
- if FR60 < 0.0 then
- FR60 := 1.02 * FRDC;
- 2:
- if FRDC < 0.0 then
- FRDC := FR60 / 1.02;
- 4: // GMRac
- begin
- if Fradius < 0.0 then
- Fradius := FGMR60 / 0.7788;
- end;
- 5:
- if FradiusUnits = 0 then
- FradiusUnits := FGMRunits;
- 6, 10: // radius, diam
- begin
- if FGMR60 < 0.0 then
- FGMR60 := 0.7788 * FRadius;
- if Fcapradius60 < 0.0 then
- Fcapradius60 := Fradius; // default to radius
- end;
- 7:
- if FGMRUnits = 0 then
- FGMRunits := FradiusUnits;
- 8:
- if EmergAmps < 0.0 then
- EmergAmps := 1.5 * NormAmps;
- 9:
- if NormAmps < 0.0 then
- NormAmps := EmergAmps / 1.5;
- end;
- {Check for critical errors}
- case ParamPointer of
- 4:
- if (Fradius = 0.0) then
- DoSimpleMsg('Error: Radius is specified as zero for ConductorData.' + Name, 999);
- 6:
- if (FGMR60 = 0.0) then
- DoSimpleMsg('Error: GMR is specified as zero for ConductorData.' + Name, 999);
- end;
+ if Fradius < 0.0 then
+ Fradius := FGMR60 / 0.7788;
+ if (Fradius = 0.0) then
+ DoSimpleMsg('Error: Radius is specified as zero for %s', [FullName], 999);
end;
+ ord(TProp.GMRunits):
+ if FradiusUnits = 0 then
+ FradiusUnits := FGMRunits;
+ ord(TProp.radius), ord(TProp.diam):
+ begin
+ if FGMR60 < 0.0 then
+ FGMR60 := 0.7788 * FRadius;
+ if Fcapradius60 < 0.0 then
+ Fcapradius60 := Fradius; // default to radius
+ end;
+ ord(TProp.radunits):
+ if FGMRUnits = 0 then
+ FGMRunits := FradiusUnits;
+ ord(TProp.normamps):
+ if EmergAmps < 0.0 then
+ EmergAmps := 1.5 * NormAmps;
+ ord(TProp.emergamps):
+ if NormAmps < 0.0 then
+ NormAmps := EmergAmps / 1.5;
+ ord(TProp.Seasons):
+ setlength(AmpRatings, NumAmpRatings);
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-procedure TConductorData.ClassMakeLike(const OtherObj: Pointer);
+procedure TConductorDataObj.MakeLike(OtherObj: Pointer);
var
- OtherConductorData: TConductorDataObj;
+ Other: TObj;
begin
- OtherConductorData := TConductorDataObj(OtherObj);
- with TConductorDataObj(ActiveDSSObject) do
- begin
- FRDC := OtherConductorData.FRDC;
- FR60 := OtherConductorData.FR60;
- FResistanceUnits := OtherConductorData.FResistanceUnits;
- FGMR60 := OtherConductorData.FGMR60;
- Fcapradius60 := OtherConductorData.Fcapradius60;
- FGMRUnits := OtherConductorData.FGMRUnits;
- FRadius := OtherConductorData.FRadius;
- FRadiusUnits := OtherConductorData.FRadiusUnits;
- NormAmps := OtherConductorData.NormAmps;
- EmergAmps := OtherConductorData.EmergAmps;
- end;
- // Inherited ClassMakeLike(OtherObj);
+ inherited MakeLike(OtherObj);
+ Other := TObj(OtherObj);
+ FRDC := Other.FRDC;
+ FR60 := Other.FR60;
+ FResistanceUnits := Other.FResistanceUnits;
+ FGMR60 := Other.FGMR60;
+ Fcapradius60 := Other.Fcapradius60;
+ FGMRUnits := Other.FGMRUnits;
+ FRadius := Other.FRadius;
+ FRadiusUnits := Other.FRadiusUnits;
+ NormAmps := Other.NormAmps;
+ EmergAmps := Other.EmergAmps;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TConductorData Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TConductorDataObj.Create(ParClass: TDSSClass; const ConductorDataName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(ConductorDataName);
+ Name := AnsiLowerCase(ConductorDataName);
DSSObjType := ParClass.DSSClassType;
FRDC := -1.0;
@@ -296,124 +272,9 @@ constructor TConductorDataObj.Create(ParClass: TDSSClass; const ConductorDataNam
AmpRatings[0] := NormAmps;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TConductorDataObj.Destroy;
begin
inherited destroy;
end;
-procedure TConductorDataObj.DumpProperties(F: TFileStream; Complete: Boolean);
-var
- j,
- i: Integer;
- TempStr: String;
-begin
- inherited DumpProperties(F, Complete);
- with ParentClass do
- begin
- for i := 1 to NumProperties do
- begin
- FSWrite(F, '~ ' + PropertyName^[i] + '=');
- case i of
- 1:
- FSWriteln(F, Format('%.6g', [FRDC]));
- 2:
- FSWriteln(F, Format('%.6g', [FR60]));
- 3:
- FSWriteln(F, Format('%s', [LineUnitsStr(FresistanceUnits)]));
- 4:
- FSWriteln(F, Format('%.6g', [FGMR60]));
- 5:
- FSWriteln(F, Format('%s', [LineUnitsStr(FGMRUnits)]));
- 6:
- FSWriteln(F, Format('%.6g', [Fradius]));
- 7:
- FSWriteln(F, Format('%s', [LineUnitsStr(FRadiusUnits)]));
- 8:
- FSWriteln(F, Format('%.6g', [NormAmps]));
- 9:
- FSWriteln(F, Format('%.6g', [EmergAmps]));
- 10:
- FSWriteln(F, Format('%.6g', [radius * 2.0]));
- 11:
- FSWriteln(F, Format('%d', [NumAmpRatings]));
- 12:
- begin
- TempStr := '[';
- for j := 1 to NumAmpRatings do
- TempStr := TempStr + floattoStrf(AmpRatings[j - 1], ffgeneral, 8, 4) + ',';
- TempStr := TempStr + ']';
- FSWriteln(F, TempStr);
- end;
- 13:
- FSWriteln(F, Format('%.6g',[Fcapradius60]));
- end;
- end;
- end;
-end;
-
-function TConductorDataObj.GetPropertyValue(Index: Integer): String;
-Var
- j: Integer;
- Tempstr: String;
-begin
- Result := '';
- case Index of // Special cases
- 1:
- Result := Format('%.6g', [FRDC]);
- 2:
- Result := Format('%.6g', [FR60]);
- 3:
- Result := Format('%s', [LineUnitsStr(FresistanceUnits)]);
- 4:
- Result := Format('%.6g', [FGMR60]);
- 5:
- Result := Format('%s', [LineUnitsStr(FGMRUnits)]);
- 6:
- Result := Format('%.6g', [Fradius]);
- 7:
- Result := Format('%s', [LineUnitsStr(FRadiusUnits)]);
- 8:
- Result := Format('%.6g', [NormAmps]);
- 9:
- Result := Format('%.6g', [EmergAmps]);
- 10:
- Result := Format('%.6g', [radius*2.0]);
- 11:
- Result := Format('%d', [NumAmpRatings]);
- 12:
- begin
- TempStr := '[';
-
- for j := 1 to NumAmpRatings do
- TempStr := TempStr + FloatToStrf(AmpRatings[j-1], ffgeneral, 8, 4) + ',';
-
- TempStr := TempStr + ']';
- Result := TempStr;
- end;
- 13:
- Result := Format('%.6g',[Fcapradius60]);
- else
- Result := Inherited GetPropertyValue(index);
- end;
-end;
-
-procedure TConductorDataObj.InitPropertyValues(ArrayOffset: Integer);
-begin
- PropertyValue[ArrayOffset + 1] := '-1';
- PropertyValue[ArrayOffset + 2] := '-1';
- PropertyValue[ArrayOffset + 3] := 'none';
- PropertyValue[ArrayOffset + 4] := '-1';
- PropertyValue[ArrayOffset + 5] := 'none';
- PropertyValue[ArrayOffset + 6] := '-1';
- PropertyValue[ArrayOffset + 7] := 'none';
- PropertyValue[ArrayOffset + 8] := '-1';
- PropertyValue[ArrayOffset + 9] := '-1';
- PropertyValue[ArrayOffset + 10] := '-1';
- PropertyValue[ArrayOffset + 11] := '1';
- PropertyValue[ArrayOffset + 12] := '[-1]';
- PropertyValue[ArrayOffset + 13] := '-1';
- inherited InitPropertyValues(ArrayOffset + 13);
-end;
-
-end.
+end.
\ No newline at end of file
diff --git a/src/General/DSSObject.pas b/src/General/DSSObject.pas
index 595b5184b..d40d7496c 100644
--- a/src/General/DSSObject.pas
+++ b/src/General/DSSObject.pas
@@ -11,82 +11,75 @@ interface
uses
Classes,
+ ParserDel,
Arraydef,
DSSClass,
NamedObject;
type
+ TDSSObjectPtr = ^TDSSObject;
TDSSObject = class(TNamedObject)
PRIVATE
- function Get_PropertyValue(Index: Integer): String;
- procedure Set_PropertyValue(Index: Integer; const Value: String);
- function Get_Name: String;
procedure Set_Name(const Value: String);
-{$IFDEF DSS_CAPI}
PUBLIC
-{$ELSE}
- PROTECTED
-{$ENDIF}
DSS: TDSSContext;
PropSeqCount: Integer;
- FPropertyValue: pStringArray;
PrpSequence: pIntegerArray;
function GetNextPropertySet(idx: Integer): Integer;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); virtual;
// DSSContext convenience functions
procedure DoErrorMsg(Const S, Emsg, ProbCause: String; ErrNum: Integer);inline;
- procedure DoSimpleMsg(Const S: String; ErrNum:Integer);inline;
+ procedure DoSimpleMsg(Const S: String; ErrNum:Integer);inline;overload;
+ procedure DoSimpleMsg(Const S: String; fmtArgs: Array of Const; ErrNum:Integer);overload;inline;
+
procedure AppendToEventLog(const opdev: String; const action: String);inline;
- function GetCktElementIndex(const FullObjName: String): Integer;inline;
PUBLIC
-
- DSSObjType: Integer; // PD, PC, Monitor, CondCode, etc.
+ DSSObjType: Integer; // PD, PC, Monitor, LineCode, etc.
ParentClass: TDSSClass;
ClassIndex: Integer; // Index into the class collection list
- HasBeenSaved: Boolean;
- Flag: Boolean; // General purpose Flag for each object don't assume inited
+ Flags: TDSSObjectFlags;
constructor Create(ParClass: TDSSClass);
destructor Destroy; OVERRIDE;
- function Edit: Integer; // Allow Calls to edit from object itself
+ function Edit(Parser: TDSSParser): Integer; // Allow Calls to edit from object itself
+ procedure MakeLike(OtherPtr: Pointer); virtual;
+
+ procedure SetAsNextSeq(Index: Integer);
- {Get actual values of properties}
function GetPropertyValue(Index: Integer): String; VIRTUAL; // Use dssclass.propertyindex to get index by name
- procedure InitPropertyValues(ArrayOffset: Integer); VIRTUAL;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); VIRTUAL;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); VIRTUAL;
procedure SaveWrite(F: TFileStream); VIRTUAL;
+ procedure CustomSetRaw(Idx: Integer; Value: String); virtual;
- procedure ClearPropSeqArray;
-
- property PropertyValue[Index: Integer]: String READ Get_PropertyValue WRITE Set_PropertyValue;
+ property PropertyValue[Index: Integer]: String READ GetPropertyValue;
- property Name: String READ Get_Name WRITE Set_Name;
+ property Name: String READ LName WRITE Set_Name;
+ function FullName: String;
+ function DSSClassName: String;
end;
-
implementation
uses
Sysutils,
Utilities,
LoadShape,
- DSSGlobals;
+ DSSGlobals,
+ UComplex, DSSUcomplex,
+ DSSObjectHelper;
-procedure TDSSObject.ClearPropSeqArray;
-var
- i: Integer;
-begin
- PropSeqCount := 0;
- for i := 1 to ParentClass.NumProperties do
- PrpSequence^[i] := 0;
+procedure TDSSObject.CustomSetRaw(Idx: Integer; Value: String);
+begin
+ DoSimpleMsg('Error: base CustomSetRaw reached', 8754);
end;
constructor TDSSObject.Create(ParClass: TDSSClass);
@@ -97,89 +90,69 @@ constructor TDSSObject.Create(ParClass: TDSSClass);
DSSObjType := 0;
PropSeqCount := 0;
ParentClass := ParClass;
- FPropertyValue := Allocmem(SizeOf(FPropertyValue^[1]) * ParentClass.NumProperties);
-
- // init'd to zero when allocated
+ // init'd to zero when allocated
PrpSequence := Allocmem(SizeOf(PrpSequence^[1]) * ParentClass.NumProperties);
- HasBeenSaved := FALSE;
-
+ Flags := [];
end;
destructor TDSSObject.Destroy;
-
-var
- i: Integer;
begin
if DSS = nil then
begin
inherited Destroy;
exit;
end;
-
- for i := 1 to ParentClass.NumProperties do
- FPropertyValue^[i] := '';
- Reallocmem(FPropertyValue, 0);
Reallocmem(PrpSequence, 0);
inherited Destroy;
end;
-
-procedure TDSSObject.DumpProperties(F: TFileStream; Complete: Boolean);
+procedure TDSSObject.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
+var
+ i: Integer;
begin
FSWriteln(F);
- FSWriteln(F, 'New ' + DSSClassName + '.' + Name);
+ FSWriteln(F, 'New ' + EncloseQuotes(FullName));
+ if Leaf then
+ begin
+ with ParentClass do
+ for i := 1 to NumProperties do
+ FSWriteLn(F, '~ ' + PropertyName^[i] + '=' + GetPropertyValue(i));
+
+ if Complete then
+ FSWriteln(F);
+ end;
end;
-function TDSSObject.Edit: Integer;
+function TDSSObject.Edit(Parser: TDSSParser): Integer;
begin
ParentClass.Active := ClassIndex;
- Result := ParentClass.Edit;
+ Result := ParentClass.Edit(Parser);
end;
-function TDSSObject.GetPropertyValue(Index: Integer): String;
-begin
- Result := FPropertyValue^[Index]; // Default Behavior for all DSS Objects
-end;
-
-function TDSSObject.Get_PropertyValue(Index: Integer): String;
+procedure TDSSObject.MakeLike(OtherPtr: Pointer);
+var
+ other: TDSSObject;
begin
- Result := GetPropertyValue(Index); // This is virtual function that may call routine
+ other := TDSSObject(OtherPtr);
+ PropSeqCount := other.PropSeqCount;
+ Move(other.PrpSequence[1], PrpSequence[1], SizeOf(PrpSequence[1]) * ParentClass.NumProperties);
end;
-procedure TDSSObject.InitPropertyValues(ArrayOffset: Integer);
+function TDSSObject.GetPropertyValue(Index: Integer): String;
begin
- PropertyValue[ArrayOffset + 1] := ''; //Like Property
-
- // Clear propertySequence Array after initialization
- ClearPropSeqArray;
-
+ ParentClass.GetObjPropertyValue(self, Index, Result);
end;
procedure TDSSObject.SaveWrite(F: TFileStream);
var
iprop: Integer;
str: String;
- LShpFlag,
- NptsRdy: Boolean; // Created to know the that object is loadshape
begin
- {Write only properties that were explicitly set in the
- final order they were actually set}
- LShpFlag := FALSE;
- NptsRdy := FALSE;
- with ParentClass do
- begin
- if ParentClass.Name = 'LoadShape' then
- begin
- iProp := 1;
- LShpFlag := TRUE
- end
- else
- iProp := GetNextPropertySet(0); // Works on ActiveDSSObject
- end;
-// The part above was created to guarantee that the npts property will be the
-// first to be declared when saving LoadShapes
+ // Write only properties that were explicitly set in the
+ // final order they were actually set
+ iProp := GetNextPropertySet(-9999999);
while iProp > 0 do
begin
str := trim(PropertyValue[iProp]);
@@ -188,22 +161,10 @@ procedure TDSSObject.SaveWrite(F: TFileStream);
if Length(str) > 0 then
begin
with ParentClass do
- FSWrite(F, ' ' + PropertyName^[RevPropertyIdxMap[iProp]]);
+ FSWrite(F, ' ' + PropertyName[iProp]);
FSWrite(F, '=' + CheckForBlanks(str));
end;
- if LShpFlag then
- begin
- iProp := GetNextPropertySet(0); // starts over
- LShpFlag := FALSE; // The npts is already processed
- NptsRdy := TRUE // Flag to not repeat it again
- end
- else
- begin
- iProp := GetNextPropertySet(iProp);
- if NptsRdy then
- if iProp = 1 then
- iProp := GetNextPropertySet(iProp);
- end;
+ iProp := GetNextPropertySet(iProp);
end;
end;
@@ -220,7 +181,7 @@ function TDSSObject.GetNextPropertySet(idx: Integer): Integer;
idx := PrpSequence^[idx];
for i := 1 to ParentClass.NumProperties do
begin
- if PrpSequence^[i] > idx then
+ if (PrpSequence^[i] <> 0) and (PrpSequence^[i] > idx) then
if PrpSequence^[i] < Smallest then
begin
Smallest := PrpSequence^[i];
@@ -237,39 +198,45 @@ procedure TDSSObject.Set_Name(const Value: String);
LocalName := Value;
end;
-function TDSSObject.Get_Name: String;
+procedure TDSSObject.SetAsNextSeq(Index: Integer);
begin
- Result := LocalName;
-end;
-
-procedure TDSSObject.Set_PropertyValue(Index: Integer;
- const Value: String);
-begin
- FPropertyValue^[Index] := Value;
-
// Keep track of the order in which this property was accessed for Save Command
Inc(PropSeqCount);
PrpSequence^[Index] := PropSeqCount;
end;
-procedure TDSSObject.DoErrorMsg(Const S, Emsg, ProbCause: String; ErrNum: Integer);inline;
+procedure TDSSObject.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
+begin
+end;
+
+procedure TDSSObject.DoErrorMsg(Const S, Emsg, ProbCause: String; ErrNum: Integer);
begin
DSSGlobals.DoErrorMsg(DSS, S, Emsg, ProbCause, ErrNum)
end;
-procedure TDSSObject.DoSimpleMsg(Const S: String; ErrNum:Integer);inline;
+procedure TDSSObject.DoSimpleMsg(Const S: String; ErrNum:Integer);
begin
DSSGlobals.DoSimpleMsg(DSS, S, ErrNum)
end;
+
+procedure TDSSObject.DoSimpleMsg(Const S: String; fmtArgs: Array of Const; ErrNum:Integer);
+begin
+ DSSGlobals.DoSimpleMsg(DSS, DSSTranslate(S), fmtArgs, ErrNum)
+end;
-procedure TDSSObject.AppendToEventLog(const opdev: String; const action: String);inline;
+procedure TDSSObject.AppendToEventLog(const opdev: String; const action: String);
begin
Utilities.AppendtoEventLog(DSS, opdev, action);
end;
-function TDSSObject.GetCktElementIndex(const FullObjName: String): Integer;inline;
+function TDSSObject.FullName: String;
+begin
+ Result := ParentClass.Name + '.' + Name;
+end;
+
+function TDSSObject.DSSClassName: String;
begin
- Result := Utilities.GetCktElementIndex(DSS, FullObjName)
+ Result := ParentClass.Name;
end;
end.
diff --git a/src/General/DSSObjectHelper.pas b/src/General/DSSObjectHelper.pas
index 5b7853ad7..730bcb865 100644
--- a/src/General/DSSObjectHelper.pas
+++ b/src/General/DSSObjectHelper.pas
@@ -3,65 +3,1632 @@
interface
uses
+ Classes,
DSSObject,
DSSClass,
- ParserDel,
Circuit,
- ArrayDef;
+ ArrayDef,
+ CAPI_Types;
type
TDSSClassHelper = class helper for TDSSClass
private
- function GetAuxParser: TParser; inline;
function GetCircuit: TDSSCircuit; inline;
- function GetParser: TParser; inline;
- function GetDSSObject: TDSSObject; inline;
protected
property ActiveCircuit: TDSSCircuit read GetCircuit;
- property ActiveDSSObject: TDSSObject read GetDSSObject;
- property AuxParser: TParser read GetAuxParser;
- property Parser: TParser read GetParser;
+ public
+ procedure AddProperties_Double(props: Array of Integer; ptrs: Array of PDouble);
+ procedure AddProperties_Object(props: Array of Integer; ptrs: Array of TDSSObjectPtr; clss: Array of TDSSClass);
+
+ function ParseObjPropertyValue(Obj: Pointer; Index: Integer; const Value: String; out prevInt: Integer): Boolean;
+ function GetObjPropertyValue(obj: Pointer; Index: Integer; out PropStr: String): Boolean;
+
+ //TODO: add error as result for the 16 following functions
+
+ procedure SetObjDouble(ptr: Pointer; Index: Integer; Value: Double);
+ procedure SetObjInteger(ptr: Pointer; Index: Integer; Value: Integer; prevInt: PInteger);
+ procedure SetObjString(ptr: Pointer; Index: Integer; Value: String);
+ procedure SetObjObject(ptr: Pointer; Index: Integer; Value: TDSSObject);
+
+ procedure SetObjDoubles(ptr: Pointer; Index: Integer; Value: PDouble; ValueCount: Integer);
+ procedure SetObjIntegers(ptr: Pointer; Index: Integer; Value: PInteger; ValueCount: Integer);
+ procedure SetObjStrings(ptr: Pointer; Index: Integer; Value: PPAnsiChar; ValueCount: Integer);
+ procedure SetObjObjects(ptr: Pointer; Index: Integer; Value: TDSSObjectPtr; ValueCount: Integer);
+
+ function GetObjDouble(Obj: Pointer; Index: Integer): Double;
+ function GetObjInteger(Obj: Pointer; Index: Integer): Integer;
+ function GetObjString(Obj: Pointer; Index: Integer): String;
+ function GetObjObject(Obj: Pointer; Index: Integer): TDSSObject;
+
+ procedure GetObjDoubles(Obj: Pointer; Index: Integer; var ResultPtr: PDouble; ResultCount: PAPISize);
+ procedure GetObjIntegers(Obj: Pointer; Index: Integer; var ResultPtr: PInteger; ResultCount: PAPISize);
+ procedure GetObjStrings(Obj: Pointer; Index: Integer; var ResultPtr: PPAnsiChar; ResultCount: PAPISize);
+ procedure GetObjObjects(Obj: Pointer; Index: Integer; var ResultPtr: PPointer; ResultCount: PAPISize);
end;
TDSSObjectHelper = class helper for TDSSObject
private
- function GetAuxParser: TParser; inline;
function GetCircuit: TDSSCircuit; inline;
- function GetParser: TParser; inline;
protected
property ActiveCircuit: TDSSCircuit read GetCircuit;
- property AuxParser: TParser read GetAuxParser;
- property Parser: TParser read GetParser;
public
- function InterpretDblArray(const s: String; MaxValues: Integer; ResultArray: pDoubleArray): Integer;inline;
- function InterpretIntArray(const s: String; MaxValues: Integer; ResultArray: pIntegerArray): Integer;inline;
+ function AdjustInputFilePath(const Value: String): String;
+
+ // Set[Property|Double|Integer|...] calls BeginEdit and EndEdit, for convenience, if not already in an active edit
+ function ParsePropertyValue(Index: Integer; Value: String): Boolean;
+ function SetDouble(Index: Integer; Value: Double): Boolean;
+ function SetInteger(Index: Integer; Value: Integer): Boolean;
+ function SetString(Index: Integer; Value: String): Boolean;
+ function SetObject(Index: Integer; Value: TDSSObject): Boolean;
+ function SetDoubles(Index: Integer; Value: Array of Double): Boolean; overload;
+ function SetDoubles(Index: Integer; Value: PDouble; ValueCount: Integer): Boolean; overload;
+ function SetIntegers(Index: Integer; Value: Array of Integer): Boolean; overload;
+ function SetIntegers(Index: Integer; Value: PInteger; ValueCount: Integer): Boolean; overload;
+ function SetStrings(Index: Integer; Value: Array of String): Boolean; overload;
+ function SetObjects(Index: Integer; Value: Array of TDSSObject): Boolean; overload;
+ function SetObjects(Index: Integer; Value: TDSSObjectPtr; ValueCount: Integer): Boolean; overload;
+ function SetStrings(Index: Integer; Value: PPAnsiChar; ValueCount: Integer): Boolean; overload;
+
+ function GetDouble(Index: Integer): Double;
+ function GetInteger(Index: Integer): Integer;
+ function GetString(Index: Integer): String;
+ function GetObject(Index: Integer): TDSSObject;
+
+ procedure GetDoubles(Index: Integer; var ResultPtr: PDouble; ResultCount: PAPISize);
+ procedure GetIntegers(Index: Integer; var ResultPtr: PInteger; ResultCount: PAPISize);
+ procedure GetStrings(Index: Integer; var ResultPtr: PPAnsiChar; ResultCount: PAPISize);
+ procedure GetObjects(Index: Integer; var ResultPtr: PPointer; ResultCount: PAPISize);
+
+ procedure BeginEdit(Activate: Boolean);
+ procedure EndEdit(NumChanges: Integer);
end;
implementation
uses
DSSHelper,
- Utilities;
-
+ CktElement,
+ Utilities,
+ SysUtils,
+ UComplex, DSSUcomplex,
+ UcMatrix,
+ ParserDel,
+ Math,
+ CAPI_Utils;
+
+type
+ PLongBool = ^LongBool;
+ PPDouble = ^PDouble;
+ PPString= ^PString;
+ PPByte = ^PByte;
+ TDSSObjectPtrPtr = ^TDSSObjectPtr;
+ PStringList = ^TStringList;
+
function TDSSClassHelper.GetCircuit: TDSSCircuit;
begin
Result := DSS.ActiveCircuit;
end;
-function TDSSClassHelper.GetParser: TParser; inline;
+function TDSSClassHelper.ParseObjPropertyValue(Obj: Pointer; Index: Integer; const Value: String; out prevInt: Integer): Boolean;
+// This handles most of the parsing and passes the processed values to
+// the specific functions (e.g. SetObjInteger) if possible, to reduce code duplication.
+var
+ doubleVal: Double;
+ doubleVals: Array of Double;
+ doublePtr: PDouble;
+ integerPtr, positionPtr: PInteger;
+ complexPtr: PComplex;
+ complexVal: Complex;
+ i, intVal: Integer;
+ dataPtr: Pointer;
+ ptype: TPropertyType;
+ otherObjPtr: TDSSObjectPtr;
+ otherObj: TDSSObject;
+ cls: TDSSClass;
+ scale: Double;
+ stringList: TStringList;
+ stringListPtr: PStringList;
+ flags: TPropertyFlags;
+ DataStr: String;
+ enumInfo: TDSSEnum;
+
+ OrderFound, Norder, maxSize: Integer;
+ mat, matbak: TCmatrix;
+ darray: PDoubleArray;
+ iarray: pIntegerArray;
+
+ errCode: Word;
+
+ ElemName: String;
+
+ PropParser: TDSSParser;
+ objs: Array of TDSSObject;
+
+ function GetDouble(Value: String): Double;
+ begin
+ Val(Value, Result, errCode);
+ if errCode <> 0 then
+ begin
+ PropParser.CmdString := '(' + Value + ')';
+ PropParser.NextParam;
+ Result := PropParser.DblValue;
+ end;
+ end;
+ function GetInteger(Value: String): Integer;
+ begin
+ Val(Value, Result, errCode);
+ if errCode <> 0 then
+ begin
+ PropParser.CmdString := '(' + Value + ')';
+ PropParser.NextParam;
+ Result := PropParser.IntValue;
+ end;
+ end;
+ function GetComplex(const s: String): Complex;
+ // moved from Utilities -- previously InterpretComplex
+ begin
+ PropParser.CmdString := S;
+ PropParser.NextParam;
+ Result.re := PropParser.dblvalue;
+ PropParser.NextParam;
+ Result.im := PropParser.dblvalue;
+ end;
begin
- Result := DSS.Parser;
+ Result := False;
+
+ if (Index < 0) or (Index > NumProperties) or
+ (PropertyOffset[Index] = -1) then
+ Exit;
+ flags := PropertyFlags[Index];
+
+ if TPropertyFlag.CustomSetRaw in flags then
+ begin
+ TDSSObject(obj).CustomSetRaw(Index, Value);
+ Result := True;
+ Exit;
+ end;
+
+ PropParser := DSS.PropParser;
+
+ ptype := PropertyType[Index];
+ case ptype of
+ TPropertyType.StringSilentROFunctionProperty:
+ //TPropertyType.DoubleArraySilentROFunctionProperty:
+ begin
+ //TODO: error message, optionally
+ Result := True;
+ end;
+ TPropertyType.DoubleOnArrayProperty,
+ TPropertyType.DoubleOnStructArrayProperty,
+ TPropertyType.DoubleProperty:
+ begin
+ if flags = [] then
+ begin
+ // Most properties don't have any flags set, just skip the checks
+ SetObjDouble(Obj, Index, GetDouble(Value));
+ Result := True;
+ Exit;
+ end;
+ if TPropertyFlag.SilentReadOnly in flags then
+ begin
+ Result := True;
+ Exit;
+ end;
+
+ if TPropertyFlag.IntervalUnits in flags then
+ begin
+ Val(Value, doubleVal, errCode);
+ if errCode <> 0 then
+ begin
+ Val(Copy(Value, 1, Length(Value) - 1), doubleVal, errCode);
+ if errCode <> 0 then
+ begin
+ DoSimpleMsg(
+ '%s.%s: Error in specification, invalid value: "%s". Units can only be h, m, or s (single char only). If omitted, "s" is assumed.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value],
+ 2020034);
+ Exit;
+ end;
+
+ case Value[High(Value)] of
+ 'h':
+ doubleVal := doubleVal * 3600;
+ 'm':
+ doubleVal := doubleVal * 60;
+ 's':
+ ;
+ else
+ begin
+ DoSimpleMsg(
+ '%s.%s: Error in specification, invalid value: "%s". Units can only be h, m, or s (single char only). If omitted, "s" is assumed.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value],
+ 2020035);
+ Exit;
+ end;
+ end;
+ end;
+ end
+ else
+ doubleVal := GetDouble(Value);
+
+ SetObjDouble(Obj, index, doubleVal);
+ Result := True;
+ end;
+ TPropertyType.MappedStringEnumOnStructArrayProperty:
+ begin
+ SetObjInteger(Obj, Index, TDSSEnum(Pointer(PropertyOffset2[Index])).StringToOrdinal(AnsiLowerCase(Value)), @prevInt);
+ Result := True;
+ end;
+ TPropertyType.StringEnumActionProperty:
+ begin
+ SetObjInteger(Obj, Index, TDSSEnum(Pointer(PropertyOffset2[Index])).StringToOrdinal(AnsiLowerCase(Value)), @prevInt);
+ Result := True;
+ end;
+ TPropertyType.IntegerOnStructArrayProperty,
+ TPropertyType.IntegerProperty:
+ begin
+ if TPropertyFlag.IntervalUnits in flags then
+ begin
+ Val(Value, intVal, errCode);
+ if errCode <> 0 then
+ begin
+ Val(Copy(Value, 1, Length(Value) - 1), intVal, errCode);
+ if errCode <> 0 then
+ begin
+ DoSimpleMsg(
+ '%s.%s: Error in specification, invalid value: "%s". Units can only be h, m, or s (single char only). If omitted, "s" is assumed.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value],
+ 2020034);
+ Exit;
+ end;
+
+ case Value[High(Value)] of
+ 'h':
+ intVal := intVal * 3600;
+ 'm':
+ intVal := intVal * 60;
+ 's':
+ ;
+ else
+ begin
+ DoSimpleMsg(
+ '%s.%s: Error in specification, invalid value: "%s". Units can only be h, m, or s (single char only). If omitted, "s" is assumed.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value],
+ 2020035);
+
+ Exit;
+ end;
+ end;
+ end;
+ end
+ else
+ intVal := GetInteger(Value);
+
+ SetObjInteger(Obj, Index, intVal, @prevInt);
+ Result := True;
+ end;
+ TPropertyType.MappedStringEnumProperty:
+ begin
+ if (TPropertyFlag.ConditionalReadOnly in flags) and (PLongBool(PByte(obj) + PropertyOffset3[Index])^) then
+ begin
+ Result := True;
+ Exit;
+ end;
+ SetObjInteger(Obj, Index, TDSSEnum(Pointer(PropertyOffset2[Index])).StringToOrdinal(AnsiLowerCase(Value)), @prevInt);
+ Result := True;
+ end;
+ TPropertyType.MappedIntEnumProperty:
+ begin
+ enumInfo := TDSSEnum(PropertyOffset2[Index]);
+ intVal := GetInteger(Value);
+ if not (enumInfo.IsOrdinalValid(intVal)) then
+ begin
+ DoSimpleMsg(
+ Format('%s.%s: "%s" is not a valid value.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value]
+ ), 401);
+ DoSimpleMsg('Invalid value (%d).', [intVal], 5004);
+ Exit;
+ end;
+ SetObjInteger(Obj, Index, intVal, @prevInt);
+ Result := True;
+ end;
+ TPropertyType.EnabledProperty,
+ TPropertyType.BooleanActionProperty,
+ TPropertyType.BooleanProperty:
+ begin
+ SetObjInteger(Obj, Index, Integer(InterpretYesNo(Value)), @prevInt);
+ Result := True;
+ end;
+ TPropertyType.StringListProperty:
+ begin
+ if TPropertyFlag.WriteByFunction in flags then
+ stringList := TStringList.Create()
+ else
+ begin
+ stringListPtr := PStringList(PByte(obj) + PropertyOffset[Index]);
+ stringList := stringListPtr^;
+ end;
+ InterpretTStringListArray(DSS, Value, stringList);
+ if TPropertyFlag.WriteByFunction in flags then
+ TWriteStringListPropertyFunction(Pointer(PropertyWriteFunction[Index]))(obj, stringList);
+
+ Result := True;
+ end;
+ TPropertyType.ComplexProperty:
+ begin
+ complexPtr := PComplex(PByte(obj) + PropertyOffset[Index]);
+ complexPtr^ := GetComplex(Value);
+ Result := True;
+ end;
+ TPropertyType.ComplexPartsProperty:
+ begin
+ complexVal := GetComplex(Value);
+ doublePtr := PDouble(PByte(obj) + PropertyOffset[Index]);
+ doublePtr^ := complexVal.re;
+ doublePtr := PDouble(PByte(obj) + PropertyOffset2[Index]);
+ doublePtr^ := complexVal.im;
+ Result := True;
+ end;
+ TPropertyType.BusProperty,
+ TPropertyType.BusOnStructArrayProperty,
+ TPropertyType.StringProperty,
+ TPropertyType.MakeLikeProperty,
+ TPropertyType.StringOnArrayProperty,
+ TPropertyType.StringOnStructArrayProperty:
+ begin
+ SetObjString(Obj, Index, Value);
+ Result := True;
+ end;
+ TPropertyType.BusesOnStructArrayProperty:
+ begin
+ // Number of items
+ intVal := PInteger(PByte(obj) + PropertyOffset[Index])^;
+ // Current position
+ positionPtr := PInteger(PByte(obj) + PropertyStructArrayIndexOffset);
+
+ PropParser.CmdString := Value; // Load up Parser
+
+ // Loop for no more than the expected number of items; Ignore omitted values
+ for i := 1 to intVal do
+ begin
+ PropParser.NextParam; // ignore any parameter name not expecting any
+ DataStr := PropParser.StrValue;
+ if Length(DataStr) > 0 then
+ TDSSCktElement(obj).SetBus(i, DataStr);
+ end;
+ positionPtr^ := intVal; // match the effective behavior of the original code
+
+ Result := True;
+ end;
+ TPropertyType.IntegerArrayProperty:
+ begin
+ integerPtr := PInteger(PByte(obj) + PropertyOffset2[Index]);
+ dataPtr := PByte(obj) + PropertyOffset[Index];
+ if PPInteger(dataPtr)^ = NIL then
+ begin
+ // If not initialized, allocate here.
+ // Note that this should not be used with dynamic arrays
+ ReAllocmem(PPInteger(dataPtr)^, Sizeof(Integer) * integerPtr^);
+ end;
+ integerPtr^ := InterpretIntArray(DSS,
+ Value,
+ integerPtr^,
+ pIntegerArray(PPInteger(dataPtr)^)
+ );
+ Result := True;
+ end;
+ TPropertyType.MappedStringEnumArrayProperty:
+ begin
+ if (TPropertyFlag.SizeIsFunction in flags) then
+ maxSize := TIntegerPropertyFunction(Pointer(PropertyOffset3[Index]))(obj)
+ else
+ maxSize := PropertyOffset3[Index];
+
+ integerPtr := PPInteger(PByte(obj) + PropertyOffset[Index])^;
+
+ PropParser.CmdString := Value; // Load up Parser
+ for i := 1 to maxSize do
+ begin
+ PropParser.NextParam; // ignore any parameter name not expecting any
+ DataStr := PropParser.StrValue;
+
+ if Length(DataStr) > 0 then
+ integerPtr^ := TDSSEnum(Pointer(PropertyOffset2[Index])).StringToOrdinal(PropParser.StrValue);
+
+ Inc(integerPtr);
+ end;
+ Result := True;
+ end;
+ TPropertyType.MappedStringEnumArrayOnStructArrayProperty:
+ begin
+ // Number of items
+ intVal := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+
+ // Current position
+ positionPtr := PInteger(PByte(obj) + PropertyStructArrayIndexOffset);
+
+ // Pointer to the first of the target fields
+ integerPtr := PInteger(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ +
+ PropertyOffset[Index]
+ );
+
+ PropParser.CmdString := Value; // Load up Parser
+ // Loop for no more than the expected number of items; Ignore omitted values
+ for i := 1 to intVal do
+ begin
+ PropParser.NextParam; // ignore any parameter name not expecting any
+ DataStr := PropParser.StrValue;
+
+ if Length(DataStr) > 0 then
+ integerPtr^ := TDSSEnum(Pointer(PropertyOffset2[Index])).StringToOrdinal(PropParser.StrValue);
+
+ // Move to the next position
+ integerPtr := PInteger(ptruint(integerPtr) + PropertyStructArrayStep);
+ end;
+
+ positionPtr^ := intVal; // match the effective behavior of the original code
+
+ Result := True;
+ end;
+ TPropertyType.DoubleSymMatrixProperty:
+ begin
+ scale := PropertyScale[Index];
+ Norder := PInteger(PByte(obj) + PropertyOffset2[Index])^; // e.g. Fnphases
+ dataPtr := PByte(obj) + PropertyOffset[Index];
+ darray := Allocmem(Sizeof(Double) * Norder * Norder);
+ PropParser.Token := Value;
+ OrderFound := PropParser.ParseAsSymMatrix(Norder, darray, 1, scale);
+ if OrderFound = Norder then
+ begin
+ // Replace matrix with the new pointer
+ ReAllocMem(PPDouble(dataPtr)^, 0);
+ PPDouble(dataPtr)^ := Pointer(darray);
+ end
+ else
+ begin
+ //TODO: Error message
+ // No changes, just dispose the temporary copy
+ FreeMem(darray);
+ end;
+ Result := True;
+ end;
+ TPropertyType.ComplexPartSymMatrixProperty:
+ begin
+ if TPropertyFlag.ScaledByFunction in flags then
+ scale := TPropertyScaleFunction(Pointer(PropertyOffset2[Index]))(obj, False) // False = Setter scale
+ else
+ scale := PropertyScale[Index];
+
+ mat := PCMatrix(Pointer(PByte(obj) + PropertyOffset[Index]))^;
+ doublePtr := PDouble(mat.GetValuesArrayPtr(Norder));
+ if TPropertyFlag.ImagPart in flags then
+ Inc(doublePtr);
+
+ matbak := TCMatrix.CreateMatrix(Norder);
+ matbak.CopyFrom(mat);
+
+ PropParser.Token := Value;
+ OrderFound := PropParser.ParseAsSymMatrix(Norder, PDoubleArray(doublePtr), 2, scale);
+ if OrderFound <> Norder then
+ begin
+ //TODO: Proper error message
+ // No changes, restore the original copy
+ mat.CopyFrom(matbak);
+ end;
+ matbak.Free;
+ Result := True;
+ end;
+
+ TPropertyType.DoubleArrayProperty,
+ TPropertyType.DoubleDArrayProperty,
+ TPropertyType.DoubleFArrayProperty,
+ TPropertyType.DoubleVArrayProperty:
+ begin
+ integerPtr := NIL;
+ if TPropertyFlag.SizeIsFunction in flags then
+ begin
+ intVal := TIntegerPropertyFunction(Pointer(PropertyOffset3[Index]))(obj);
+ integerPtr := @intVal;
+ end
+ else
+ if (ptype <> TPropertyType.DoubleFArrayProperty) then
+ integerPtr := PInteger(PByte(obj) + PropertyOffset2[Index]); // Size pointer
+
+ if (TPropertyFlag.AllowNone in flags) and (Length(Value) = 4) and (Comparetext(Value, 'NONE') = 0) then
+ begin
+ if integerPtr <> NIL then
+ integerPtr^ := 0;
+ Result := True;
+ Exit;
+ end;
+
+ if TPropertyFlag.WriteByFunction in flags then
+ begin
+ SetLength(doubleVals, integerPtr^);
+ dataPtr := @doubleVals[0];
+ end
+ else
+ dataPtr := PByte(obj) + PropertyOffset[Index];
+
+ if ((ptype = TPropertyType.DoubleArrayProperty) or (ptype = TPropertyType.DoubleVArrayProperty))
+ and (PPDouble(dataPtr)^ = NIL) then
+ begin
+ // If not initialized, allocate here.
+ // Note that this should not be used with dynamic arrays
+ ReAllocmem(PPDouble(dataPtr)^, Sizeof(Double) * integerPtr^);
+ end;
+
+ case ptype of
+ TPropertyType.DoubleArrayProperty,
+ TPropertyType.DoubleDArrayProperty:
+ begin
+ if TPropertyFlag.ArrayMaxSize in flags then
+ maxSize := PropertyOffset3[Index]
+ else
+ maxSize := integerPtr^;
+
+ if not (TPropertyFlag.IntegerToDouble in flags) then
+ integerPtr^ := InterpretDblArray(DSS,
+ Value,
+ maxSize,
+ pDoubleArray(PPDouble(dataPtr)^)
+ )
+ else
+ begin
+ iarray := Allocmem(Sizeof(Integer) * integerPtr^);
+ integerPtr^ := InterpretIntArray(DSS,
+ Value,
+ maxSize,
+ iarray
+ );
+ darray := pDoubleArray(PPDouble(dataPtr)^);
+ for i := 1 to integerPtr^ do
+ darray[i] := iarray[i];
+
+ ReallocMem(iarray, 0);
+ end;
+ end;
+ TPropertyType.DoubleVArrayProperty:
+ begin
+ PropParser.Token := Value;
+ PropParser.ParseAsVector(integerPtr^, pDoubleArray(PPDouble(dataPtr)^));
+ end;
+ TPropertyType.DoubleFArrayProperty:
+ begin
+ PropParser.Token := Value;
+ prevInt := PropParser.ParseAsVector(PropertyOffset2[Index], pDoubleArray(PDouble(dataPtr)));
+ if prevInt <> PropertyOffset2[Index] then
+ begin
+ //TODO: error/warn if wrong number of values specified? (Only for some properties)
+ end;
+ end;
+ end;
+
+ Result := True;
+ scale := PropertyScale[Index];
+
+ if scale <> 1 then
+ begin
+ doublePtr := PPDouble(dataPtr)^;
+ for i := 1 to integerPtr^ do
+ begin
+ doublePtr^ := doublePtr^ * scale;
+ Inc(doublePtr);
+ end;
+ end;
+
+ if TPropertyFlag.WriteByFunction in flags then
+ begin
+ SetLength(doubleVals, integerPtr^);
+ TWriteDoublesPropertyFunction(Pointer(PropertyWriteFunction[Index]))(obj, @doubleVals[0], Length(doubleVals))
+ end
+ end;
+ TPropertyType.DoubleArrayOnStructArrayProperty:
+ begin
+ // Number of items
+ intVal := PInteger(PByte(obj) + PropertyOffset2[Index])^;
+
+ // Current position
+ positionPtr := PInteger(PByte(obj) + PropertyStructArrayIndexOffset);
+
+ // Pointer to the first of the target fields
+ doublePtr := PDouble(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ +
+ PropertyOffset[Index]
+ );
+
+ scale := PropertyScale[Index];
+
+ PropParser.CmdString := Value; // Load up Parser
+ // Loop for no more than the expected number of items; Ignore omitted values
+ for i := 1 to intVal do
+ begin
+ PropParser.NextParam; // ignore any parameter name not expecting any
+ DataStr := PropParser.StrValue;
+
+ if Length(DataStr) > 0 then
+ doublePtr^ := PropParser.Dblvalue * scale;
+
+ // Move to the next position
+ doublePtr := PDouble(ptruint(doublePtr) + PropertyStructArrayStep);
+ end;
+
+ positionPtr^ := intVal; // match the effective behavior of the original code
+
+ Result := True;
+ end;
+ TPropertyType.DSSObjectReferenceProperty:
+ begin
+ if not (TPropertyFlag.OnArray in flags) then
+ otherObjPtr := TDSSObjectPtr((PtrUint(obj) + PtrUint(PropertyOffset[Index])))
+ else
+ begin
+ otherObjPtr := TDSSObjectPtr(PPByte(PtrUint(obj) + PropertyOffset[Index])^);
+ inc(otherObjPtr, PInteger(PtrUint(obj) + PropertyStructArrayIndexOffset)^ - 1);
+ end;
+
+ cls := Pointer(PropertyOffset2[Index]);
+ ElemName := Value;
+ if cls <> NIL then
+ begin
+ if TPropertyFlag.CheckForVar in flags then
+ PropParser.CheckforVar(ElemName);
+
+ if Length(Value) = 0 then //TODO: allow "none" to clear it too?
+ otherObj := NIL
+ else
+ otherObj := cls.Find(ElemName); //TODO: add False?
+
+ if otherObj = NIL then
+ begin
+ DoSimpleMsg(
+ Format('%s.%s: %s object "%s" not found.',
+ [TDSSObject(obj).FullName, PropertyName[Index], cls.Name, Value]
+ ), 401);
+ //TODO: stop?
+ end;
+ end
+ else
+ begin
+ ElemName := ConstructElemName(DSS, AnsiLowerCase(Value)); // substitute @var value if any
+ intVal := GetCktElementIndex(DSS, ElemName);
+ otherObj := NIL;
+ if intVal > 0 then
+ otherObj := DSS.ActiveCircuit.CktElements.Get(intVal);
+
+ if otherObj = NIL then
+ begin
+ DoSimpleMsg(
+ Format('%s.%s: CktElement "%s" not found.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value]
+ ), 402);
+ //TODO: stop?
+ end;
+ end;
+
+ //TODO: add validation -- e.g. PD element for EnergyMeter
+ if (TPropertyFlag.WriteByFunction in flags) then
+ TWriteObjRefPropertyFunction(Pointer(PropertyWriteFunction[Index]))(obj, otherObj)
+ else
+ otherObjPtr^ := otherObj;
+
+ Result := True;
+ end;
+ TPropertyType.DSSObjectReferenceArrayProperty:
+ begin
+ PropParser.CmdString := Value;
+
+ // Class of the objects
+ cls := Pointer(PropertyOffset2[Index]);
+
+ if TPropertyFlag.WriteByFunction in flags then
+ begin
+ SetLength(objs, 0);
+ PropParser.NextParam;
+ if cls <> NIL then
+ begin
+ while Length(PropParser.StrValue) > 0 do
+ begin
+ ElemName := PropParser.StrValue;
+ if TPropertyFlag.CheckForVar in flags then
+ PropParser.CheckforVar(ElemName);
+
+ otherObj := cls.Find(ElemName, False);
+ if otherObj = NIL then
+ begin
+ DoSimpleMsg(
+ Format('%s.%s: %s object "%s" not found.',
+ [TDSSObject(obj).FullName, PropertyName[Index], cls.Name, PropParser.StrValue]
+ ), 403);
+ Exit;
+ end;
+ SetLength(objs, Length(objs) + 1);
+ objs[High(objs)] := otherObj;
+ PropParser.NextParam;
+ end;
+ end
+ else
+ begin
+ while Length(PropParser.StrValue) > 0 do
+ begin
+ ElemName := ConstructElemName(DSS, AnsiLowerCase(PropParser.StrValue));
+ intVal := GetCktElementIndex(DSS, ElemName);
+ otherObj := NIL;
+ if intVal > 0 then
+ otherObj := DSS.ActiveCircuit.CktElements.Get(intVal);
+
+ if otherObj = NIL then
+ begin
+ DoSimpleMsg(
+ Format('%s.%s: object "%s" not found.',
+ [TDSSObject(obj).FullName, PropertyName[Index], ElemName]
+ ), 403);
+ Exit;
+ end;
+ SetLength(objs, Length(objs) + 1);
+ objs[High(objs)] := otherObj;
+ PropParser.NextParam;
+ end;
+ end;
+ TWriteObjRefsPropertyFunction(Pointer(PropertyWriteFunction[Index]))(obj, PPointer(@objs[0]), Length(objs));
+ Result := True;
+ Exit;
+ end;
+
+ // Number of items
+ intVal := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+
+ if intVal < 1 then
+ begin
+ DoSimpleMsg(
+ Format('%s.%s: No objects are expected! Check if the order of property assignments is correct.',
+ [TDSSObject(obj).FullName, PropertyName[Index]]
+ ), 402);
+ Exit;
+ end;
+
+ // Current position
+ positionPtr := NIL;
+ if (PropertyStructArrayIndexOffset2 <> 0) or (PropertyStructArrayIndexOffset <> 0) then
+ begin
+ if TPropertyFlag.AltIndex in flags then
+ positionPtr := PInteger(PByte(obj) + PropertyStructArrayIndexOffset2)
+ else
+ positionPtr := PInteger(PByte(obj) + PropertyStructArrayIndexOffset);
+ end;
+
+ // Start of array
+ otherObjPtr := TDSSObjectPtrPtr((PtrUint(obj) + PtrUint(PropertyOffset[Index])))^;
+
+ // TODO: if cls = NIL,..
+ for i := 1 to intVal do
+ begin
+ PropParser.NextParam;
+ if Length(PropParser.StrValue) = 0 then
+ break;
+
+ otherObj := cls.Find(PropParser.StrValue, False);
+ if otherObj = NIL then
+ begin
+ DoSimpleMsg(
+ Format('%s.%s: %s object "%s" not found.',
+ [TDSSObject(obj).FullName, PropertyName[Index], cls.Name, PropParser.StrValue]
+ ), 403);
+ //TODO: stop?
+ end
+ else
+ otherObjPtr^ := otherObj;
+
+ Inc(otherObjPtr);
+ end;
+
+ if positionPtr <> NIL then
+ positionPtr^ := i;
+
+ Result := True;
+ end;
+ end;
end;
-function TDSSClassHelper.GetAuxParser: TParser; inline;
+function FloatToStrEx(v: Double): String; inline;
begin
- Result := DSS.AuxParser;
+ if IsNaN(v) then
+ Result := '----'
+ else
+ Result := FloatToStr(v);
end;
+function TDSSClassHelper.GetObjPropertyValue(obj: Pointer; Index: Integer; out PropStr: String): Boolean;
+var
+ c: PComplex;
+ otherObj: TDSSObject;
+ otherObjPtr, otherObjPtr0: TDSSObjectPtr;
+ scale: Double;
+ stringList: TStringList;
+ i, j, intVal, Norder, count: Integer;
+ doublePtr: PDouble;
+ integerPtr: PInteger;
+ darray: PDoubleArray;
+ mat: TCMatrix;
+ ValueCount: Array[0..1] of Integer;
+begin
+ if (Index > 0) and (Index <= NumProperties) and
+ (PropertyOffset[Index] <> -1) then
+ begin
+ case PropertyType[Index] of
+ TPropertyType.DoubleProperty,
+ TPropertyType.DoubleOnArrayProperty,
+ TPropertyType.DoubleOnStructArrayProperty:
+ PropStr := FloatToStrEx(GetObjDouble(obj, Index));
+
+ TPropertyType.BusProperty,
+ TPropertyType.BusOnStructArrayProperty,
+ TPropertyType.StringSilentROFunctionProperty,
+ TPropertyType.StringOnArrayProperty,
+ TPropertyType.StringEnumActionProperty,
+ TPropertyType.StringOnStructArrayProperty,
+ TPropertyType.MappedStringEnumOnStructArrayProperty,
+ TPropertyType.MappedStringEnumProperty,
+ TPropertyType.StringProperty,
+ TPropertyType.MakeLikeProperty:
+ PropStr := GetObjString(obj, Index);
+
+ TPropertyType.IntegerOnStructArrayProperty,
+ TPropertyType.MappedIntEnumProperty,
+ TPropertyType.IntegerProperty:
+ PropStr := IntToStr(GetObjInteger(obj, Index));
+
+ TPropertyType.BooleanActionProperty,
+ TPropertyType.EnabledProperty,
+ TPropertyType.BooleanProperty:
+ PropStr := StrYOrN(Boolean(GetObjInteger(obj, Index) <> 0));
+
+
+ TPropertyType.ComplexProperty:
+ begin
+ c := PComplex(PByte(obj) + PropertyOffset[Index]);
+ PropStr := Format('[%g, %g]', [c.re, c.im]);
+ end;
+
+ TPropertyType.ComplexPartsProperty:
+ PropStr := Format('[%g, %g]', [
+ PDouble(PByte(obj) + PropertyOffset[Index])^,
+ PDouble(PByte(obj) + PropertyOffset2[Index])^
+ ]);
+
+ TPropertyType.DoubleArrayProperty,
+ TPropertyType.DoubleDArrayProperty,
+ TPropertyType.DoubleVArrayProperty:
+ begin
+ if TPropertyFlag.SizeIsFunction in PropertyFlags[Index] then
+ begin
+ Norder := TIntegerPropertyFunction(Pointer(PropertyOffset3[Index]))(obj);
+ end
+ else
+ Norder := PInteger(PByte(obj) + PropertyOffset2[Index])^;
+
+ if (TPropertyFlag.AllowNone in PropertyFlags[Index]) and (Norder = 0) then
+ begin
+ PropStr := '[NONE]';
+ Exit;
+ end;
+
+ if TPropertyFlag.ReadByFunction in PropertyFlags[Index] then
+ begin
+ ValueCount[0] := 0;
+ ValueCount[1] := 0;
+ doublePtr := NIL;
+ TDoublesPropertyFunction(Pointer(PropertyReadFunction[Index]))(obj, doublePtr, @ValueCount[0]);
+ PropStr := GetDSSArray_Real(
+ ValueCount[0],
+ pDoubleArray(doublePtr),
+ PropertyScale[Index]
+ );
+ DSS_Dispose_PDouble(doublePtr);
+ Exit;
+ end;
+
+ PropStr := GetDSSArray_Real(
+ Norder,
+ pDoubleArray(PPDouble(PByte(obj) + PropertyOffset[Index])^),
+ PropertyScale[Index]
+ );
+ end;
+ TPropertyType.DoubleFArrayProperty:
+ PropStr := GetDSSArray_Real(
+ PropertyOffset2[Index],
+ pDoubleArray(PDouble(PByte(obj) + PropertyOffset[Index])),
+ PropertyScale[Index]
+ );
+ TPropertyType.DoubleSymMatrixProperty:
+ begin
+ scale := PropertyScale[Index];
+ Norder := PInteger(PByte(obj) + PropertyOffset2[Index])^;
+ darray := PDoubleArray(PByte(obj) + PropertyOffset[Index]);
+ PropStr := '(';
+ if (darray <> NIL) and (Norder > 0) then
+ for i := 1 to Norder do
+ begin
+ for j := 1 to i do
+ begin
+ PropStr := PropStr + Format('%g', [darray[(i - 1) * Norder + j] / scale]) + ' ';
+ end;
+ if i < Norder then
+ PropStr := PropStr + '|';
+ end;
+ PropStr := PropStr + ')';
+ end;
+ TPropertyType.ComplexPartSymMatrixProperty:
+ begin
+ if TPropertyFlag.ScaledByFunction in PropertyFlags[Index] then
+ scale := TPropertyScaleFunction(Pointer(PropertyOffset2[Index]))(obj, True) // True = Getter scale
+ else
+ scale := PropertyScale[Index];
+
+ mat := PCMatrix(Pointer(PByte(obj) + PropertyOffset[Index]))^;
+ if mat = NIL then
+ begin
+ PropStr := '';
+ Result := True;
+ Exit;
+ end;
+ PropStr := '[';
+ if TPropertyFlag.ImagPart in PropertyFlags[Index] then
+ for i := 1 to mat.order do
+ begin
+ for j := 1 to i do
+ PropStr := PropStr + Format('%g ', [mat.GetElement(i, j).im / scale]);
+ if i < mat.order then
+ PropStr := PropStr + '|';
+ end
+ else
+ for i := 1 to mat.order do
+ begin
+ for j := 1 to i do
+ PropStr := PropStr + Format('%g ', [mat.GetElement(i, j).re / scale]);
+ if i < mat.order then
+ PropStr := PropStr + '|';
+ end;
+ PropStr := PropStr + ']';
+ end;
+ TPropertyType.DoubleArrayOnStructArrayProperty:
+ begin
+ // Number of items
+ intVal := PInteger(PByte(obj) + PropertyOffset2[Index])^;
+
+ if intVal <= 0 then
+ begin
+ PropStr := '[]';
+ Result := True;
+ Exit;
+ end;
+
+ // Pointer to the first of the target fields
+ doublePtr := PDouble(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ +
+ PropertyOffset[Index]
+ );
-function TDSSClassHelper.GetDSSObject: TDSSObject; inline;
+ scale := PropertyScale[Index];
+
+ propStr := '[';
+ for i := 1 to intVal do
+ begin
+ PropStr := PropStr + Format('%.7g, ', [doublePtr^ / scale]);
+ doublePtr := PDouble(ptruint(doublePtr) + PropertyStructArrayStep);
+ end;
+ propStr := propStr + ']';
+ end;
+
+ TPropertyType.BusesOnStructArrayProperty:
+ begin
+ // Number of items
+ intVal := PInteger(PByte(obj) + PropertyOffset[Index])^;
+
+ if intVal <= 0 then
+ begin
+ PropStr := '[]';
+ Result := True;
+ Exit;
+ end;
+
+ propStr := '[';
+ for i := 1 to intVal do
+ PropStr := PropStr + Format('%s, ', [TDSSCktElement(obj).GetBus(i)]);
+
+ propStr := propStr + ']';
+ end;
+ TPropertyType.MappedStringEnumArrayProperty:
+ begin
+ if (TPropertyFlag.SizeIsFunction in PropertyFlags[Index]) then
+ intVal := TIntegerPropertyFunction(Pointer(PropertyOffset3[Index]))(obj)
+ else
+ intVal := PropertyOffset3[Index];
+
+ if intVal <= 0 then
+ begin
+ PropStr := '[]';
+ Result := True;
+ Exit;
+ end;
+
+ integerPtr := PPInteger(PByte(obj) + PropertyOffset[Index])^;
+
+ propStr := '[';
+ for i := 1 to intVal do
+ begin
+ PropStr := PropStr + TDSSEnum(Pointer(PropertyOffset2[Index])).OrdinalToString(integerPtr^) + ', ';
+ Inc(integerPtr);
+ end;
+ propStr := propStr + ']';
+ Result := True;
+ end;
+ TPropertyType.MappedStringEnumArrayOnStructArrayProperty:
+ begin
+ // Number of items
+ intVal := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+
+ if intVal <= 0 then
+ begin
+ PropStr := '[]';
+ Result := True;
+ Exit;
+ end;
+
+ // Pointer to the first of the target fields
+ integerPtr := PInteger(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ +
+ PropertyOffset[Index]
+ );
+
+ propStr := '[';
+
+ for i := 1 to intVal do
+ begin
+ PropStr := PropStr + TDSSEnum(Pointer(PropertyOffset2[Index])).OrdinalToString(integerPtr^) + ', ';
+ integerPtr := PInteger(ptruint(integerPtr) + PropertyStructArrayStep);
+ end;
+
+ propStr := propStr + ']';
+ end;
+
+ TPropertyType.IntegerArrayProperty:
+ PropStr := GetDSSArray_Integer(
+ PInteger(PByte(obj) + PropertyOffset2[Index])^,
+ pIntegerArray(PPInteger(PByte(obj) + PropertyOffset[Index])^)
+ );
+
+ TPropertyType.DSSObjectReferenceProperty:
+ begin
+ otherObj := GetObjObject(Obj, Index);
+ if otherObj <> NIL then
+ begin
+ if PropertyOffset2[Index] = 0 then
+ PropStr := otherObj.FullName
+ else
+ PropStr := otherObj.Name
+ end
+ else
+ PropStr := '';
+ end;
+ TPropertyType.StringListProperty:
+ begin
+ if TPropertyFlag.ReadByFunction in PropertyFlags[Index] then
+ stringList := TStringListPropertyFunction(Pointer(PropertyReadFunction[Index]))(obj)
+ else
+ stringList := PStringList(PByte(obj) + PropertyOffset[Index])^;
+
+ PropStr := StringListToString(stringList);
+ if TPropertyFlag.ReadByFunction in PropertyFlags[Index] then
+ stringList.Free();
+ end;
+ TPropertyType.DSSObjectReferenceArrayProperty:
+ begin
+ if TPropertyFlag.ReadByFunction in PropertyFlags[Index] then
+ begin
+ ValueCount[0] := 0;
+ ValueCount[1] := 0;
+ otherObjPtr0 := NIL;
+ TObjRefsPropertyFunction(Pointer(PropertyReadFunction[Index]))(obj, PPointer(otherObjPtr0), @ValueCount[0]);
+ otherObjPtr := otherObjPtr0;
+ count := ValueCount[0];
+ end
+ else
+ begin
+ // Number of items
+ count := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+ // Start of array
+ otherObjPtr := TDSSObjectPtrPtr((PtrUint(obj) + PtrUint(PropertyOffset[Index])))^;
+ end;
+
+ if count < 1 then
+ begin
+ PropStr := '[]';
+ Exit;
+ end;
+
+ PropStr := '[';
+ if Pointer(PropertyOffset2[Index]) = NIL then
+ begin
+ for i := 1 to count do
+ begin
+ PropStr := PropStr + CheckForBlanks(otherObjPtr^.FullName);
+ if i <> count then
+ PropStr := PropStr + ', ';
+ Inc(otherObjPtr);
+ end;
+ end
+ else
+ begin
+ for i := 1 to count do
+ begin
+ PropStr := PropStr + CheckForBlanks(otherObjPtr^.Name);
+ if i <> count then
+ PropStr := PropStr + ', ';
+ Inc(otherObjPtr);
+ end;
+ end;
+ PropStr := PropStr + ']';
+
+ if TPropertyFlag.ReadByFunction in PropertyFlags[Index] then
+ DSS_Dispose_PPointer(PPointer(otherObjPtr0))
+ end;
+ end;
+ Result := True;
+ end;
+ Result := False;
+end;
+
+procedure TDSSClassHelper.AddProperties_Double(props: Array of Integer; ptrs: Array of PDouble);
+var
+ i: Integer;
+begin
+ if Length(props) <> Length(ptrs) then
+ raise Exception.Create('Number of properties must match number of pointers');
+
+ for i := 0 to High(Props) do
+ begin
+ PropertyType[props[i]] := TPropertyType.DoubleProperty;
+ PropertyOffset[props[i]] := ptrint(ptrs[i]);
+ end;
+end;
+
+procedure TDSSClassHelper.AddProperties_Object(props: Array of Integer; ptrs: Array of TDSSObjectPtr; clss: Array of TDSSClass);
+var
+ i: Integer;
+begin
+ if (Length(props) <> Length(ptrs)) or (Length(props) <> Length(clss)) then
+ raise Exception.Create('Number of properties must match number of pointers');
+
+ for i := 0 to High(Props) do
+ begin
+ PropertyType[props[i]] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[props[i]] := ptrint(ptrs[i]);
+ PropertyOffset2[props[i]] := ptrint(clss[i]);
+ end;
+end;
+
+procedure TDSSClassHelper.SetObjObjects(ptr: Pointer;Index: Integer; Value: TDSSObjectPtr; ValueCount: Integer);
+// Note: there is some duplication between this and ParseObjPropertyValue
+var
+ otherObjPtr: TDSSObjectPtr;
+ i, maxCount: Integer;
+ positionPtr: PInteger;
+ flags: TPropertyFlags;
+ Obj: TDSSObject;
+begin
+ Obj := TDSSObject(ptr);
+ flags := PropertyFlags[Index];
+ if TPropertyFlag.WriteByFunction in flags then
+ begin
+ TWriteObjRefsPropertyFunction(PropertyWriteFunction[Index])(obj, PPointer(Value), ValueCount);
+ Exit;
+ end;
+
+ // Number of items
+ maxCount := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+
+ if maxCount < 1 then
+ begin
+ DoSimpleMsg(
+ Format('%s.%s: No objects are expected! Check if the order of property assignments is correct.',
+ [TDSSObject(obj).FullName, PropertyName[Index]]
+ ), 402);
+ Exit;
+ end;
+
+ // Current position
+ positionPtr := NIL;
+ if (PropertyStructArrayIndexOffset2 <> 0) or (PropertyStructArrayIndexOffset <> 0) then
+ begin
+ if TPropertyFlag.AltIndex in flags then
+ positionPtr := PInteger(PByte(obj) + PropertyStructArrayIndexOffset2)
+ else
+ positionPtr := PInteger(PByte(obj) + PropertyStructArrayIndexOffset);
+ end;
+
+ // Start of array
+ otherObjPtr := TDSSObjectPtrPtr((PtrUint(obj) + PtrUint(PropertyOffset[Index])))^;
+
+ //TODO: disallow incomplete arrays?
+ maxCount := Min(ValueCount, maxCount);
+
+ for i := 1 to maxCount do
+ begin
+ //TODO: add type validation
+ otherObjPtr^ := Value^;
+ Inc(otherObjPtr);
+ Inc(Value);
+ end;
+
+ if positionPtr <> NIL then
+ positionPtr^ := maxCount;
+end;
+
+procedure TDSSClassHelper.SetObjObject(ptr: Pointer; Index: Integer; Value: TDSSObject);
+var
+ otherObjPtr: TDSSObjectPtr;
+ flags: TPropertyFlags;
+ posPtr: PInteger;
+ Obj: TDSSObject;
+begin
+ Obj := TDSSObject(ptr);
+ flags := PropertyFlags[Index];
+
+ //TODO: add type validation -- e.g. PD element for EnergyMeter
+ if (TPropertyFlag.WriteByFunction in flags) then
+ TWriteObjRefPropertyFunction(Pointer(PropertyWriteFunction[Index]))(obj, Value)
+ else if not (TPropertyFlag.OnArray in flags) then
+ begin
+ otherObjPtr := TDSSObjectPtr((PtrUint(obj) + PtrUint(PropertyOffset[Index])));
+ otherObjPtr^ := Value;
+ end
+ else // if TPropertyFlag.OnArray in flags then
+ begin
+ otherObjPtr := TDSSObjectPtr(PPByte(PtrUint(obj) + PropertyOffset[Index])^); // start of array
+ posPtr := PInteger(PtrUint(obj) + PropertyStructArrayIndexOffset);
+ inc(otherObjPtr, posPtr^ - 1);
+ otherObjPtr^ := Value;
+ end;
+end;
+
+procedure TDSSClassHelper.SetObjString(ptr: Pointer; Index: Integer; Value: String);
+var
+ stringPtr: PString;
+ otherObj: TDSSObject;
+ prevInt: Integer;
+ flags: TPropertyFlags;
+ Obj: TDSSObject;
begin
- Result := DSS.ActiveDSSObject;
+ Obj := TDSSObject(ptr);
+ flags := PropertyFlags[Index];
+ //TODO: if IsFilename, validate path here
+ if TPropertyFlag.Transform_LowerCase in flags then
+ Value := AnsiLowerCase(Value);
+
+ case PropertyType[Index] of
+ TPropertyType.MakeLikeProperty:
+ begin
+ otherObj := obj.ParentClass.Find(Value);
+ if otherObj = NIL then
+ begin
+ DoSimpleMsg('Error in %s MakeLike: "%s" not found.', [obj.ParentClass.Name, Value], 383);
+ Exit;
+ end;
+ obj.MakeLike(otherObj);
+ end;
+ TPropertyType.StringProperty:
+ begin
+ //TODO: if IsFilename, validate path here
+ stringPtr := PString(PByte(obj) + PropertyOffset[Index]);
+ stringPtr^ := Value;
+ end;
+ TPropertyType.StringOnArrayProperty:
+ begin
+ stringPtr := PPString(PByte(obj) + PropertyOffset[Index])^;
+ stringPtr[PInteger(PByte(obj) + PropertyOffset2[Index])^ - 1] := Value;
+ end;
+ TPropertyType.StringOnStructArrayProperty:
+ begin
+ stringPtr := PString(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer array
+ PropertyOffset[Index] + ptruint(// base field
+ PropertyStructArrayStep * // step size
+ (PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^ - 1) // index
+ )
+ );
+ stringPtr^ := Value;
+ end;
+ TPropertyType.BusProperty:
+ TDSSCktElement(obj).SetBus(PropertyOffset[Index], Value);
+ TPropertyType.BusOnStructArrayProperty:
+ TDSSCktElement(obj).SetBus(PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^, Value);
+ TPropertyType.MappedStringEnumProperty:
+ begin
+ if (TPropertyFlag.ConditionalReadOnly in flags) and (PLongBool(PByte(obj) + PropertyOffset3[Index])^) then
+ Exit;
+ SetObjInteger(Obj, Index, TDSSEnum(Pointer(PropertyOffset2[Index])).StringToOrdinal(AnsiLowerCase(Value)), @prevInt);
+ end;
+ end;
+end;
+
+procedure TDSSClassHelper.SetObjDouble(ptr: Pointer; Index: Integer; Value: Double);
+var
+ flags: TPropertyFlags;
+ scale: Double;
+ doublePtr: PDouble;
+ Obj: TDSSObject;
+begin
+ Obj := TDSSObject(ptr);
+ flags := PropertyFlags[Index];
+ scale := PropertyScale[Index];
+ if (flags = []) and (PropertyType[Index] = TPropertyType.DoubleProperty) then
+ begin
+ // Most properties don't have any flags set, just skip the checks
+ doublePtr := PDouble(PByte(obj) + PropertyOffset[Index]);
+ doublePtr^ := Value * scale;
+ Exit;
+ end;
+
+ if TPropertyFlag.SilentReadOnly in flags then
+ Exit; // Just in case a user calls this by error, no need to do anything
+
+ if TPropertyFlag.ScaledByFunction in flags then
+ scale := TPropertyScaleFunction(Pointer(PropertyOffset2[Index]))(obj, False); // False = Setter scale
+
+ if flags <> [] then
+ begin
+ if (TPropertyFlag.GreaterThanOne in flags) and (Value <= 1) then
+ begin
+ if not (TPropertyFlag.IgnoreInvalid in flags) then
+ DoSimpleMsg(
+ '%s.%s: Value (%g) must be greater than one.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value],
+ 2020031);
+ Exit;
+ end;
+ if (TPropertyFlag.NonZero in flags) and (Value = 0) then
+ begin
+ if not (TPropertyFlag.IgnoreInvalid in flags) then
+ DoSimpleMsg(
+ '%s.%s: Value (%g) cannot be zero.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value],
+ 2020031);
+
+ Exit;
+ end;
+ if (TPropertyFlag.NonNegative in flags) and (Value < 0) then
+ begin
+ if not (TPropertyFlag.IgnoreInvalid in flags) then
+ DoSimpleMsg(
+ '%s.%s: Value (%g) cannot be negative.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value],
+ 2020032);
+
+ Exit;
+ end;
+ if (TPropertyFlag.NonPositive in flags) and (Value > 0) then
+ begin
+ if not (TPropertyFlag.IgnoreInvalid in flags) then
+ DoSimpleMsg(
+ '%s.%s: Value (%g) cannot be positive.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value],
+ 2020033);
+
+ Exit;
+ end;
+ end;
+
+ Value := Value * scale;
+
+ if (Value = 0) and (PropertyTrapZero[Index] <> 0) then
+ begin
+ // DoSimpleMsg('Zero Reactance specified for ......' + DSS.ActiveAutoTransObj.Name, 1011201);
+ Value := PropertyTrapZero[Index];
+ end;
+
+ if (Value <> 0) and PropertyInverse[Index] then
+ Value := 1.0 / Value;
+
+ if (TPropertyFlag.WriteByFunction in flags) then
+ TWriteDoublePropertyFunction(Pointer(PropertyWriteFunction[Index]))(obj, Value)
+ else
+ case PropertyType[Index] of
+ TPropertyType.DoubleProperty:
+ begin
+ doublePtr := PDouble(PByte(obj) + PropertyOffset[Index]);
+ doublePtr^ := Value;
+ end;
+ TPropertyType.DoubleOnArrayProperty:
+ begin
+ doublePtr := PPDouble(PByte(obj) + PropertyOffset[Index])^;
+ doublePtr[PInteger(PByte(obj) + PropertyOffset2[Index])^ - 1] := Value;
+ end;
+ TPropertyType.DoubleOnStructArrayProperty:
+ begin
+ doublePtr := PDouble(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer array
+ PropertyOffset[Index] + ptruint(// base field
+ PropertyStructArrayStep * // step size
+ (PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^ - 1) // index
+ )
+ );
+ doublePtr^ := Value;
+ end;
+ end;
+end;
+
+procedure TDSSClassHelper.SetObjInteger(ptr: Pointer; Index: Integer; Value: Integer; prevInt: PInteger);
+var
+ flags: TPropertyFlags;
+ integerPtr: PInteger = NIL;
+ boolPtr: PLongBool;
+ ptype: TPropertyType;
+ Obj: TDSSObject;
+begin
+ Obj := TDSSObject(ptr);
+ flags := PropertyFlags[Index];
+ ptype := PropertyType[Index];
+
+ if (TPropertyFlag.ConditionalReadOnly in flags) and (PLongBool(PByte(obj) + PropertyOffset3[Index])^) then
+ Exit;
+
+ if flags = [] then
+ begin
+ // Most properties don't have any flags set, just skip the checks
+ if ptype = TPropertyType.BooleanProperty then
+ begin
+ boolPtr := PLongBool(PByte(obj) + PropertyOffset[Index]);
+ if prevInt <> NIL then
+ prevInt^ := Integer(boolPtr^);
+ boolPtr^ := Value <> 0;
+ Exit;
+ end
+ else
+ if ptype in [TPropertyType.IntegerProperty, TPropertyType.MappedIntEnumProperty, TPropertyType.MappedStringEnumProperty] then
+ begin
+ integerPtr := PInteger(PByte(obj) + PropertyOffset[Index]);
+ if prevInt <> NIL then
+ prevInt^ := integerPtr^;
+ integerPtr^ := Value;
+ Exit;
+ end;
+ end;
+ if (TPropertyFlag.IntegerStructIndex in flags) and ((Value < 1) or (Value > PInteger(PByte(obj) + PropertyStructArrayCountOffset)^)) then
+ begin
+ if not (TPropertyFlag.IgnoreInvalid in flags) then
+ DoSimpleMsg(
+ '%s.%s: Invalid value (%d).',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value],
+ 2020031);
+
+ Exit;
+ end;
+ if (TPropertyFlag.GreaterThanOne in flags) and (Value <= 1) then
+ begin
+ if not (TPropertyFlag.IgnoreInvalid in flags) then
+ DoSimpleMsg(
+ '%s.%s: Value (%d) must be greater than one.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value],
+ 2020031);
+
+ Exit;
+ end;
+ if (TPropertyFlag.NonZero in flags) and (Value = 0) then
+ begin
+ if not (TPropertyFlag.IgnoreInvalid in flags) then
+ DoSimpleMsg(
+ '%s.%s: Value (%d) cannot be zero.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value],
+ 2020031);
+
+ Exit;
+ end;
+ if (TPropertyFlag.NonNegative in flags) and (Value < 0) then
+ begin
+ if not (TPropertyFlag.IgnoreInvalid in flags) then
+ DoSimpleMsg(
+ '%s.%s: Value (%d) cannot be negative.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value],
+ 2020032);
+
+ Exit;
+ end;
+ if (TPropertyFlag.NonPositive in flags) and (Value > 0) then
+ begin
+ if not (TPropertyFlag.IgnoreInvalid in flags) then
+ DoSimpleMsg(
+ '%s.%s: Value (%d) cannot be positive.',
+ [TDSSObject(obj).FullName, PropertyName[Index], Value],
+ 2020033);
+
+ Exit;
+ end;
+ if TPropertyFlag.ValueOffset in flags then
+ Value := Value + Round(PropertyValueOffset[Index]);
+
+ case ptype of
+ TPropertyType.EnabledProperty:
+ begin
+ TDSSCktElement(obj).Enabled := Value <> 0;
+ Exit
+ end;
+ TPropertyType.BooleanActionProperty:
+ begin
+ if Value <> 0 then
+ TActionProcedure(PropertyOffset[Index])(obj);
+ Exit;
+ end;
+ TPropertyType.IntegerOnStructArrayProperty:
+ begin
+ integerPtr := PInteger(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer array
+ PropertyOffset[Index] + ptruint(// base field
+ PropertyStructArrayStep * // step size
+ (PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^ - 1) // index
+ )
+ );
+ end;
+ TPropertyType.MappedStringEnumOnStructArrayProperty:
+ begin
+ integerPtr := PInteger(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer array
+ PropertyOffset[Index] + ptruint(// base field
+ PropertyStructArrayStep * // step size
+ (PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^ - 1) // index
+ )
+ );
+ end;
+ TPropertyType.MappedIntEnumProperty:
+ begin
+ integerPtr := PInteger(PByte(obj) + PropertyOffset[Index]);
+ end;
+ TPropertyType.MappedStringEnumProperty:
+ begin
+ if TPropertyFlag.OnArray in flags then
+ begin
+ integerPtr := PPInteger(PByte(obj) + PropertyOffset[Index])^;
+ Inc(integerPtr, PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^ - 1);
+ end
+ else
+ integerPtr := PInteger(PByte(obj) + PropertyOffset[Index]);
+ end;
+ TPropertyType.StringEnumActionProperty:
+ begin
+ TEnumActionProcedure(PropertyOffset[Index])(obj, Value);
+ Exit;
+ end;
+ TPropertyType.BooleanProperty:
+ begin
+ boolPtr := PLongBool(PByte(obj) + PropertyOffset[Index]);
+ if prevInt <> NIL then
+ prevInt^ := Integer(boolPtr^);
+ boolPtr^ := Value <> 0;
+ Exit;
+ end;
+ TPropertyType.IntegerProperty:
+ if not (TPropertyFlag.WriteByFunction in flags) then
+ begin
+ integerPtr := PInteger(PByte(obj) + PropertyOffset[Index]);
+ if prevInt <> NIL then
+ prevInt^ := integerPtr^;
+ integerPtr^ := Value;
+ Exit;
+ end;
+ else
+ //TODO: error?
+ Exit;
+ end;
+
+ if (prevInt <> NIL) and (integerPtr <> NIL) then
+ prevInt^ := integerPtr^;
+
+ if (TPropertyFlag.WriteByFunction in flags) then
+ TWriteIntegerPropertyFunction(Pointer(PropertyWriteFunction[Index]))(obj, Value)
+ else
+ integerPtr^ := Value;
end;
function TDSSObjectHelper.GetCircuit: TDSSCircuit;
@@ -69,25 +1636,1451 @@ function TDSSObjectHelper.GetCircuit: TDSSCircuit;
Result := DSS.ActiveCircuit;
end;
-function TDSSObjectHelper.GetAuxParser: TParser; inline;
+function TDSSObjectHelper.AdjustInputFilePath(const Value: String): String;
+begin
+ Result := Utilities.AdjustInputFilePath(DSS, Value)
+end;
+
+function TDSSObjectHelper.ParsePropertyValue(Index: Integer; Value: String): Boolean;
+var
+ prevInt: Integer;
+ singleEdit: Boolean;
+begin
+ singleEdit := not (Flg.EditionActive in Flags);
+ if singleEdit then
+ BeginEdit(True);
+ Result := True; // TODO
+ ParentClass.ParseObjPropertyValue(self, Index, Value, prevInt);
+ PropertySideEffects(Index, prevInt);
+ if singleEdit then
+ EndEdit(1);
+end;
+
+function TDSSObjectHelper.SetDouble(Index: Integer; Value: Double): Boolean;
+var
+ singleEdit: Boolean;
+begin
+ singleEdit := not (Flg.EditionActive in Flags);
+ if singleEdit then
+ BeginEdit(True);
+ Result := True; // TODO
+ ParentClass.SetObjDouble(self, Index, Value);
+ PropertySideEffects(Index);
+ if singleEdit then
+ EndEdit(1);
+end;
+
+function TDSSObjectHelper.SetString(Index: Integer; Value: String): Boolean;
+var
+ singleEdit: Boolean;
+begin
+ singleEdit := not (Flg.EditionActive in Flags);
+ if singleEdit then
+ BeginEdit(True);
+ Result := True; // TODO
+ ParentClass.SetObjString(self, Index, Value);
+ PropertySideEffects(Index);
+ if singleEdit then
+ EndEdit(1);
+end;
+
+function TDSSObjectHelper.SetInteger(Index: Integer; Value: Integer): Boolean;
+var
+ prevInt: Integer;
+ singleEdit: Boolean;
+begin
+ singleEdit := not (Flg.EditionActive in Flags);
+ if singleEdit then
+ BeginEdit(True);
+ Result := True; // TODO
+ ParentClass.SetObjInteger(self, Index, Value, @prevInt);
+ PropertySideEffects(Index, prevInt);
+ if singleEdit then
+ EndEdit(1);
+end;
+
+function TDSSObjectHelper.SetObject(Index: Integer; Value: TDSSObject): Boolean;
+var
+ singleEdit: Boolean;
+begin
+ singleEdit := not (Flg.EditionActive in Flags);
+ if singleEdit then
+ BeginEdit(True);
+ Result := True; // TODO
+ ParentClass.SetObjObject(self, Index, Value);
+ PropertySideEffects(Index);
+ if singleEdit then
+ EndEdit(1);
+end;
+
+function TDSSObjectHelper.SetObjects(Index: Integer; Value: Array of TDSSObject): Boolean;
+begin
+ Result := SetObjects(Index, @Value[0], Length(Value));
+end;
+
+function TDSSObjectHelper.SetObjects(Index: Integer; Value: TDSSObjectPtr; ValueCount: Integer): Boolean;
+var
+ singleEdit: Boolean;
+begin
+ singleEdit := not (Flg.EditionActive in Flags);
+ if singleEdit then
+ BeginEdit(True);
+ Result := True; // TODO
+ ParentClass.SetObjObjects(self, Index, Value, ValueCount);
+ PropertySideEffects(Index);
+ if singleEdit then
+ EndEdit(1);
+end;
+
+function TDSSObjectHelper.SetIntegers(Index: Integer; Value: Array of Integer): Boolean;
+begin
+ Result := SetIntegers(Index, @Value[0], Length(Value));
+end;
+
+function TDSSObjectHelper.SetIntegers(Index: Integer; Value: PInteger; ValueCount: Integer): Boolean;
+var
+ singleEdit: Boolean;
+begin
+ singleEdit := not (Flg.EditionActive in Flags);
+ if singleEdit then
+ BeginEdit(True);
+ Result := True;//TODO
+ ParentClass.SetObjIntegers(self, Index, Value, ValueCount);
+ PropertySideEffects(Index);
+ if singleEdit then
+ EndEdit(1);
+end;
+
+function TDSSObjectHelper.SetDoubles(Index: Integer; Value: Array of Double): Boolean;
begin
- Result := DSS.AuxParser;
+ Result := SetDoubles(Index, @Value[0], Length(Value));
end;
-function TDSSObjectHelper.GetParser: TParser; inline;
+function TDSSObjectHelper.SetDoubles(Index: Integer; Value: PDouble; ValueCount: Integer): Boolean;
+var
+ singleEdit: Boolean;
begin
- Result := DSS.Parser;
+ singleEdit := not (Flg.EditionActive in Flags);
+ if singleEdit then
+ BeginEdit(True);
+ Result := True;//TODO
+ ParentClass.SetObjDoubles(self, Index, Value, ValueCount);
+ PropertySideEffects(Index);
+ if singleEdit then
+ EndEdit(1);
end;
-function TDSSObjectHelper.InterpretDblArray(const s: String; MaxValues: Integer; ResultArray: pDoubleArray): Integer;inline;
+function TDSSObjectHelper.SetStrings(Index: Integer; Value: Array of String): Boolean;
+var
+ ValuePChar: Array of PChar = NIL;
+ i: Integer;
+var
+ singleEdit: Boolean;
begin
- Result := Utilities.InterpretDblArray(DSS, s, MaxValues, ResultArray)
+ singleEdit := not (Flg.EditionActive in Flags);
+ if singleEdit then
+ BeginEdit(True);
+ Result := True;//TODO
+ Setlength(ValuePChar, Length(Value));
+ for i := 0 to High(Value) do
+ ValuePChar[i] := PChar(Value[i]);
+
+ ParentClass.SetObjStrings(self, Index, @ValuePChar[0], Length(Value));
+ if singleEdit then
+ EndEdit(1);
end;
-function TDSSObjectHelper.InterpretIntArray(const s: String; MaxValues: Integer; ResultArray: pIntegerArray): Integer;inline;
+function TDSSObjectHelper.SetStrings(Index: Integer; Value: PPAnsiChar; ValueCount: Integer): Boolean; overload;
+var
+ singleEdit: Boolean;
begin
- Result := Utilities.InterpretIntArray(DSS, s, MaxValues, ResultArray)
+ singleEdit := not (Flg.EditionActive in Flags);
+ if singleEdit then
+ BeginEdit(True);
+ Result := True;//TODO
+ ParentClass.SetObjStrings(self, Index, Value, ValueCount);
+ PropertySideEffects(Index);
+ if singleEdit then
+ EndEdit(1);
end;
+procedure TDSSObjectHelper.BeginEdit(Activate: Boolean);
+begin
+ ParentClass.BeginEdit(self, Activate);
+end;
+
+procedure TDSSObjectHelper.EndEdit(NumChanges: Integer);
+begin
+ ParentClass.EndEdit(self, NumChanges);
+end;
+
+function TDSSObjectHelper.GetDouble(Index: Integer): Double;
+begin
+ Result := ParentClass.GetObjDouble(self, Index);
+end;
+
+function TDSSObjectHelper.GetInteger(Index: Integer): Integer;
+begin
+ Result := ParentClass.GetObjInteger(self, Index);
+end;
+
+function TDSSObjectHelper.GetString(Index: Integer): String;
+begin
+ Result := ParentClass.GetObjString(self, Index);
+end;
+
+function TDSSObjectHelper.GetObject(Index: Integer): TDSSObject;
+begin
+ Result := ParentClass.GetObjObject(self, Index);
+end;
+
+procedure TDSSObjectHelper.GetDoubles(Index: Integer; var ResultPtr: PDouble; ResultCount: PAPISize);
+begin
+ ParentClass.GetObjDoubles(self, Index, ResultPtr, ResultCount);
+end;
+
+procedure TDSSObjectHelper.GetIntegers(Index: Integer; var ResultPtr: PInteger; ResultCount: PAPISize);
+begin
+ ParentClass.GetObjIntegers(self, Index, ResultPtr, ResultCount);
+end;
+
+procedure TDSSObjectHelper.GetStrings(Index: Integer; var ResultPtr: PPAnsiChar; ResultCount: PAPISize);
+begin
+ ParentClass.GetObjStrings(self, Index, ResultPtr, ResultCount);
+end;
+
+procedure TDSSObjectHelper.GetObjects(Index: Integer; var ResultPtr: PPointer; ResultCount: PAPISize);
+begin
+ ParentClass.GetObjObjects(self, Index, ResultPtr, ResultCount);
+end;
+
+procedure TDSSClassHelper.SetObjIntegers(ptr: Pointer; Index: Integer; Value: PInteger; ValueCount: Integer);
+var
+ i, maxSize, step: Integer;
+ integerPtr, positionPtr, sizePtr: PInteger;
+ dataPtr: PPInteger;
+ flags: TPropertyFlags;
+ Obj: TDSSObject;
+begin
+ Obj := TDSSObject(ptr);
+ flags := PropertyFlags[Index];
+ case PropertyType[Index] of
+ TPropertyType.IntegerArrayProperty:
+ begin
+ sizePtr := PInteger(PByte(obj) + PropertyOffset2[Index]);
+ dataPtr := PPInteger(PByte(obj) + PropertyOffset[Index]);
+ maxSize := sizePtr^;
+ if maxSize <> ValueCount then
+ Exit; //TODO: ERROR
+
+ if dataPtr^ = NIL then
+ begin
+ // If not initialized, allocate here.
+ // Note that this should not be used with dynamic arrays
+ ReAllocmem(dataPtr^, Sizeof(Integer) * maxSize);
+ end;
+ integerPtr := dataPtr^;
+ Move(Value^, integerPtr, SizeOf(Integer) * ValueCount);
+ end;
+ TPropertyType.MappedStringEnumArrayProperty:
+ begin
+ if (TPropertyFlag.SizeIsFunction in flags) then
+ maxSize := TIntegerPropertyFunction(Pointer(PropertyOffset3[Index]))(obj)
+ else
+ maxSize := PropertyOffset3[Index];
+
+ if maxSize <> ValueCount then
+ Exit; //TODO: ERROR
+
+ //TODO: validate -- if not TDSSEnum(Pointer(PropertyOffset2[Index])).OrdinalIsValid(Value^) then Exit;
+
+ integerPtr := PPInteger(PByte(obj) + PropertyOffset[Index])^;
+ // TODO: validate before copying
+ Move(Value^, integerPtr, SizeOf(Integer) * ValueCount);
+ end;
+ TPropertyType.MappedStringEnumArrayOnStructArrayProperty:
+ begin
+ // Number of items
+ maxSize := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+
+ if maxSize <> ValueCount then
+ Exit; //TODO: ERROR
+
+ // Current position
+ positionPtr := PInteger(PByte(obj) + PropertyStructArrayIndexOffset);
+
+ // Pointer to the first of the target fields
+ integerPtr := PInteger(PPByte(PByte(obj) + PropertyStructArrayOffset)^ + PropertyOffset[Index]);
+
+ for i := 1 to maxSize do
+ begin
+ //TODO: validate -- if TDSSEnum(Pointer(PropertyOffset2[Index])).OrdinalIsValid(Value^) then
+ integerPtr^ := Value^;
+ // Move to the next position
+ integerPtr := PInteger(ptruint(integerPtr) + PropertyStructArrayStep);
+ Inc(Value);
+ end;
+
+ positionPtr^ := maxSize; // match the effective behavior of the original code
+ end;
+ TPropertyType.MappedStringEnumProperty, // shortcut
+ TPropertyType.IntegerOnStructArrayProperty, // shortcut
+ TPropertyType.MappedStringEnumOnStructArrayProperty: // shortcut
+ begin
+ if (PropertyType[Index] = TPropertyType.MappedStringEnumProperty) and not (TPropertyFlag.OnArray in PropertyFlags[Index]) then
+ Exit;
+
+ // Number of items
+ maxSize := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+ if maxSize <> ValueCount then
+ Exit; //TODO: ERROR
+
+ if (PropertyType[Index] = TPropertyType.MappedStringEnumProperty) and (TPropertyFlag.OnArray in PropertyFlags[Index]) then
+ begin
+ step := SizeOf(Integer);
+ integerPtr := PInteger(PPByte(PByte(obj) + PropertyOffset[Index])^);
+ end
+ else
+ begin
+ step := PropertyStructArrayStep;
+ integerPtr := PInteger(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ +
+ PropertyOffset[Index]
+ );
+ end;
+
+ for i := 1 to maxSize do
+ begin
+ //TODO: validate -- if TDSSEnum(Pointer(PropertyOffset2[Index])).OrdinalIsValid(Value^) then
+ integerPtr^ := Value^;
+ // Move to the next position
+ integerPtr := PInteger(ptruint(integerPtr) + step);
+ Inc(Value);
+ end;
+ end;
+ end;
+end;
+
+procedure TDSSClassHelper.SetObjDoubles(ptr: Pointer; Index: Integer; Value: PDouble; ValueCount: Integer);
+var
+ i, j, maxSize, Norder, intVal, step: Integer;
+ positionPtr, sizePtr: PInteger;
+ scale: Double;
+ doublePtr: PDouble;
+ dataPtr: PPDouble;
+ complexPtr: PComplex;
+ ptype: TPropertyType;
+ flags: TPropertyFlags;
+ doubleVals: Array of Double = NIL;
+ mat: TCMatrix;
+ Obj: TDSSObject;
+begin
+ Obj := TDSSObject(ptr);
+ ptype := PropertyType[Index];
+ flags := PropertyFlags[Index];
+ case ptype of
+ TPropertyType.ComplexProperty:
+ if ValueCount = 2 then
+ begin
+ complexPtr := PComplex(PByte(obj) + PropertyOffset[Index]);
+ complexPtr^ := PComplex(Value)^;
+ end;
+ TPropertyType.ComplexPartsProperty:
+ if ValueCount = 2 then
+ begin
+ doublePtr := PDouble(PByte(obj) + PropertyOffset[Index]);
+ doublePtr^ := Value^;
+ doublePtr := PDouble(PByte(obj) + PropertyOffset2[Index]);
+ Inc(Value);
+ doublePtr^ := Value^;
+ end;
+ TPropertyType.DoubleSymMatrixProperty:
+ begin
+ scale := PropertyScale[Index];
+ Norder := PInteger(PByte(obj) + PropertyOffset2[Index])^; // e.g. Fnphases
+ dataPtr := PPDouble(PByte(obj) + PropertyOffset[Index]);
+
+ // Allow both the full matrix or the triangle
+ if (ValueCount <> (Norder * Norder)) and (ValueCount <> (Norder * (Norder - 1)) div 2) then
+ Exit; // TODO: error?
+
+ doublePtr := PPDouble(dataPtr)^;
+ if doublePtr = NIL then
+ begin
+ ReAllocMem(dataPtr^, 0);
+ doublePtr := Allocmem(Sizeof(Double) * Norder * Norder);
+ dataPtr^ := doublePtr;
+ end;
+
+ if ValueCount = (Norder * Norder) then
+ Move(Value^, doublePtr^, ValueCount * SizeOf(Double))
+ else
+ begin
+ for i := 0 to Norder - 1 do
+ for j := 0 to Norder - 1 do
+ if j >= i then
+ begin
+ doublePtr[i * Norder + j] := Value^;
+ if i <> j then
+ doublePtr[j * Norder + i] := Value^;
+
+ Inc(Value);
+ end;
+ end;
+ if scale <> 1 then
+ begin
+ for i := 0 to Norder - 1 do
+ for j := 0 to Norder - 1 do
+ doublePtr[i * Norder + j] := doublePtr[i * Norder + j] * scale;
+ end;
+ end;
+ TPropertyType.ComplexPartSymMatrixProperty:
+ begin
+ if TPropertyFlag.ScaledByFunction in flags then
+ scale := TPropertyScaleFunction(Pointer(PropertyOffset2[Index]))(obj, False) // False = Setter scale
+ else
+ scale := PropertyScale[Index];
+
+ mat := PCMatrix(Pointer(PByte(obj) + PropertyOffset[Index]))^;
+ doublePtr := PDouble(mat.GetValuesArrayPtr(Norder));
+ if TPropertyFlag.ImagPart in flags then
+ Inc(doublePtr);
+
+ // Allow both the full matrix or the triangle
+ if (ValueCount <> (Norder * Norder)) and (ValueCount <> (Norder * (Norder - 1)) div 2) then
+ Exit; // TODO: error?
+
+ if ValueCount = (Norder * Norder) then
+ begin
+ // Full matrix, but still need to copy elements one by one.
+ for i := 0 to Norder - 1 do
+ for j := 0 to Norder - 1 do
+ begin
+ doublePtr[(i * Norder + j) * 2] := Value^;
+ Inc(Value);
+ end;
+ end
+ else
+ begin
+ for i := 0 to Norder - 1 do
+ for j := 0 to Norder - 1 do
+ if j >= i then
+ begin
+ doublePtr[i * Norder + j] := Value^;
+ if i <> j then
+ doublePtr[j * Norder + i] := Value^;
+
+ Inc(Value);
+ end;
+ end;
+ if scale <> 1 then
+ begin
+ for i := 0 to Norder - 1 do
+ for j := 0 to Norder - 1 do
+ doublePtr[i * Norder + j] := doublePtr[i * Norder + j] * scale;
+ end;
+ end;
+ TPropertyType.DoubleArrayProperty,
+ TPropertyType.DoubleDArrayProperty,
+ TPropertyType.DoubleFArrayProperty,
+ TPropertyType.DoubleVArrayProperty:
+ begin
+ scale := PropertyScale[Index];
+
+ sizePtr := NIL;
+ if TPropertyFlag.SizeIsFunction in flags then
+ begin
+ intVal := TIntegerPropertyFunction(Pointer(PropertyOffset3[Index]))(obj);
+ sizePtr := @intVal;
+ end
+ else
+ if (ptype <> TPropertyType.DoubleFArrayProperty) then
+ sizePtr := PInteger(PByte(obj) + PropertyOffset2[Index]); // Size pointer
+
+ if (TPropertyFlag.AllowNone in flags) and (ValueCount = 0) then
+ begin
+ if sizePtr <> NIL then
+ sizePtr^ := 0;
+ Exit;
+ end;
+
+ if TPropertyFlag.WriteByFunction in flags then
+ begin
+ doublePtr := Value;
+ end
+ else
+ begin
+ dataPtr := PPDouble(PByte(obj) + PropertyOffset[Index]);
+ doublePtr := dataPtr^;
+ end;
+
+ if ((ptype = TPropertyType.DoubleArrayProperty) or (ptype = TPropertyType.DoubleVArrayProperty))
+ and (doublePtr = NIL) then
+ begin
+ // If not initialized, allocate here.
+ // Note that this should not be used with dynamic arrays
+ ReAllocmem(dataPtr^, Sizeof(Double) * sizePtr^);
+ doublePtr := dataPtr^;
+ end;
+
+ case ptype of
+ TPropertyType.DoubleArrayProperty,
+ TPropertyType.DoubleDArrayProperty,
+ TPropertyType.DoubleVArrayProperty:
+ begin
+ if TPropertyFlag.ArrayMaxSize in flags then
+ maxSize := PropertyOffset3[Index]
+ else
+ maxSize := sizePtr^;
+
+ if maxSize <> ValueCount then
+ Exit; // TODO
+
+ Move(Value^, doublePtr^, maxSize * SizeOf(Double));
+ end;
+ TPropertyType.DoubleFArrayProperty:
+ begin
+ maxSize := PropertyOffset2[Index];
+ if maxSize <> ValueCount then
+ Exit; // TODO
+
+ Move(Value^, doublePtr^, maxSize * SizeOf(Double));
+ end;
+ end;
+
+ if scale <> 1 then
+ begin
+ for i := 1 to sizePtr^ do
+ begin
+ doublePtr^ := doublePtr^ * scale;
+ Inc(doublePtr);
+ end;
+ end;
+
+ if TPropertyFlag.WriteByFunction in flags then
+ begin
+ SetLength(doubleVals, sizePtr^);
+ Move(Value^, doubleVals[0], sizePtr^ * SizeOf(Double));
+ TWriteDoublesPropertyFunction(Pointer(PropertyWriteFunction[Index]))(obj, @doubleVals[0], Length(doubleVals))
+ end
+ end;
+ TPropertyType.DoubleOnStructArrayProperty, // shortcut
+ TPropertyType.DoubleOnArrayProperty: // shortcut
+ begin
+ // Number of items
+ maxSize := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+
+ if maxSize <> ValueCount then
+ Exit; // TODO
+
+ if PropertyType[Index] = TPropertyType.DoubleOnStructArrayProperty then
+ begin
+ step := PropertyStructArrayStep;
+ doublePtr := PDouble(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer struct array
+ PropertyOffset[Index] // base field
+ );
+ end
+ else
+ begin
+ step := SizeOf(Double);
+ doublePtr := PDouble(
+ PPByte(PByte(obj) + PropertyOffset[Index])^ // Pointer to the pointer array
+ );
+ end;
+
+ scale := PropertyScale[Index];
+ if scale <> 1 then
+ begin
+ for i := 1 to maxSize do
+ begin
+ doublePtr^ := Value^ * scale;
+ doublePtr := PDouble(PByte(doublePtr) + step);
+ Inc(Value);
+ end;
+ end
+ else
+ begin
+ for i := 1 to maxSize do
+ begin
+ doublePtr^ := Value^;
+ doublePtr := PDouble(PByte(doublePtr) + step);
+ Inc(Value);
+ end;
+ end;
+ end;
+ TPropertyType.DoubleArrayOnStructArrayProperty:
+ begin
+ // Number of items
+ maxSize := PInteger(PByte(obj) + PropertyOffset2[Index])^;
+
+ if maxSize <> ValueCount then
+ Exit; // TODO
+
+ // Current position
+ positionPtr := PInteger(PByte(obj) + PropertyStructArrayIndexOffset);
+
+ // Pointer to the first of the target fields
+ doublePtr := PDouble(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + PropertyOffset[Index]
+ );
+
+ scale := PropertyScale[Index];
+
+ // Loop for no more than the expected number of items; Ignore omitted values
+ for i := 1 to maxSize do
+ begin
+ doublePtr^ := Value^ * scale;
+ // Move to the next position
+ doublePtr := PDouble(ptruint(doublePtr) + PropertyStructArrayStep);
+ Inc(Value);
+ end;
+ positionPtr^ := maxSize; // match the effective behavior of the original code
+ end;
+ end;
+end;
+
+procedure TDSSClassHelper.SetObjStrings(ptr: Pointer; Index: Integer; Value: PPAnsiChar; ValueCount: Integer);
+var
+ i, maxSize, step: Integer;
+ positionPtr, integerPtr: PInteger;
+ flags: TPropertyFlags;
+ stringListPtr: PStringList;
+ stringList: TStringList;
+ stringPtr: PString;
+ Obj: TDSSObject;
+begin
+ Obj := TDSSObject(ptr);
+ flags := PropertyFlags[Index];
+ case PropertyType[Index] of
+ TPropertyType.StringListProperty:
+ begin
+ if (TPropertyFlag.WriteByFunction in flags) then
+ stringList := TStringList.Create()
+ else
+ begin
+ stringListPtr := PStringList(PByte(obj) + PropertyOffset[Index]);
+ stringList := stringListPtr^;
+ end;
+
+ stringList.Clear();
+ for i := 1 to ValueCount do
+ begin
+ stringList.Add(Value^);
+ Inc(Value);
+ end;
+
+ if (TPropertyFlag.WriteByFunction in flags) then
+ TWriteStringListPropertyFunction(Pointer(PropertyWriteFunction[Index]))(obj, stringList)
+ end;
+ TPropertyType.BusesOnStructArrayProperty,
+ TPropertyType.BusOnStructArrayProperty: // allow this one as a shortcut
+ begin
+ // Number of items
+ maxSize := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+ // Current position
+ positionPtr := PInteger(PByte(obj) + PropertyStructArrayIndexOffset);
+
+ if ValueCount <> maxSize then
+ Exit; //TODO: error
+
+ // Loop for no more than the expected number of items; Ignore omitted values
+ for i := 1 to maxSize do
+ begin
+ if Length(Value^) > 0 then
+ TDSSCktElement(obj).SetBus(i, Value^);
+ Inc(Value);
+ end;
+ positionPtr^ := maxSize; // match the effective behavior of the original code
+ end;
+ TPropertyType.MappedStringEnumArrayProperty:
+ begin
+ if (TPropertyFlag.SizeIsFunction in flags) then
+ maxSize := TIntegerPropertyFunction(Pointer(PropertyOffset3[Index]))(obj)
+ else
+ maxSize := PropertyOffset3[Index];
+
+ integerPtr := PPInteger(PByte(obj) + PropertyOffset[Index])^;
+
+ if ValueCount <> maxSize then
+ Exit; //TODO: error
+
+ for i := 1 to maxSize do
+ begin
+ if Length(Value^) > 0 then
+ integerPtr^ := TDSSEnum(Pointer(PropertyOffset2[Index])).StringToOrdinal(Value^);
+ Inc(Value);
+ Inc(integerPtr);
+ end;
+ end;
+ TPropertyType.StringOnStructArrayProperty, // shortcut
+ TPropertyType.StringOnArrayProperty: // shortcut
+ begin
+ // Number of items
+ maxSize := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+ if maxSize <> ValueCount then
+ Exit; //TODO: error
+
+ if PropertyType[Index] = TPropertyType.StringOnStructArrayProperty then
+ begin
+ step := PropertyStructArrayStep;
+ stringPtr := PString(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer struct array
+ PropertyOffset[Index] // base field
+ );
+ end
+ else
+ begin
+ step := SizeOf(String);
+ stringPtr := PString(
+ PPByte(PByte(obj) + PropertyOffset[Index])^ // Pointer to the pointer array
+ );
+ end;
+
+ for i := 1 to maxSize do
+ begin
+ stringPtr^ := Value^;
+ stringPtr := PString(ptruint(stringPtr) + step);
+ Inc(Value);
+ end;
+ end;
+ TPropertyType.MappedStringEnumProperty, // shortcut
+ TPropertyType.MappedStringEnumOnStructArrayProperty, // shortcut
+ TPropertyType.MappedStringEnumArrayOnStructArrayProperty:
+ begin
+ if (PropertyType[Index] = TPropertyType.MappedStringEnumProperty) and not (TPropertyFlag.OnArray in PropertyFlags[Index]) then
+ Exit;
+
+ // Number of items
+ maxSize := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+ if maxSize <> ValueCount then
+ Exit; //TODO: error
+
+ // Current position
+ positionPtr := PInteger(PByte(obj) + PropertyStructArrayIndexOffset);
+
+ // Pointer to the first of the target fields
+ if (PropertyType[Index] = TPropertyType.MappedStringEnumProperty) and (TPropertyFlag.OnArray in PropertyFlags[Index]) then
+ begin
+ step := SizeOf(Integer);
+ integerPtr := PInteger(PPByte(PByte(obj) + PropertyOffset[Index])^);
+ end
+ else
+ begin
+ step := PropertyStructArrayStep;
+ integerPtr := PInteger(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ +
+ PropertyOffset[Index]
+ );
+ end;
+
+ for i := 1 to maxSize do
+ begin
+ if Length(Value^) > 0 then
+ integerPtr^ := TDSSEnum(Pointer(PropertyOffset2[Index])).StringToOrdinal(Value^);
+
+ // Move to the next position
+ Inc(Value);
+ integerPtr := PInteger(ptruint(integerPtr) + step);
+ end;
+ if PropertyType[Index] = TPropertyType.MappedStringEnumArrayOnStructArrayProperty then
+ positionPtr^ := maxSize; // match the effective behavior of the original code
+ end;
+ end;
+end;
+
+function TDSSClassHelper.GetObjDouble(Obj: Pointer; Index: Integer): Double;
+var
+ scale: Double;
+begin
+ Result := NaN;
+ if not ((Index > 0) and (Index <= NumProperties) and (PropertyOffset[Index] <> -1)) then
+ Exit;
+
+ case PropertyType[Index] of
+ TPropertyType.DoubleProperty:
+ begin
+ if TPropertyFlag.ConditionalValue in PropertyFlags[index] then
+ if not PLongBool(PtrUint(obj) + PropertyOffset3[Index])^ then
+ begin
+ Result := NaN;
+ Exit;
+ end;
+
+ scale := PropertyScale[Index];
+
+ if TPropertyFlag.ReadByFunction in PropertyFlags[index] then
+ Result := TDoublePropertyFunction(Pointer(PropertyReadFunction[Index]))(obj)
+ else
+ Result := PDouble(PByte(obj) + PropertyOffset[Index])^;
+
+ if TPropertyFlag.ScaledByFunction in PropertyFlags[index] then
+ scale := TPropertyScaleFunction(Pointer(PropertyOffset2[Index]))(obj, True); // True = Getter scale
+
+ if PropertyInverse[Index] then
+ Result := 1 / (Result / scale)
+ else
+ Result := Result / scale;
+ end;
+ TPropertyType.DoubleOnArrayProperty:
+ Result := (
+ (PPDouble(PByte(obj) + PropertyOffset[Index])^) // base array
+ [PInteger(PByte(obj) + PropertyOffset2[Index])^ - 1] // element index
+ );
+ TPropertyType.DoubleOnStructArrayProperty:
+ if PropertyScale[Index] <> 1 then
+ Result := (PDouble(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer array
+ PropertyOffset[Index] + ptruint(// base field
+ PropertyStructArrayStep * // step size
+ (PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^ - 1) // index
+ )
+ )^ / PropertyScale[Index])
+ else
+ Result := PDouble(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer array
+ PropertyOffset[Index] + ptruint(// base field
+ PropertyStructArrayStep * // step size
+ (PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^ - 1) // index
+ )
+ )^;
+ end;
+end;
+
+function TDSSClassHelper.GetObjString(Obj: Pointer; Index: Integer): String;
+var
+ integerPtr: PInteger;
+ otherObj: TDSSObject;
+begin
+ Result := '';
+ if not ((Index > 0) and (Index <= NumProperties) and (PropertyOffset[Index] <> -1)) then
+ Exit;
+
+ case PropertyType[Index] of
+ TPropertyType.BusProperty:
+ Result := TDSSCktElement(obj).GetBus(PropertyOffset[Index]);
+ TPropertyType.BusOnStructArrayProperty:
+ Result := TDSSCktElement(obj).GetBus(PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^);
+ TPropertyType.StringSilentROFunctionProperty:
+ Result := TStringPropertyFunction(Pointer(PropertyOffset[Index]))(obj);
+ TPropertyType.StringOnArrayProperty:
+ Result := (
+ (PPString(PByte(obj) + PropertyOffset[Index])^)
+ [PInteger(PByte(obj) + PropertyOffset2[Index])^ - 1]
+ );
+ TPropertyType.MappedStringEnumOnStructArrayProperty:
+ Result := TDSSEnum(Pointer(PropertyOffset2[Index])).OrdinalToString(PInteger(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer array
+ PropertyOffset[Index] + ptruint(// base field
+ PropertyStructArrayStep * // step size
+ (PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^ - 1) // index
+ )
+ )^);
+ TPropertyType.StringOnStructArrayProperty:
+ Result := PString(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer array
+ PropertyOffset[Index] + ptruint(// base field
+ PropertyStructArrayStep * // step size
+ (PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^ - 1) // index
+ )
+ )^;
+ TPropertyType.StringProperty:
+ Result := PString(PByte(obj) + PropertyOffset[Index])^;
+ TPropertyType.MakeLikeProperty:
+ Result := '';//TODO? or should leave empty?
+ TPropertyType.MappedStringEnumProperty:
+ begin
+ if TPropertyFlag.OnArray in PropertyFlags[Index] then
+ begin
+ integerPtr := PPInteger(PByte(obj) + PropertyOffset[Index])^;
+ Inc(integerPtr, PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^ - 1);
+ end
+ else
+ integerPtr := PInteger(PByte(obj) + PropertyOffset[Index]);
+
+ Result := TDSSEnum(Pointer(PropertyOffset2[Index])).OrdinalToString(integerPtr^);
+ end;
+ TPropertyType.StringEnumActionProperty:
+ Result := '';
+
+ TPropertyType.DSSObjectReferenceProperty:
+ begin
+ otherObj := GetObjObject(Obj, Index);
+ if otherObj <> NIL then
+ Result := otherObj.Name
+ else
+ Result := '';
+ end;
+ end;
+end;
+
+function TDSSClassHelper.GetObjInteger(Obj: Pointer; Index: Integer): Integer;
+begin
+ Result := -1;
+ if not ((Index > 0) and (Index <= NumProperties) and (PropertyOffset[Index] <> -1)) then
+ Exit;
+
+ case PropertyType[Index] of
+ TPropertyType.IntegerOnStructArrayProperty:
+ Result := PInteger(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer array
+ PropertyOffset[Index] + ptruint(// base field
+ PropertyStructArrayStep * // step size
+ (PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^ - 1) // index
+ )
+ )^;
+
+ TPropertyType.MappedStringEnumOnStructArrayProperty:
+ Result := PInteger(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer array
+ PropertyOffset[Index] + ptruint(// base field
+ PropertyStructArrayStep * // step size
+ (PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^ - 1) // index
+ )
+ )^;
+
+
+ TPropertyType.MappedIntEnumProperty,
+ TPropertyType.IntegerProperty,
+ TPropertyType.MappedStringEnumProperty:
+ begin
+ if (TPropertyFlag.ReadByFunction in PropertyFlags[Index]) then
+ Result := TIntegerPropertyFunction(Pointer(PropertyReadFunction[Index]))(obj)
+ else
+ Result := PInteger(PByte(obj) + PropertyOffset[Index])^;
+
+ Result := Result - Round(PropertyValueOffset[Index]);
+ end;
+
+ TPropertyType.EnabledProperty:
+ Result := Integer(TDSSCktElement(obj).Enabled);
+
+ TPropertyType.BooleanProperty:
+ Result := Integer(PLongBool(PByte(obj) + PropertyOffset[Index])^);
+
+
+ TPropertyType.BooleanActionProperty:
+ Result := 0;
+ end;
+end;
+
+function TDSSClassHelper.GetObjObject(Obj: Pointer; Index: Integer): TDSSObject;
+var
+ otherObjPtr: TDSSObjectPtr;
+ posPtr: PInteger;
+begin
+ Result := NIL;
+ if not ((Index > 0) and (Index <= NumProperties) and (PropertyOffset[Index] <> -1)) then
+ Exit;
+
+ case PropertyType[Index] of
+ TPropertyType.DSSObjectReferenceProperty:
+ if not (TPropertyFlag.OnArray in PropertyFlags[Index]) then
+ Result := TDSSObjectPtr((PtrUint(obj) + PtrUint(PropertyOffset[Index])))^
+ else // if TPropertyFlag.OnArray in flags then
+ begin
+ otherObjPtr := TDSSObjectPtr(PPByte(PtrUint(obj) + PropertyOffset[Index])^); // start of array
+ posPtr := PInteger(PtrUint(obj) + PropertyStructArrayIndexOffset);
+ inc(otherObjPtr, posPtr^ - 1);
+ Result := otherObjPtr^;
+ end;
+ end;
+end;
+
+procedure TDSSClassHelper.GetObjDoubles(Obj: Pointer; Index: Integer; var ResultPtr: PDouble; ResultCount: PAPISize);
+var
+ c: PComplex;
+ i, j, count, step: Integer;
+ doublePtr, outPtr: PDouble;
+ mat: TCMatrix;
+ scale: Double;
+ Result: PDoubleArray0;
+begin
+ if not ((Index > 0) and (Index <= NumProperties) and (PropertyOffset[Index] <> -1)) then
+ begin
+ Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 0);
+ Exit;
+ end;
+
+ case PropertyType[Index] of
+ TPropertyType.ComplexProperty:
+ begin
+ Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2);
+ c := PComplex(PByte(obj) + PropertyOffset[Index]);
+ Result[0] := c.re;
+ Result[1] := c.im;
+ end;
+ TPropertyType.ComplexPartsProperty:
+ begin
+ Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2);
+ Result[0] := PDouble(PByte(obj) + PropertyOffset[Index])^;
+ Result[1] := PDouble(PByte(obj) + PropertyOffset2[Index])^;
+ end;
+ TPropertyType.DoubleArrayProperty,
+ TPropertyType.DoubleDArrayProperty,
+ TPropertyType.DoubleVArrayProperty,
+ TPropertyType.DoubleFArrayProperty,
+ TPropertyType.DoubleSymMatrixProperty:
+ begin
+ doublePtr := NIL;
+ if PropertyType[Index] = TPropertyType.DoubleFArrayProperty then
+ begin
+ doublePtr := PDouble(PByte(obj) + PropertyOffset[Index]);
+ count := PropertyOffset2[Index]
+ end
+ else if TPropertyFlag.SizeIsFunction in PropertyFlags[Index] then
+ count := TIntegerPropertyFunction(Pointer(PropertyOffset3[Index]))(obj)
+ else
+ count := PInteger(PByte(obj) + PropertyOffset2[Index])^;
+
+ if TPropertyFlag.ReadByFunction in PropertyFlags[Index] then
+ begin
+ TDoublesPropertyFunction(Pointer(PropertyReadFunction[Index]))(obj, ResultPtr, ResultCount);
+ Result := PDoubleArray0(ResultPtr);
+ if PropertyScale[Index] <> 1 then
+ for i := 0 to ResultCount^ do
+ Result[i - 1] := Result[i - 1] / PropertyScale[Index];
+ Exit;
+ end;
+
+ if doublePtr = NIL then
+ doublePtr := PPDouble(PByte(obj) + PropertyOffset[Index])^;
+
+ if PropertyType[Index] = TPropertyType.DoubleSymMatrixProperty then
+ count := count * count;
+
+ if (count <= 0) or (doublePtr = NIL) then
+ Exit;
+
+ Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, count);
+ if PropertyScale[Index] <> 1 then
+ begin
+ for i := 1 to count do
+ begin
+ Result[i - 1] := doublePtr^ / PropertyScale[Index];
+ Inc(doublePtr);
+ end;
+ end
+ else
+ Move(doublePtr^, Result[0], count * SizeOf(Double));
+ end;
+ TPropertyType.ComplexPartSymMatrixProperty:
+ begin
+ if TPropertyFlag.ScaledByFunction in PropertyFlags[Index] then
+ scale := TPropertyScaleFunction(Pointer(PropertyOffset2[Index]))(obj, True) // True = Getter scale
+ else
+ scale := PropertyScale[Index];
+
+ mat := PCMatrix(Pointer(PByte(obj) + PropertyOffset[Index]))^;
+ if mat = NIL then
+ Exit;
+
+ Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, mat.Order * mat.Order);
+ outPtr := @Result[0];
+
+ if TPropertyFlag.ImagPart in PropertyFlags[Index] then
+ for i := 1 to mat.Order do
+ begin
+ for j := 1 to mat.Order do
+ begin
+ outPtr^ := mat.GetElement(i, j).im / scale;
+ Inc(outPtr);
+ end;
+ end
+ else
+ for i := 1 to mat.Order do
+ begin
+ for j := 1 to mat.Order do
+ begin
+ outPtr^ := mat.GetElement(i, j).re / scale;
+ Inc(outPtr);
+ end;
+ end;
+ end;
+ TPropertyType.DoubleOnStructArrayProperty, // shortcut
+ TPropertyType.DoubleOnArrayProperty: // shortcut
+ begin
+ // Number of items
+ count := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+ if count <= 0 then
+ Exit;
+
+ Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, count);
+ scale := PropertyScale[Index];
+ if PropertyType[Index] = TPropertyType.DoubleOnStructArrayProperty then
+ begin
+ step := PropertyStructArrayStep;
+ doublePtr := PDouble(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer struct array
+ PropertyOffset[Index] // base field
+ );
+ end
+ else
+ begin
+ step := SizeOf(Double);
+ doublePtr := PDouble(
+ PPByte(PByte(obj) + PropertyOffset[Index])^ // Pointer to the pointer array
+ );
+ end;
+
+ if scale <> 1 then
+ begin
+ for i := 1 to count do
+ begin
+ Result[i - 1] := doublePtr^ / scale;
+ doublePtr := PDouble(ptruint(doublePtr) + step);
+ end
+ end
+ else
+ begin
+ for i := 1 to count do
+ begin
+ Result[i - 1] := doublePtr^;
+ doublePtr := PDouble(ptruint(doublePtr) + step);
+ end
+ end;
+ end;
+ TPropertyType.DoubleArrayOnStructArrayProperty:
+ begin
+ // Number of items
+ count := PInteger(PByte(obj) + PropertyOffset2[Index])^;
+ if count <= 0 then
+ Exit;
+
+ // Pointer to the first of the target fields
+ doublePtr := PDouble(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ +
+ PropertyOffset[Index]
+ );
+ scale := PropertyScale[Index];
+ Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, count);
+ for i := 1 to count do
+ begin
+ Result[i - 1] := doublePtr^ / scale;
+ doublePtr := PDouble(ptruint(doublePtr) + PropertyStructArrayStep);
+ end;
+ end;
+ end;
+end;
+
+procedure TDSSClassHelper.GetObjIntegers(Obj: Pointer; Index: Integer; var ResultPtr: PInteger; ResultCount: PAPISize);
+var
+ integerPtr: PInteger;
+ i, count, step: Integer;
+ Result: PIntegerArray0;
+begin
+ Result := NIL;
+ if not ((Index > 0) and (Index <= NumProperties) and (PropertyOffset[Index] <> -1)) then
+ begin
+ Result := DSS_RecreateArray_PInteger(ResultPtr, ResultCount, 0);
+ Exit;
+ end;
+
+ case PropertyType[Index] of
+ TPropertyType.IntegerArrayProperty:
+ begin
+ count := PInteger(PByte(obj) + PropertyOffset2[Index])^;
+ Result := DSS_RecreateArray_PInteger(ResultPtr, ResultCount, count);
+ Move((PPInteger(PByte(obj) + PropertyOffset[Index])^)^, Result[0], count * SizeOf(Integer));
+ end;
+ TPropertyType.MappedStringEnumArrayProperty:
+ begin
+ if not (TPropertyFlag.SizeIsFunction in PropertyFlags[Index]) then
+ count := TIntegerPropertyFunction(Pointer(PropertyOffset3[Index]))(obj)
+ else
+ count := PropertyOffset3[Index];
+
+ if count <= 0 then
+ Exit;
+
+ integerPtr := PPInteger(PByte(obj) + PropertyOffset[Index])^;
+ Result := DSS_RecreateArray_PInteger(ResultPtr, ResultCount, count);
+ Move(integerPtr^, Result[0], count * SizeOf(Integer));
+ end;
+ TPropertyType.MappedStringEnumArrayOnStructArrayProperty:
+ begin
+ // Number of items
+ count := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+ if count <= 0 then
+ Exit;
+ // Pointer to the first of the target fields
+ integerPtr := PInteger(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ +
+ PropertyOffset[Index]
+ );
+ Result := DSS_RecreateArray_PInteger(ResultPtr, ResultCount, count);
+ for i := 1 to count do
+ begin
+ Result[i - 1] := integerPtr^;
+ integerPtr := PInteger(ptruint(integerPtr) + PropertyStructArrayStep);
+ end;
+ end;
+ TPropertyType.MappedStringEnumProperty, // shortcut
+ TPropertyType.IntegerOnStructArrayProperty, // shortcut
+ TPropertyType.MappedStringEnumOnStructArrayProperty: // shortcut
+ begin
+ if (PropertyType[Index] = TPropertyType.MappedStringEnumProperty) and not (TPropertyFlag.OnArray in PropertyFlags[Index]) then
+ Exit;
+
+ count := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+ if count <= 0 then
+ Exit;
+
+ if (PropertyType[Index] = TPropertyType.MappedStringEnumProperty) and (TPropertyFlag.OnArray in PropertyFlags[Index]) then
+ begin
+ step := SizeOf(Integer);
+ integerPtr := PInteger(PPByte(PByte(obj) + PropertyOffset[Index])^);
+ end
+ else
+ begin
+ step := PropertyStructArrayStep;
+ integerPtr := PInteger(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ +
+ PropertyOffset[Index]
+ );
+ end;
+
+ Result := DSS_RecreateArray_PInteger(ResultPtr, ResultCount, count);
+ for i := 1 to count do
+ begin
+ Result[i - 1] := integerPtr^;
+ integerPtr := PInteger(ptruint(integerPtr) + step);
+ end;
+ // Inc(integerPtr, PInteger(PByte(obj) + PropertyStructArrayIndexOffset)^ - 1);
+ end;
+ end;
+end;
+
+procedure TDSSClassHelper.GetObjStrings(Obj: Pointer; Index: Integer; var ResultPtr: PPAnsiChar; ResultCount: PAPISize);
+var
+ i, count, step: Integer;
+ stringList: TStringList;
+ ptype: TPropertyType;
+ integerPtr: PInteger;
+ stringPtr: PString;
+ otherObjPtr: TDSSObjectPtr;
+ Result: PPAnsiCharArray0;
+begin
+ if not ((Index > 0) and (Index <= NumProperties) and (PropertyOffset[Index] <> -1)) then
+ begin
+ Result := DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, 0);
+ Exit;
+ end;
+ ptype := PropertyType[Index];
+ case ptype of
+ TPropertyType.BusesOnStructArrayProperty,
+ TPropertyType.BusOnStructArrayProperty: // allow this one as a shortcut
+ begin
+ // Number of items
+ count := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+ if count <= 0 then
+ Exit;
+ Result := DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, count);
+ for i := 1 to count do
+ Result[i - 1] := DSS_CopyStringAsPChar(TDSSCktElement(obj).GetBus(i));
+ end;
+ TPropertyType.StringListProperty:
+ begin
+ if TPropertyFlag.ReadByFunction in PropertyFlags[Index] then
+ stringList := TStringListPropertyFunction(Pointer(PropertyReadFunction[Index]))(obj)
+ else
+ stringList := PStringList(PByte(obj) + PropertyOffset[Index])^;
+
+ if stringList = NIL then
+ Exit;
+
+ count := stringList.Count;
+ Result := DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, count);
+ for i := 1 to count do
+ Result[i - 1] := DSS_CopyStringAsPChar(stringList.Strings[i - 1]);
+
+ if TPropertyFlag.ReadByFunction in PropertyFlags[Index] then
+ stringList.Free();
+ end;
+ TPropertyType.MappedStringEnumProperty, // shortcut
+ TPropertyType.MappedStringEnumOnStructArrayProperty: // shortcut
+ begin
+ if (PropertyType[Index] = TPropertyType.MappedStringEnumProperty) and not (TPropertyFlag.OnArray in PropertyFlags[Index]) then
+ Exit;
+
+ count := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+ if count <= 0 then
+ Exit;
+
+ if (PropertyType[Index] = TPropertyType.MappedStringEnumProperty) and (TPropertyFlag.OnArray in PropertyFlags[Index]) then
+ begin
+ step := SizeOf(Integer);
+ integerPtr := PInteger(PPByte(PByte(obj) + PropertyOffset[Index])^);
+ end
+ else
+ begin
+ step := PropertyStructArrayStep;
+ integerPtr := PInteger(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ +
+ PropertyOffset[Index]
+ );
+ end;
+
+ Result := DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, count);
+ for i := 1 to count do
+ begin
+ Result[i - 1] := DSS_CopyStringAsPChar(TDSSEnum(Pointer(PropertyOffset2[Index])).OrdinalToString(integerPtr^));
+ integerPtr := PInteger(ptruint(integerPtr) + PropertyStructArrayStep);
+ end;
+ end;
+
+ TPropertyType.DSSObjectReferenceProperty:
+ begin
+ if not (TPropertyFlag.OnArray in PropertyFlags[Index]) then
+ Exit;
+
+ count := PInteger(PtrUint(obj) + PropertyStructArrayCountOffset)^;
+ if count <= 0 then
+ Exit;
+
+ otherObjPtr := TDSSObjectPtr(PPByte(PtrUint(obj) + PropertyOffset[Index])^);
+ Result := DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, count);
+ if TPropertyFlag.FullNameAsArray in PropertyFlags[Index] then
+ for i := 1 to count do
+ begin
+ if otherObjPtr^ <> NIL then
+ Result[i - 1] := DSS_CopyStringAsPChar(otherObjPtr^.FullName)
+ else
+ Result[i - 1] := NIL;
+
+ inc(otherObjPtr);
+ end
+ else
+ for i := 1 to count do
+ begin
+ if otherObjPtr^ <> NIL then
+ Result[i - 1] := DSS_CopyStringAsPChar(otherObjPtr^.Name)
+ else
+ Result[i - 1] := NIL;
+
+ inc(otherObjPtr);
+ end;
+ end;
+
+ TPropertyType.MappedStringEnumArrayProperty:
+ begin
+ if not (TPropertyFlag.SizeIsFunction in PropertyFlags[Index]) then
+ count := TIntegerPropertyFunction(Pointer(PropertyOffset3[Index]))(obj)
+ else
+ count := PropertyOffset3[Index];
+
+ if count <= 0 then
+ Exit;
+
+ integerPtr := PPInteger(PByte(obj) + PropertyOffset[Index])^;
+
+ Result := DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, count);
+ for i := 1 to count do
+ begin
+ Result[i - 1] := DSS_CopyStringAsPChar(TDSSEnum(Pointer(PropertyOffset2[Index])).OrdinalToString(integerPtr^));
+ Inc(integerPtr);
+ end;
+ end;
+ TPropertyType.StringOnStructArrayProperty, // shortcut
+ TPropertyType.StringOnArrayProperty: // shortcut
+ begin
+ // Number of items
+ count := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+
+ if count <= 0 then
+ Exit;
+
+ if PropertyType[Index] = TPropertyType.StringOnStructArrayProperty then
+ begin
+ step := PropertyStructArrayStep;
+ stringPtr := PString(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ + // Pointer to the pointer struct array
+ PropertyOffset[Index] // base field
+ );
+ end
+ else
+ begin
+ step := SizeOf(String);
+ stringPtr := PString(
+ PPByte(PByte(obj) + PropertyOffset[Index])^ // Pointer to the pointer array
+ );
+ end;
+
+ Result := DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, count);
+ for i := 1 to count do
+ begin
+ Result[i - 1] := DSS_CopyStringAsPChar(stringPtr^);
+ stringPtr := PString(ptruint(stringPtr) + step);
+ end;
+ end;
+ TPropertyType.MappedStringEnumArrayOnStructArrayProperty:
+ begin
+ // Number of items
+ count := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+
+ if count <= 0 then
+ Exit;
+
+ // Pointer to the first of the target fields
+ integerPtr := PInteger(
+ PPByte(PByte(obj) + PropertyStructArrayOffset)^ +
+ PropertyOffset[Index]
+ );
+
+ Result := DSS_RecreateArray_PPAnsiChar(ResultPtr, ResultCount, count);
+ for i := 1 to count do
+ begin
+ Result[i - 1] := DSS_CopyStringAsPChar(TDSSEnum(Pointer(PropertyOffset2[Index])).OrdinalToString(integerPtr^));
+ integerPtr := PInteger(ptruint(integerPtr) + PropertyStructArrayStep);
+ end;
+ end;
+ end;
+end;
+
+procedure TDSSClassHelper.GetObjObjects(Obj: Pointer; Index: Integer; var ResultPtr: PPointer; ResultCount: PAPISize);
+var
+ i, count: Integer;
+ otherObjPtr: TDSSObjectPtr;
+ Result: PPointerArray0;
+begin
+ Result := NIL;
+ if not ((Index > 0) and (Index <= NumProperties) and (PropertyOffset[Index] <> -1)) then
+ Exit;
+
+ case PropertyType[Index] of
+ TPropertyType.DSSObjectReferenceArrayProperty:
+ begin
+ if TPropertyFlag.ReadByFunction in PropertyFlags[Index] then
+ begin
+ TObjRefsPropertyFunction(Pointer(PropertyReadFunction[Index]))(obj, ResultPtr, ResultCount);
+ Exit;
+ end;
+
+ // Number of items
+ count := PInteger(PByte(obj) + PropertyStructArrayCountOffset)^;
+
+ if count < 1 then
+ begin
+ DoSimpleMsg(
+ Format('%s.%s: No objects are expected! Check if the order of property assignments is correct.',
+ [TDSSObject(obj).FullName, PropertyName[Index]]
+ ), 402);
+ Exit;
+ end;
+
+ // Start of array
+ otherObjPtr := TDSSObjectPtrPtr((PtrUint(obj) + PtrUint(PropertyOffset[Index])))^;
+
+ Result := DSS_RecreateArray_PPointer(ResultPtr, ResultCount, count);
+
+ // TODO: if cls = NIL,..
+ for i := 1 to count do
+ begin
+ Result[i - 1] := otherObjPtr^;
+ Inc(otherObjPtr);
+ end;
+ end;
+ end;
+end;
end.
\ No newline at end of file
diff --git a/src/General/GrowthShape.pas b/src/General/GrowthShape.pas
index 03c963088..596d70a0a 100644
--- a/src/General/GrowthShape.pas
+++ b/src/General/GrowthShape.pas
@@ -7,42 +7,38 @@
----------------------------------------------------------
}
-{ 8-18-00 Added call to InterpretDblArrayto allow File=Syntax }
-
interface
-{The GrowthShape object is a general DSS object used by all circuits
- as a reference for obtaining yearly growth curves.
-
- The values are set by the normal New and Edit procedures as for any DSS object.
-
- The values are retrieved by setting the Code Property in the GrowthShape Class.
- This sets the active GrowthShapeObj object to be the one referenced by the Code Property;
-
- Then the values of that code can be retrieved via the public variables. Or you
- can pick up the ActiveGrowthShapeObj object and save the direct reference to the object.
-
- Growth shapes are entered as multipliers for the previous year's load. If the
- load grows by 2.5% in a year, the multiplier is entered as 1.025. You do not need
- to enter subsequent years if the multiplier remains the same. You need only enter
- the years in which the growth rate is assumed to have changed.
-
- The user may place the data in CSV or binary files as well as passing through the
- command interface. The rules are the same as for LoadShapes except that the year
- is always entered. CSV files are text separated by commas, one interval to a line.
- There are two binary formats permitted: 1) a file of Singles; 2) a file of Doubles.
-
- (Year, multiplier) pairs are expected in all formats. Through the COM interface,
- supply separate arrays of Year and Mult.
-
- Edit growthshape.allisonsub npts=5
- ~ year="1999 2000 2001 2005 2010"
- ~ mult="1.10 1.07 1.05 1.025 1.01"
-
- This example describes a growth curve that start off relatively fast (10%) and after
- 10 years tapers off to 1%
-
- }
+// The GrowthShape object is a general DSS object used by all circuits
+// as a reference for obtaining yearly growth curves.
+//
+// The values are set by the normal New and Edit procedures as for any DSS object.
+//
+// The values are retrieved by setting the Code Property in the GrowthShape Class.
+// This sets the active GrowthShapeObj object to be the one referenced by the Code Property;
+//
+// Then the values of that code can be retrieved via the public variables. Or you
+// can pick up the ActiveGrowthShapeObj object and save the direct reference to the object.
+//
+// Growth shapes are entered as multipliers for the previous year's load. If the
+// load grows by 2.5% in a year, the multiplier is entered as 1.025. You do not need
+// to enter subsequent years if the multiplier remains the same. You need only enter
+// the years in which the growth rate is assumed to have changed.
+//
+// The user may place the data in CSV or binary files as well as passing through the
+// command interface. The rules are the same as for LoadShapes except that the year
+// is always entered. CSV files are text separated by commas, one interval to a line.
+// There are two binary formats permitted: 1) a file of Singles; 2) a file of Doubles.
+//
+// (Year, multiplier) pairs are expected in all formats. Through the COM interface,
+// supply separate arrays of Year and Mult.
+//
+// Edit growthshape.allisonsub npts=5
+// ~ year="1999 2000 2001 2005 2010"
+// ~ mult="1.10 1.07 1.05 1.025 1.01"
+//
+// This example describes a growth curve that start off relatively fast (10%) and after
+// 10 years tapers off to 1%
uses
Classes,
@@ -53,422 +49,177 @@ interface
Arraydef;
type
+{$SCOPEDENUMS ON}
+ TGrowthShapeProp = (
+ INVALID = 0,
+ npts = 1, // Number of points to expect
+ year = 2, // vector of year values
+ mult = 3, // vector of multiplier values corresponding to years
+ csvfile = 4, // Switch input to a csvfile (year, mult)
+ sngfile = 5, // switch input to a binary file of singles (year, mult)
+ dblfile = 6 // switch input to a binary file of doubles (year, mult)
+ );
+{$SCOPEDENUMS OFF}
TGrowthShape = class(TDSSClass)
- PRIVATE
-
- function Get_Code: String; // Returns active GrowthShape string
- procedure Set_Code(const Value: String); // sets the active GrowthShape
-
- procedure DoCSVFile(const FileName: String);
- procedure DoSngFile(const FileName: String);
- procedure DoDblFile(const FileName: String);
PROTECTED
- procedure DefineProperties;
- function MakeLike(const ShapeName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
- // Set this property to point ActiveGrowthShapeObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
-
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TGrowthShapeObj = class(TDSSObject)
PRIVATE
Npts: Integer; // Number of points in curve
NYears: Integer; // Number of years presently allocated in look up table
- BaseYear: Integer;
-
- Year: pIntegerArray; // Year values
+ Year, // Year values
YearMult,
Multiplier: pDoubleArray; // Multipliers
+ csvfile, dblfile, sngfile: String;
procedure ReCalcYearMult;
PUBLIC
-
constructor Create(ParClass: TDSSClass; const GrowthShapeName: String);
destructor Destroy; OVERRIDE;
-
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
function GetMult(Yr: Integer): Double; // Get multiplier for Specified Year
end;
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Sysutils,
- Ucomplex,
+ UComplex, DSSUcomplex,
MathUtil,
Utilities,
- BufStream,
DSSHelper,
DSSObjectHelper,
TypInfo;
+type
+ TObj = TGrowthShapeObj;
+ TProp = TGrowthShapeProp;
const
- NumPropsThisClass = 6;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TGrowthShape.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TGrowthShape.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'GrowthShape';
- DSSClassType := DSS_OBJECT;
-
- ActiveElement := 0;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := FALSE;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
+ inherited Create(dssContext, DSS_OBJECT, 'GrowthShape');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TGrowthShape.Destroy;
-
begin
-
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
procedure TGrowthShape.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
- AllocatePropertyArrays;
+ PropertyType[ord(TProp.year)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.year)] := ptruint(@obj.Year);
+ PropertyOffset2[ord(TProp.year)] := ptruint(@obj.Npts);
+ PropertyFlags[ord(TProp.year)] := [TPropertyFlag.IntegerToDouble];
+ PropertyType[ord(TProp.mult)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.mult)] := ptruint(@obj.Multiplier);
+ PropertyOffset2[ord(TProp.mult)] := ptruint(@obj.Npts);
- // Define Property names
+ // strings
+ PropertyType[ord(TProp.csvfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.csvfile)] := ptruint(@obj.csvfile);
+ PropertyFlags[ord(TProp.csvfile)] := [TPropertyFlag.IsFilename];
- PropertyName[1] := 'npts'; // Number of points to expect
- PropertyName[2] := 'year'; // vextor of year values
- PropertyName[3] := 'mult'; // vector of multiplier values corresponding to years
- PropertyName[4] := 'csvfile'; // Switch input to a csvfile (year, mult)
- PropertyName[5] := 'sngfile'; // switch input to a binary file of singles (year, mult)
- PropertyName[6] := 'dblfile'; // switch input to a binary file of doubles (year, mult)
+ PropertyType[ord(TProp.dblfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.dblfile)] := ptruint(@obj.dblfile);
+ PropertyFlags[ord(TProp.dblfile)] := [TPropertyFlag.IsFilename];
- PropertyHelp[1] := 'Number of points to expect in subsequent vector.';
- PropertyHelp[2] := 'Array of year values, or a text file spec, corresponding to the multipliers. ' +
- 'Enter only those years where the growth changes. ' +
- 'May be any integer sequence -- just so it is consistent. See help on Mult.';
- PropertyHelp[3] := 'Array of growth multiplier values, or a text file spec, corresponding to the year values. ' +
- 'Enter the multiplier by which you would multiply the previous year''s load to get the present year''s.' +
- CRLF + CRLF + 'Examples:' + CRLF + CRLF +
- ' Year = [1, 2, 5] Mult=[1.05, 1.025, 1.02].' + CRLF +
- ' Year= (File=years.txt) Mult= (file=mults.txt).' + CRLF + CRLF +
- 'Text files contain one value per line.';
- PropertyHelp[4] := 'Switch input of growth curve data to a csv file containing (year, mult) points, one per line.';
- PropertyHelp[5] := 'Switch input of growth curve data to a binary file of singles ' +
- 'containing (year, mult) points, packed one after another.';
- PropertyHelp[6] := 'Switch input of growth curve data to a binary file of doubles ' +
- 'containing (year, mult) points, packed one after another.';
+ PropertyType[ord(TProp.sngfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.sngfile)] := ptruint(@obj.sngfile);
+ PropertyFlags[ord(TProp.sngfile)] := [TPropertyFlag.IsFilename];
+ // integer properties
+ PropertyType[ord(TProp.Npts)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Npts)] := ptruint(@obj.Npts);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
+ inherited DefineProperties;
+ CommandList.Abbrev := FALSE; //TODO: why only this class?
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGrowthShape.NewObject(const ObjName: String): Integer;
-begin
- // create a new object of this class and add to list
- DSS.ActiveDSSObject := TGrowthShapeObj.Create(Self, ObjName);
- Result := AddObjectToList(DSS.ActiveDSSObject);
-end;
-
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGrowthShape.Edit: Integer;
+function TGrowthShape.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
- YrBuffer: pDoubleArray;
- i: Integer;
-
+ Obj: TObj;
begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveGrowthShapeObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActiveGrowthShapeObj;
-
- with DSS.ActiveGrowthShapeObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 600);
- 1:
- Npts := Parser.Intvalue;
- 2:
- begin
- ReAllocmem(Year, Sizeof(Year^[1]) * Npts);
- YrBuffer := Allocmem(Sizeof(Double) * Npts);
- Npts := InterpretDblArray(Param, Npts, YrBuffer); // Parser.ParseAsVector(Npts, Yrbuffer);
-
- for i := 1 to Npts do
- Year^[i] := Round(YrBuffer^[i]);
- BaseYear := Year^[1];
- FreeMem(YrBuffer, Sizeof(Double) * Npts);
- end;
- 3:
- begin
- ReAllocmem(Multiplier, Sizeof(Multiplier^[1]) * Npts);
- Npts := InterpretDblArray(Param, Npts, Multiplier); //Parser.ParseAsVector(Npts, Multiplier);
-
- end;
- 4:
- DoCSVFile(AdjustInputFilePath(Param));
- 5:
- DoSngFile(AdjustInputFilePath(Param));
- 6:
- DoDblFile(AdjustInputFilePath(Param));
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveGrowthShapeObj, ParamPointer - NumPropsThisClass)
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end; {WHILE}
-
- ReCalcYearMult;
- end; {WITH}
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGrowthShape.MakeLike(const ShapeName: String): Integer;
-var
- OtherGrowthShape: TGrowthShapeObj;
- i: Integer;
-begin
- Result := 0;
- {See if we can find this line code in the present collection}
- OtherGrowthShape := Find(ShapeName);
- if OtherGrowthShape <> NIL then
- with DSS.ActiveGrowthShapeObj do
- begin
- Npts := OtherGrowthShape.Npts;
- ReallocMem(Multiplier, SizeOf(Multiplier^[1]) * Npts);
- for i := 1 to Npts do
- Multiplier^[i] := OtherGrowthShape.Multiplier^[i];
- ReallocMem(Year, SizeOf(Year^[1]) * Npts);
- for i := 1 to Npts do
- Year^[i] := OtherGrowthShape.Year^[i];
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherGrowthShape.PropertyValue[i];
-
- end
- else
- DoSimpleMsg('Error in GrowthShape MakeLike: "' + ShapeName + '" Not Found.', 601);
-
-
-end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGrowthShape.Get_Code: String; // Returns active line code string
-var
- GrowthShapeObj: TGrowthShapeObj;
-
-begin
-
- GrowthShapeObj := ElementList.Active;
- Result := GrowthShapeObj.Name;
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TGrowthShape.Set_Code(const Value: String); // sets the active GrowthShape
-
-var
- GrowthShapeObj: TGrowthShapeObj;
-
-begin
-
- DSS.ActiveGrowthShapeObj := NIL;
- GrowthShapeObj := ElementList.First;
- while GrowthShapeObj <> NIL do
- begin
-
- if CompareText(GrowthShapeObj.Name, Value) = 0 then
- begin
- DSS.ActiveGrowthShapeObj := GrowthShapeObj;
- Exit;
- end;
-
- GrowthShapeObj := ElementList.Next;
- end;
-
- DoSimpleMsg('GrowthShape: "' + Value + '" not Found.', 602);
-
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TGrowthShape.DoCSVFile(const FileName: String);
-
-var
- F: TBufferedFileStream = nil;
- i: Integer;
- s: String;
-
+procedure TGrowthShapeObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- try
- F := TBufferedFileStream.Create(FileName, fmOpenRead);
- except
- DoSimpleMsg('Error Opening File: "' + FileName, 603);
- FreeAndNil(F);
- Exit;
- end;
-
- try
- with DSS.ActiveGrowthShapeObj do
+ case Idx of
+ ord(TProp.Npts):
begin
- i := 0;
- while ((F.Position + 1) < F.Size) and (i < Npts) do
- begin
- Inc(i);
- FSReadln(F, s); {Use AuxParser to allow flexible formats}
- with AuxParser do
- begin
- // Readln(F,Year^[i], Multiplier^[i]);
- CmdString := S;
- NextParam;
- Year^[i] := IntValue;
- NextParam;
- Multiplier^[i] := DblValue;
- end;
- end;
- F.Free();
- end;
- except
- On E: Exception do
- begin
- DoSimpleMsg('Error Processing CSV File: "' + FileName + '. ' + E.Message, 604);
- FreeAndNil(F);
- Exit;
+ ReAllocmem(Year, Sizeof(Double) * Npts);
+ ReAllocmem(Multiplier, Sizeof(Double) * Npts);
end;
+ ord(TProp.csvfile):
+ DoCSVFile(DSS, Year, Multiplier, Npts, False, csvfile, ParentClass.Name, True);
+ ord(TProp.sngfile):
+ DoSngFile(DSS, Year, Multiplier, Npts, False, sngfile, ParentClass.Name, True);
+ ord(TProp.dblfile):
+ DoDblFile(DSS, Year, Multiplier, Npts, False, dblfile, ParentClass.Name, True);
end;
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TGrowthShape.DoSngFile(const FileName: String);
-var
- F: TFileStream = nil;
- Y, M: Single;
- i: Integer;
-
+function TGrowthShape.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
begin
- try
- F := TFileStream.Create(FileName, fmOpenRead);
- except
- DoSimpleMsg('Error Opening File: "' + FileName, 605);
- FreeAndNil(F);
- Exit;
- end;
-
- try
- with DSS.ActiveGrowthShapeObj do
- begin
- i := 0;
- while ((F.Position + 1) < F.Size) and (i < Npts) do
- begin
- Inc(i);
- if F.Read(Y, SizeOf(Y)) <> SizeOf(Y) then
- Break;
- if F.Read(M, SizeOf(M)) <> SizeOf(M) then
- Break;
-
- Year^[i] := Round(Y);
- Multiplier^[i] := M;
- end;
- F.Free();
- end;
- except
- DoSimpleMsg('Error Processing GrowthShape File: "' + FileName, 606);
- FreeAndNil(F);
- Exit;
- end;
-
+ TObj(ptr).ReCalcYearMult;
+ Exclude(TObj(ptr).Flags, Flg.EditionActive);
+ Result := True;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TGrowthShape.DoDblFile(const FileName: String);
+procedure TGrowthShapeObj.MakeLike(OtherPtr: Pointer);
var
- F: TFileStream = nil;
+ Other: TObj;
i: Integer;
- Yr: Double;
-
begin
- try
- F := TFileStream.Create(FileName, fmOpenRead);
- except
- DoSimpleMsg('Error Opening File: "' + FileName, 607);
- FreeAndNil(F);
- Exit;
- end;
-
- try
- with DSS.ActiveGrowthShapeObj do
- begin
- i := 0;
- while ((F.Position + 1) < F.Size) and (i < Npts) do
- begin
- Inc(i);
- if F.Read(Yr, SizeOf(Yr)) <> SizeOf(Yr) then
- Break;
- if F.Read(Multiplier^[i], SizeOf(Double)) <> SizeOf(Double) then
- Break;
- Year^[i] := Round(Yr);
- end;
- F.Free();
- end;
- except
- DoSimpleMsg('Error Processing GrowthShape File: "' + FileName, 608);
- FreeAndNil(F);
- Exit;
- end;
-
-
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ Npts := Other.Npts;
+ ReallocMem(Multiplier, SizeOf(Double) * Npts);
+ for i := 1 to Npts do
+ Multiplier^[i] := Other.Multiplier^[i];
+ ReallocMem(Year, SizeOf(Double) * Npts);
+ for i := 1 to Npts do
+ Year^[i] := Other.Year^[i];
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TGrowthShape Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TGrowthShapeObj.Create(ParClass: TDSSClass; const GrowthShapeName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(GrowthShapeName);
+ Name := AnsiLowerCase(GrowthShapeName);
DSSObjType := ParClass.DSSClassType;
Npts := 0;
@@ -476,71 +227,55 @@ constructor TGrowthShapeObj.Create(ParClass: TDSSClass; const GrowthShapeName: S
Multiplier := NIL;
NYears := 30;
YearMult := AllocMem(SizeOf(yearMult^[1]) * NYears);
-
- InitPropertyValues(0);
-
+ csvfile := '';
+ dblfile := '';
+ sngfile := '';
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TGrowthShapeObj.Destroy;
begin
-
ReallocMem(Year, 0);
ReallocMem(Multiplier, 0);
ReallocMem(YearMult, 0);
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function TGrowthShapeObj.GetMult(Yr: Integer): Double;
-
// This function returns the multiplier to use for a load in the given year.
// The first year specified in the curve is the base year. The Base value
// is the beginning of the first year.
-
var
Index: Integer;
-
begin
-
Result := 1.0; // default return value if no points in curve
if NPts > 0 then
begin // Handle Exceptional cases
- Index := Yr - BaseYear;
+ Index := Yr - Round(Year^[1]);
if Index > 0 then
begin // Returns 1.0 for base year or any year previous
-
if Index > Nyears then
begin // Make some more space
NYears := Index + 10;
ReallocMem(YearMult, SizeOf(YearMult^[1]) * NYears);
ReCalcYearMult;
end;
-
Result := YearMult^[Index];
-
end;
-
end;
-
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TGrowthShapeObj.ReCalcYearMult;
-
var
i, DataPtr, Yr: Integer;
Mult, MultInc: Double;
-
begin
// Fill up the YearMult array with total yearly multiplier from base year
Mult := Multiplier^[1];
MultInc := Mult;
YearMult^[1] := Mult;
DataPtr := 1;
- Yr := BaseYear;
+ Yr := Round(Year^[1]);
for i := 2 to NYears do
begin
Inc(Yr);
@@ -557,74 +292,4 @@ procedure TGrowthShapeObj.ReCalcYearMult;
end;
end;
-procedure TGrowthShapeObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
-
- with ParentClass do
- begin
- for i := 1 to NumProperties do
- begin
- case i of
- 2, 3:
- FSWriteln(F, '~ ' + PropertyName^[i] + '=(' + PropertyValue[i] + ')');
- else
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
- end;
-
- end;
-
-end;
-
-
-function TGrowthShapeObj.GetPropertyValue(Index: Integer): String;
-var
- i: Integer;
-begin
- case Index of
- 2, 3:
- Result := '(';
- else
- Result := '';
- end;
-
- case Index of
- 2:
- for i := 1 to Npts do
- Result := Result + Format('%-d, ', [Year^[i]]);
- 3:
- for i := 1 to Npts do
- Result := Result + Format('%-g, ', [Multiplier^[i]]);
- else
- Result := inherited GetPropertyValue(index);
- end;
-
- case Index of
- 2, 3:
- Result := Result + ')';
- else
- end;
-
-end;
-
-procedure TGrowthShapeObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := '0'; // Number of points to expect
- PropertyValue[2] := ''; // vextor of year values
- PropertyValue[3] := ''; // vector of multiplier values corresponding to years
- PropertyValue[4] := ''; // Switch input to a csvfile (year, mult)
- PropertyValue[5] := ''; // switch input to a binary file of singles (year, mult)
- PropertyValue[6] := ''; // switch input to a binary file of doubles (year, mult)
-
- inherited InitPropertyValues(NumPropsThisClass);
-end;
-
-
-end.
+end.
\ No newline at end of file
diff --git a/src/General/LineCode.pas b/src/General/LineCode.pas
index 5ae2edb4e..aff3c9284 100644
--- a/src/General/LineCode.pas
+++ b/src/General/LineCode.pas
@@ -9,17 +9,15 @@
interface
-{The Linecode object is a general DSS object used by all circuits
- as a reference for obtaining line impedances.
-
- The values are set by the normal New and Edit procedures for any DSS object.
-
- The values are retrieved by setting the Code Property in the LineCode Class.
- This sets the active Linecode object to be the one referenced by the Code Property;
-
- Then the values of that code can be retrieved via the public variables.
-
- }
+// The Linecode object is a general DSS object used by all circuits
+// as a reference for obtaining line impedances.
+//
+// The values are set by the normal New and Edit procedures for any DSS object.
+//
+// The values are retrieved by setting the Code Property in the LineCode Class.
+// This sets the active Linecode object to be the one referenced by the Code Property;
+//
+// Then the values of that code can be retrieved via the public variables.
uses
Classes,
@@ -30,55 +28,61 @@ interface
Arraydef;
type
+{$SCOPEDENUMS ON}
+ TLineCodeProp = (
+ INVALID = 0,
+ nphases = 1,
+ r1 = 2,
+ x1 = 3,
+ r0 = 4,
+ x0 = 5,
+ C1 = 6,
+ C0 = 7,
+ units = 8,
+ rmatrix = 9,
+ xmatrix = 10,
+ cmatrix = 11,
+ baseFreq = 12,
+ normamps = 13,
+ emergamps = 14,
+ faultrate = 15,
+ pctperm = 16,
+ repair = 17,
+ Kron = 18,
+ Rg = 19,
+ Xg = 20,
+ rho = 21,
+ neutral = 22,
+ B1 = 23,
+ B0 = 24,
+ Seasons = 25,
+ Ratings = 26,
+ LineType = 27
+ );
+{$SCOPEDENUMS OFF}
TLineCode = class(TDSSClass)
- PRIVATE
- SymComponentsChanged: Boolean;
- MatrixChanged: Boolean;
-
- function Get_Code: String; // Returns active line code string
- procedure Set_Code(const Value: String); // sets the active linecode
-
- procedure SetZ1Z0(i: Integer; Value: Double);
- procedure SetUnits(const s: String); // decode units specification
-
- procedure DoMatrix(i: Integer); // set impedances as matrices
-
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const LineName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
- LineTypeList: TCommandList;
-
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
- // Set this property to point ActiveLineCodeObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
-
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TLineCodeObj = class(TDSSObject)
PRIVATE
-
FNeutralConductor: Integer;
- procedure Set_NPhases(Value: Integer);
procedure DoKronReduction;
- function get_Rmatrix: String;
- function get_Xmatrix: String;
- function get_CMatrix: String;
PUBLIC
NumAmpRatings,
FNPhases: Integer;
- SymComponentsModel,
- ReduceByKron: Boolean;
+ SymComponentsModel: LongBool;
Z, // Base Frequency Series Z matrix
Zinv,
@@ -103,535 +107,268 @@ TLineCodeObj = class(TDSSObject)
AmpRatings: array of Double;
FLineType: Integer; // Pointer to code for type of line
- Units: Integer; {See LineUnits}
+ Units: Integer; // See LineUnits
constructor Create(ParClass: TDSSClass; const LineCodeName: String);
destructor Destroy; OVERRIDE;
- property NumPhases: Integer READ FNPhases WRITE Set_Nphases;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
+
procedure CalcMatricesFromZ1Z0;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
+ procedure Set_NumPhases(Value: Integer);
+ property NumPhases: Integer read FNPhases write Set_NumPhases;
end;
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Sysutils,
- Ucomplex,
+ UComplex, DSSUcomplex,
Utilities,
LineUnits,
DSSHelper,
DSSObjectHelper,
TypInfo;
+type
+ TObj = TLineCodeObj;
+ TProp = TLineCodeProp;
const
- NumPropsThisClass = 27;
-
- LINE_TYPES: Array of String = [
- 'oh', 'ug', 'ug_ts', 'ug_cn', 'swt_ldbrk', 'swt_fuse',
- 'swt_sect', 'swt_rec', 'swt_disc', 'swt_brk', 'swt_elbow'
- ];
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TLineCode.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TLineCode.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'LineCode';
- DSSClassType := DSS_OBJECT;
- ActiveElement := 0;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- LineTypeList := TCommandList.Create(LINE_TYPES);
- LineTypeList.Abbrev := TRUE; // Allow abbreviations for line type code
+ inherited Create(dssContext, DSS_OBJECT, 'LineCode');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TLineCode.Destroy;
begin
- LineTypeList.Free();
-
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLineCode.DefineProperties;
-begin
-
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- PropertyName[1] := 'nphases';
- PropertyName[2] := 'r1';
- PropertyName[3] := 'x1';
- PropertyName[4] := 'r0';
- PropertyName[5] := 'x0';
- PropertyName[6] := 'C1';
- PropertyName[7] := 'C0';
- PropertyName[8] := 'units';
- PropertyName[9] := 'rmatrix';
- PropertyName[10] := 'xmatrix';
- PropertyName[11] := 'cmatrix';
- PropertyName[12] := 'baseFreq';
- PropertyName[13] := 'normamps';
- PropertyName[14] := 'emergamps';
- PropertyName[15] := 'faultrate';
- PropertyName[16] := 'pctperm';
- PropertyName[17] := 'repair';
- PropertyName[18] := 'Kron';
- PropertyName[19] := 'Rg';
- PropertyName[20] := 'Xg';
- PropertyName[21] := 'rho';
- PropertyName[22] := 'neutral';
- PropertyName[23] := 'B1';
- PropertyName[24] := 'B0';
- PropertyName[25] := 'Seasons';
- PropertyName[26] := 'Ratings';
- PropertyName[27] := 'LineType';
-
- PropertyHelp[1] := 'Number of phases in the line this line code data represents. Setting this property reinitializes the line code. Impedance matrix is reset for default symmetrical component.';
- PropertyHelp[2] := 'Positive-sequence Resistance, ohms per unit length. Setting any of R1, R0, X1, X0, C1, C0 forces ' +
- 'the program to use the symmetrical component line definition. See also Rmatrix.';
- PropertyHelp[3] := 'Positive-sequence Reactance, ohms per unit length. Setting any of R1, R0, X1, X0, C1, C0 forces ' +
- 'the program to use the symmetrical component line definition. See also Xmatrix';
- PropertyHelp[4] := 'Zero-sequence Resistance, ohms per unit length. Setting any of R1, R0, X1, X0, C1, C0 forces ' +
- 'the program to use the symmetrical component line definition.';
- PropertyHelp[5] := 'Zero-sequence Reactance, ohms per unit length. Setting any of R1, R0, X1, X0, C1, C0 forces ' +
- 'the program to use the symmetrical component line definition.';
- PropertyHelp[6] := 'Positive-sequence capacitance, nf per unit length. Setting any of R1, R0, X1, X0, C1, C0 forces ' +
- 'the program to use the symmetrical component line definition. See also Cmatrix and B1.';
- PropertyHelp[7] := 'Zero-sequence capacitance, nf per unit length. Setting any of R1, R0, X1, X0, C1, C0 forces ' +
- 'the program to use the symmetrical component line definition. See also B0.';
- PropertyHelp[8] := 'One of (ohms per ...) {none|mi|km|kft|m|me|ft|in|cm}. Default is none; assumes units agree with length units' +
- 'given in Line object';
- PropertyHelp[9] := 'Resistance matrix, lower triangle, ohms per unit length. Order of the matrix is the number of phases. ' +
- 'May be used to specify the impedance of any line configuration. For balanced line models, you may ' +
- 'use the standard symmetrical component data definition instead.';
- PropertyHelp[10] := 'Reactance matrix, lower triangle, ohms per unit length. Order of the matrix is the number of phases. ' +
- 'May be used to specify the impedance of any line configuration. For balanced line models, you may ' +
- 'use the standard symmetrical component data definition instead.';
- PropertyHelp[11] := 'Nodal Capacitance matrix, lower triangle, nf per unit length.Order of the matrix is the number of phases. ' +
- 'May be used to specify the shunt capacitance of any line configuration. For balanced line models, you may ' +
- 'use the standard symmetrical component data definition instead.';
- PropertyHelp[12] := 'Frequency at which impedances are specified.';
- PropertyHelp[13] := 'Normal ampere limit on line. This is the so-called Planning Limit. It may also be ' +
- 'the value above which load will have to be dropped in a contingency. Usually about ' +
- '75% - 80% of the emergency (one-hour) rating.';
- PropertyHelp[14] := 'Emergency ampere limit on line (usually one-hour rating).';
- PropertyHelp[15] := 'Number of faults per unit length per year.';
- PropertyHelp[16] := 'Percentage of the faults that become permanent.';
- PropertyHelp[17] := 'Hours to repair.';
- PropertyHelp[18] := 'Kron = Y/N. Default=N. Perform Kron reduction on the impedance matrix after it is formed, reducing order by 1. ' +
- 'Eliminates the conductor designated by the "Neutral=" property. ' +
- 'Do this after the R, X, and C matrices are defined. Ignored for symmetrical components. ' +
- 'May be issued more than once to eliminate more than one conductor by resetting the Neutral property after the previous ' +
- 'invoking of this property. Generally, you do not want to do a Kron reduction on the matrix if you intend to solve at a ' +
- 'frequency other than the base frequency and exploit the Rg and Xg values.';
- PropertyHelp[19] := 'Carson earth return resistance per unit length used to compute impedance values at base frequency. For making better frequency adjustments. ' +
- 'Default is 0.01805 = 60 Hz value in ohms per kft (matches default line impedances). ' +
- 'This value is required for harmonic solutions if you wish to adjust the earth return impedances for frequency. ' +
- 'If not, set both Rg and Xg = 0.';
- PropertyHelp[20] := 'Carson earth return reactance per unit length used to compute impedance values at base frequency. For making better frequency adjustments. ' +
- 'Default value is 0.155081 = 60 Hz value in ohms per kft (matches default line impedances). ' +
- 'This value is required for harmonic solutions if you wish to adjust the earth return impedances for frequency. ' +
- 'If not, set both Rg and Xg = 0.';
- PropertyHelp[21] := 'Default=100 meter ohms. Earth resitivity used to compute earth correction factor.';
- PropertyHelp[22] := 'Designates which conductor is the "neutral" conductor that will be eliminated by Kron reduction. ' +
- 'Default is the last conductor (nphases value). After Kron reduction is set to 0. Subsequent issuing of Kron=Yes ' +
- 'will not do anything until this property is set to a legal value. Applies only to LineCodes defined by R, X, and C matrix.';
-
- PropertyHelp[23] := 'Alternate way to specify C1. MicroS per unit length';
- PropertyHelp[24] := 'Alternate way to specify C0. MicroS per unit length';
- PropertyHelp[25] := 'Defines the number of ratings to be defined for the wire, to be used only when defining seasonal ratings using the "Ratings" property.';
- PropertyHelp[26] := 'An array of ratings to be used when the seasonal ratings flag is True. It can be used to insert' +
- CRLF + 'multiple ratings to change during a QSTS simulation to evaluate different ratings in lines.';
- PropertyHelp[27] := 'Code designating the type of line. ' + CRLF +
- 'One of: OH, UG, UG_TS, UG_CN, SWT_LDBRK, SWT_FUSE, SWT_SECT, SWT_REC, SWT_DISC, SWT_BRK, SWT_ELBOW' + CRLF + CRLF +
- 'OpenDSS currently does not use this internally. For whatever purpose the user defines. Default is OH.' ;
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
-end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLineCode.NewObject(const ObjName: String): Integer;
+function GetYCScale(obj: TLineCodeObj; getter: Boolean): Double;
begin
- // create a new object of this class and add to list
- DSS.ActiveDSSObject := TLineCodeObj.Create(Self, ObjName);
- Result := AddObjectToList(DSS.ActiveDSSObject);
+ Result := TwoPi * obj.BaseFrequency * 1.0e-9;
end;
-function TLineCodeObj.get_Rmatrix: String;
-var
- j: Integer;
- i: Integer;
+function GetC1C0Scale(obj: TLineCodeObj; getter: Boolean): Double;
begin
- Result := '[';
- for i := 1 to FNPhases do
- begin
- for j := 1 to FNphases do
- begin
- Result := Result + Format('%12.8f ', [Z.GetElement(i, j).re]);
- end;
- if i < FNphases then
- Result := Result + '|';
- end;
- Result := Result + ']';
+ Result := 1 / (TwoPi * obj.BaseFrequency) * 1.0e-6;
end;
-function TLineCodeObj.get_Xmatrix: String;
-var
- j: Integer;
- i: Integer;
+procedure Action_KronReduction(obj: TObj);
begin
- Result := '[';
- for i := 1 to FNPhases do
- begin
- for j := 1 to FNphases do
- begin
- Result := Result + Format('%12.8f ', [Z.GetElement(i, j).im]);
- end;
- if i < FNphases then
- Result := Result + '|';
- end;
- Result := Result + ']';
+ obj.DoKronReduction();
end;
-function TLineCodeObj.get_CMatrix: String;
-var
- i, j: Integer;
+procedure TLineCode.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
- Result := '[';
- for i := 1 to FNPhases do
- begin
- for j := 1 to FNphases do
- begin
- Result := Result + Format('%12.8f ', [Yc.GetElement(i, j).im / TwoPi / BaseFrequency * 1.0E9]);
- end;
- if i < FNphases then
- Result := Result + '|';
- end;
- Result := Result + ']';
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLineCode.SetUnits(const s: String);
-// decodes the units string and sets the Units variable
+ Numproperties := NumPropsThisClass;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo, False);
+
+ // matrix parts
+ PropertyType[ord(TProp.rmatrix)] := TPropertyType.ComplexPartSymMatrixProperty;
+ PropertyOffset[ord(TProp.rmatrix)] := ptruint(@obj.Z);
+ PropertyFlags[ord(TProp.rmatrix)] := [TPropertyFlag.RealPart];
+
+ PropertyType[ord(TProp.xmatrix)] := TPropertyType.ComplexPartSymMatrixProperty;
+ PropertyOffset[ord(TProp.xmatrix)] := ptruint(@obj.Z);
+ PropertyFlags[ord(TProp.xmatrix)] := [TPropertyFlag.ImagPart];
+
+ PropertyType[ord(TProp.cmatrix)] := TPropertyType.ComplexPartSymMatrixProperty;
+ PropertyOffset[ord(TProp.cmatrix)] := ptruint(@obj.YC);
+ PropertyOffset2[ord(TProp.cmatrix)] := ptruint(@GetYCScale);
+ PropertyFlags[ord(TProp.cmatrix)] := [TPropertyFlag.ScaledByFunction, TPropertyFlag.ImagPart];
+
+ // boolean properties
+ PropertyType[ord(TProp.Kron)] := TPropertyType.BooleanActionProperty;
+ PropertyOffset[ord(TProp.Kron)] := ptruint(@Action_KronReduction);
+
+ // enums
+ PropertyType[ord(TProp.units)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.units)] := ptruint(@obj.Units);
+ PropertyOffset2[ord(TProp.units)] := PtrInt(DSS.UnitsEnum);
+
+ PropertyType[ord(TProp.linetype)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.linetype)] := ptruint(@obj.FLineType);
+ PropertyOffset2[ord(TProp.linetype)] := PtrInt(DSS.LineTypeEnum);
+
+ // double arrays
+ PropertyType[ord(TProp.Ratings)] := TPropertyType.DoubleDArrayProperty;
+ PropertyOffset[ord(TProp.Ratings)] := ptruint(@obj.AmpRatings);
+ PropertyOffset2[ord(TProp.Ratings)] := ptruint(@obj.NumAmpRatings);
+
+ // integers
+ PropertyType[ord(TProp.neutral)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.Seasons)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.nphases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.neutral)] := ptruint(@obj.FNeutralConductor);
+ PropertyOffset[ord(TProp.Seasons)] := ptruint(@obj.NumAmpRatings);
+ PropertyOffset[ord(TProp.nphases)] := ptruint(@obj.FNPhases);
+
+ // doubles
+ PropertyOffset[ord(TProp.baseFreq)] := ptruint(@obj.BaseFrequency);
+ PropertyOffset[ord(TProp.normamps)] := ptruint(@obj.NormAmps);
+ PropertyOffset[ord(TProp.emergamps)] := ptruint(@obj.EmergAmps);
+ PropertyOffset[ord(TProp.faultrate)] := ptruint(@obj.FaultRate);
+ PropertyOffset[ord(TProp.pctperm)] := ptruint(@obj.PctPerm);
+ PropertyOffset[ord(TProp.repair)] := ptruint(@obj.HrsToRepair);
+ PropertyOffset[ord(TProp.Rg)] := ptruint(@obj.Rg);
+ PropertyOffset[ord(TProp.Xg)] := ptruint(@obj.Xg);
+ PropertyOffset[ord(TProp.rho)] := ptruint(@obj.rho);
+
+ PropertyOffset[ord(TProp.R1)] := ptruint(@obj.R1);
+ PropertyOffset[ord(TProp.X1)] := ptruint(@obj.X1);
+ PropertyOffset[ord(TProp.R0)] := ptruint(@obj.R0);
+ PropertyOffset[ord(TProp.X0)] := ptruint(@obj.X0);
+ PropertyFlags[ord(TProp.R1)] := [TPropertyFlag.ConditionalValue];
+ PropertyFlags[ord(TProp.X1)] := [TPropertyFlag.ConditionalValue];
+ PropertyFlags[ord(TProp.R0)] := [TPropertyFlag.ConditionalValue];
+ PropertyFlags[ord(TProp.X0)] := [TPropertyFlag.ConditionalValue];
+
+ // adv doubles
+ PropertyOffset[ord(TProp.C1)] := ptruint(@obj.C1);
+ PropertyOffset[ord(TProp.C0)] := ptruint(@obj.C0);
+ PropertyScale[ord(TProp.C1)] := 1.0e-9;
+ PropertyScale[ord(TProp.C0)] := 1.0e-9;
+ PropertyFlags[ord(TProp.C1)] := [TPropertyFlag.ConditionalValue];
+ PropertyFlags[ord(TProp.C0)] := [TPropertyFlag.ConditionalValue];
+
+ PropertyOffset[ord(TProp.B1)] := ptruint(@obj.C1);
+ PropertyOffset2[ord(TProp.B1)] := ptruint(@GetC1C0Scale);
+ PropertyFlags[ord(TProp.B1)] := [TPropertyFlag.ScaledByFunction, TPropertyFlag.Redundant, TPropertyFlag.ConditionalValue];
+ PropertyRedundantWith[ord(TProp.B1)] := ord(TProp.C1);
+
+ PropertyOffset[ord(TProp.B0)] := ptruint(@obj.C0);
+ PropertyOffset2[ord(TProp.B0)] := ptruint(@GetC1C0Scale);
+ PropertyFlags[ord(TProp.B0)] := [TPropertyFlag.ScaledByFunction, TPropertyFlag.Redundant, TPropertyFlag.ConditionalValue];
+ PropertyRedundantWith[ord(TProp.B0)] := ord(TProp.C0);
+
+ PropertyOffset3[ord(TProp.r1)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.x1)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.C1)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.B1)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.r0)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.x0)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.C0)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.B0)] := ptruint(@obj.SymComponentsModel);
-begin
- DSS.ActiveLineCodeObj.Units := GetUnitsCode(S);
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLineCode.SetZ1Z0(i: Integer; Value: Double);
-// set symmetrical component impedances and a flag to indicate they were changed
+function TLineCode.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
-
- SymComponentsChanged := TRUE;
-
-
- with DSS.ActiveLineCodeObj do
- begin
- SymComponentsModel := TRUE;
- case i of
- 1:
- R1 := Value;
- 2:
- X1 := Value;
- 3:
- R0 := Value;
- 4:
- X0 := Value;
- 5:
- C1 := Value;
- 6:
- C0 := Value;
- else
- end;
- end;
-
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLineCode.DoMatrix(i: Integer);
-
-var
- OrderFound, Norder, j: Integer;
- MatBuffer: pDoubleArray;
- Zvalues: pComplexArray;
- Factor: Double;
-
+procedure TLineCodeObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- with DSS.ActiveLineCodeObj do
- begin
- MatrixChanged := TRUE;
- MatBuffer := Allocmem(Sizeof(Double) * FNphases * FNphases);
- OrderFound := Parser.ParseAsSymMatrix(FNphases, MatBuffer);
-
- if OrderFound > 0 then // Parse was successful
- case i of
- 1:
- begin {R}
- ZValues := Z.GetValuesArrayPtr(Norder);
- if Norder = FNphases then
- for j := 1 to FNphases * FNphases do
- ZValues^[j].Re := MatBuffer^[j];
- end;
- 2:
- begin {X}
- ZValues := Z.GetValuesArrayPtr(Norder);
- if Norder = FNphases then
- for j := 1 to FNphases * FNphases do
- ZValues^[j].im := MatBuffer^[j];
- end;
- 3:
- begin {YC Matrix}
- Factor := TwoPi * BaseFrequency * 1.0e-9;
- ZValues := YC.GetValuesArrayPtr(Norder);
- if Norder = FNphases then
- for j := 1 to FNphases * FNphases do
- ZValues^[j].im := Factor * MatBuffer^[j];
- end;
- else
+ case Idx of
+ ord(TProp.nphases):
+ if FNphases <> previousIntVal then
+ begin
+ FNeutralConductor := FNphases; // Init to last conductor
+ // Put some reasonable values in these matrices
+ CalcMatricesFromZ1Z0; // reallocs matrices
end;
-
- Freemem(MatBuffer, Sizeof(Double) * FNphases * FNphases);
end;
+ case Idx of
+ 1..6, 23, 24:
+ SymComponentsModel := TRUE;
+ 9..11:
+ begin
+ Include(Flags, Flg.NeedsRecalc);
+ SymComponentsModel := FALSE;
+ end;
+ 25:
+ setlength(AmpRatings, NumAmpRatings);
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLineCode.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
+function TLineCode.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveLineCodeObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActiveLineCodeObj;
- SymComponentsChanged := FALSE;
- MatrixChanged := FALSE;
- DSS.ActiveLineCodeObj.ReduceByKron := FALSE; // Allow all matrices to be computed it raw form
-
- with DSS.ActiveLineCodeObj do
+ with TObj(ptr) do
begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 101);
- 1:
- Numphases := Parser.IntValue; // Use property value to force reallocations
- 2:
- SetZ1Z0(1, Parser.Dblvalue); {R1}
- 3:
- SetZ1Z0(2, Parser.Dblvalue); {X0}
- 4:
- SetZ1Z0(3, Parser.Dblvalue); {R1}
- 5:
- SetZ1Z0(4, Parser.Dblvalue); {X0}
- 6:
- SetZ1Z0(5, Parser.Dblvalue * 1.0e-9); {C1} // Convert from nano to farads
- 7:
- SetZ1Z0(6, Parser.Dblvalue * 1.0e-9); {C0}
- 8:
- SetUnits(Param);
- 9:
-{Rmatrix} DoMatrix(1);
- 10:
-{Xmatrix} DoMatrix(2);
- 11:
-{Cmatrix} DoMatrix(3);
- 12:
- BaseFrequency := Parser.DblValue;
- 13:
- NormAmps := Parser.Dblvalue;
- 14:
- EmergAmps := Parser.Dblvalue;
- 15:
- FaultRate := Parser.Dblvalue;
- 16:
- PctPerm := Parser.Dblvalue;
- 17:
- HrsToRepair := Parser.Dblvalue;
- 18:
- ReduceByKron := InterpretYesNo(Param);
- 19:
- Rg := Parser.DblValue;
- 20:
- Xg := Parser.DblValue;
- 21:
- rho := Parser.DblValue;
- 22:
- FNeutralConductor := Parser.IntValue;
- 23:
- SetZ1Z0(5, Parser.Dblvalue / (twopi * BaseFrequency) * 1.0e-6); {B1 -> C1}
- 24:
- SetZ1Z0(6, Parser.Dblvalue / (twopi * BaseFrequency) * 1.0e-6); {B0 -> C0}
- 25:
- begin
- NumAmpRatings := Parser.IntValue;
- setlength(AmpRatings, NumAmpRatings);
- end;
- 26:
- begin
- setlength(AmpRatings, NumAmpRatings);
- Param := Parser.StrValue;
- NumAmpRatings := InterpretDblArray(Param, NumAmpRatings, Pointer(AmpRatings));
- end;
- 27:
- FLineType := LineTypeList.Getcommand(Param);
- else
- ClassEdit(DSS.ActiveLineCodeObj, Parampointer - NumPropsThisClass)
- end;
-
- case ParamPointer of
- 9..11:
- SymComponentsModel := FALSE;
- 18:
- if ReduceByKron and not SymComponentsModel then
- DoKronReduction;
- end;
-
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
if SymComponentsModel then
CalcMatricesFromZ1Z0;
- if MatrixChanged then
+ if Flg.NeedsRecalc in Flags then
begin
+ Exclude(Flags, Flg.NeedsRecalc);
Zinv.Copyfrom(Z);
Zinv.Invert;
end;
+ Exclude(Flags, Flg.EditionActive);
end;
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLineCode.MakeLike(const LineName: String): Integer;
-var
- OtherLineCode: TLineCodeObj;
- i: Integer;
-begin
- Result := 0;
- {See if we can find this line code in the present collection}
- OtherLineCode := Find(LineName);
- if OtherLineCode <> NIL then
- with DSS.ActiveLineCodeObj do
- begin
-
- if FNPhases <> OtherLineCode.FNphases then
- begin
- FNphases := OtherLineCode.FNphases;
-
- if Z <> NIL then
- Z.Free;
- if Zinv <> NIL then
- Zinv.Free;
- if Yc <> NIL then
- Yc.Free;
-
- Z := TCmatrix.CreateMatrix(FNphases);
- Zinv := TCMatrix.CreateMatrix(FNphases);
- Yc := TCMatrix.CreateMatrix(FNphases);
- end;
-
- Z.CopyFrom(OtherLineCode.Z);
- Zinv.CopyFrom(OtherLineCode.Zinv);
- Yc.CopyFrom(OtherLineCode.Yc);
- BaseFrequency := OtherLineCode.BaseFrequency;
- R1 := OtherLineCode.R1;
- X1 := OtherLineCode.X1;
- R0 := OtherLineCode.R0;
- X0 := OtherLineCode.X0;
- C1 := OtherLineCode.C1;
- C0 := OtherLineCode.C0;
- Rg := OtherLineCode.Rg;
- Xg := OtherLineCode.Xg;
- rho := OtherLineCode.rho;
- FNeutralConductor := OtherLineCode.FNeutralConductor;
- NormAmps := OtherLineCode.NormAmps;
- EmergAmps := OtherLineCode.EmergAmps;
- FaultRate := OtherLineCode.FaultRate;
- PctPerm := OtherLineCode.PctPerm;
- HrsToRepair := OtherLineCode.HrsToRepair;
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherLineCode.PropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Line MakeLike: "' + LineName + '" Not Found.', 102);
-
-
-end;
-
-function TLineCode.Get_Code: String; // Returns active line code string
-begin
- Result := TlineCodeObj(ElementList.Active).Name;
+ Result := True;
end;
-procedure TLineCode.Set_Code(const Value: String); // sets the active linecode
+procedure TLineCodeObj.MakeLike(OtherPtr: Pointer);
var
- LineCodeObj: TLineCodeObj;
+ Other: TObj;
begin
-
- DSS.ActiveLineCodeObj := NIL;
- LineCodeObj := ElementList.First;
- while LineCodeObj <> NIL do
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ if FNPhases <> Other.FNphases then
begin
+ FNphases := Other.FNphases;
- if CompareText(LineCodeObj.Name, Value) = 0 then
- begin
- DSS.ActiveLineCodeObj := LineCodeObj;
- Exit;
- end;
-
- LineCodeObj := ElementList.Next;
+ if Z <> NIL then
+ Z.Free;
+ if Zinv <> NIL then
+ Zinv.Free;
+ if Yc <> NIL then
+ Yc.Free;
+
+ Z := TCmatrix.CreateMatrix(FNphases);
+ Zinv := TCMatrix.CreateMatrix(FNphases);
+ Yc := TCMatrix.CreateMatrix(FNphases);
end;
- DoSimpleMsg('Linecode: "' + Value + '" not Found.', 103);
-
+ Z.CopyFrom(Other.Z);
+ Zinv.CopyFrom(Other.Zinv);
+ Yc.CopyFrom(Other.Yc);
+ BaseFrequency := Other.BaseFrequency;
+ R1 := Other.R1;
+ X1 := Other.X1;
+ R0 := Other.R0;
+ X0 := Other.X0;
+ C1 := Other.C1;
+ C0 := Other.C0;
+ Rg := Other.Rg;
+ Xg := Other.Xg;
+ rho := Other.rho;
+ FNeutralConductor := Other.FNeutralConductor;
+ NormAmps := Other.NormAmps;
+ EmergAmps := Other.EmergAmps;
+ FaultRate := Other.FaultRate;
+ PctPerm := Other.PctPerm;
+ HrsToRepair := Other.HrsToRepair;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TLineCode Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TLineCodeObj.Create(ParClass: TDSSClass; const LineCodeName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(LineCodeName);
+ Name := AnsiLowerCase(LineCodeName);
DSSObjType := ParClass.DSSClassType;
FNPhases := 3; // Directly set conds and phases
@@ -658,17 +395,13 @@ constructor TLineCodeObj.Create(ParClass: TDSSClass; const LineCodeName: String)
rho := 100.0;
SymComponentsModel := TRUE;
- ReduceByKron := FALSE;
CalcMatricesFromZ1Z0; // put some reasonable values in
NumAmpRatings := 1;
setlength(AmpRatings, NumAmpRatings);
AmpRatings[0] := NormAmps;
-
- InitPropertyValues(0);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TLineCodeObj.Destroy;
begin
Z.Free;
@@ -678,11 +411,9 @@ destructor TLineCodeObj.Destroy;
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLineCodeObj.Set_NPhases(Value: Integer);
+procedure TLineCodeObj.Set_NumPhases(Value: Integer);
// Set the number of phases and reallocate phase-sensitive arrays
// Need to preserve values in Z matrices
-
begin
if Value > 0 then
begin
@@ -690,19 +421,17 @@ procedure TLineCodeObj.Set_NPhases(Value: Integer);
begin // If size is no different, we don't need to do anything
FNPhases := Value;
FNeutralConductor := FNphases; // Init to last conductor
- // Put some reasonable values in these matrices
+ // Put some reasonable values in these matrices
CalcMatricesFromZ1Z0; // reallocs matrices
end;
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TLineCodeObj.CalcMatricesFromZ1Z0;
var
Zs, Zm, Ys, Ym, Ztemp: Complex;
i, j: Integer;
Yc1, Yc0, OneThird: Double;
-
begin
if Z <> NIL then
Z.Free;
@@ -718,15 +447,15 @@ procedure TLineCodeObj.CalcMatricesFromZ1Z0;
OneThird := 1.0 / 3.0; // Do this to get more precision in next few statements
- Ztemp := CmulReal(cmplx(R1, X1), 2.0);
- Zs := CmulReal(CAdd(Ztemp, Cmplx(R0, X0)), OneThird);
- Zm := CmulReal(Csub(cmplx(R0, X0), Cmplx(R1, X1)), OneThird);
+ Ztemp := cmplx(R1, X1) * 2;
+ Zs := (Ztemp + Cmplx(R0, X0)) * OneThird;
+ Zm := (cmplx(R0, X0) - Cmplx(R1, X1)) * OneThird;
Yc1 := TwoPi * BaseFrequency * C1;
Yc0 := TwoPi * BaseFrequency * C0;
- Ys := CMulReal(Cadd(CMulReal(Cmplx(0.0, Yc1), 2.0), Cmplx(0.0, Yc0)), OneThird);
- Ym := CmulReal(Csub(cmplx(0.0, Yc0), Cmplx(0.0, Yc1)), OneThird);
+ Ys := (Cmplx(0.0, Yc1) * 2 + Cmplx(0.0, Yc0)) * OneThird;
+ Ym := (cmplx(0.0, Yc0) - Cmplx(0.0, Yc1)) * OneThird;
for i := 1 to FNphases do
begin
@@ -742,19 +471,16 @@ procedure TLineCodeObj.CalcMatricesFromZ1Z0;
Zinv.Invert;
end;
-procedure TLineCodeObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
+procedure TLineCodeObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
k,
i, j: Integer;
TempStr: String;
-
begin
inherited DumpProperties(F, Complete);
with ParentClass do
begin
-
FSWriteln(F, Format('~ %s=%d', [PropertyName^[1], FNphases]));
FSWriteln(F, Format('~ %s=%.5f', [PropertyName^[2], R1]));
FSWriteln(F, Format('~ %s=%.5f', [PropertyName^[3], X1]));
@@ -808,141 +534,17 @@ procedure TLineCodeObj.DumpProperties(F: TFileStream; Complete: Boolean);
TempStr := TempStr + ']';
FSWriteln(F, Format('~ %s=%s', [PropertyName^[26]]) + TempStr);
-
// TODO: check missing linetype here
end;
-
-end;
-
-function TLineCodeObj.GetPropertyValue(Index: Integer): String;
-var
- j: Integer;
-begin
- case Index of
- 1:
- Result := Format('%d', [FnPhases]);
- 2:
- if SymComponentsModel then
- Result := Format('%.5g', [R1])
- else
- Result := '----';
- 3:
- if SymComponentsModel then
- Result := Format('%.5g', [X1])
- else
- Result := '----';
- 4:
- if SymComponentsModel then
- Result := Format('%.5g', [R0])
- else
- Result := '----';
- 5:
- if SymComponentsModel then
- Result := Format('%.5g', [X0])
- else
- Result := '----';
- 6:
- if SymComponentsModel then
- Result := Format('%.5g', [C1 * 1.0e9])
- else
- Result := '----';
- 7:
- if SymComponentsModel then
- Result := Format('%.5g', [C0 * 1.0e9])
- else
- Result := '----';
- 8:
- Result := LineUnitsStr(Units);
- 9:
- Result := Get_Rmatrix;
- 10:
- Result := Get_Xmatrix;
- 11:
- Result := get_Cmatrix;
- 12:
- Result := Format('%.g', [Basefrequency]); // was defaultbasefrequency ??? 'baseFreq';
- 18:
- if ReduceByKron then
- Result := 'Y'
- else
- Result := 'N';
- 19:
- Result := Format('%.5g', [Rg]);
- 20:
- Result := Format('%.5g', [Xg]);
- 21:
- Result := Format('%.5g', [Rho]);
- 22:
- Result := IntToStr(FNeutralConductor);
- 23:
- if SymComponentsModel then
- Result := Format('%.5g', [twopi * Basefrequency * C1 * 1.0e6])
- else
- Result := '----';
- 24:
- if SymComponentsModel then
- Result := Format('%.5g', [twopi * Basefrequency * C0 * 1.0e6])
- else
- Result := '----';
- 25:
- Result := inttostr(NumAmpRatings);
- 26:
- begin
- Result := '[';
- for j := 1 to NumAmpRatings do
- Result := Result + floattoStrf(AmpRatings[j - 1], ffgeneral, 8, 4) + ',';
- Result := Result + ']';
- end;
- 27:
- if (FLineType >= 1) and (FLineType <= (High(LINE_TYPES) + 1)) then
- Result := LINE_TYPES[FLineType - 1]
- else
- Result := '';
- else
- Result := inherited GetPropertyValue(index);
- end;
-end;
-
-procedure TLineCodeObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := '3'; // 'nphases';
- PropertyValue[2] := '.058'; // 'r1';
- PropertyValue[3] := '.1206'; // 'x1';
- PropertyValue[4] := '0.1784'; // 'r0';
- PropertyValue[5] := '0.4047'; // 'x0';
- PropertyValue[6] := '3.4'; // 'c1';
- PropertyValue[7] := '1.6'; // 'c0';
- PropertyValue[8] := 'none'; // 'units';
- PropertyValue[9] := ''; // 'rmatrix';
- PropertyValue[10] := ''; // 'xmatrix';
- PropertyValue[11] := ''; // 'cmatrix';
- PropertyValue[12] := Format('%6.1f', [DSS.DefaultBaseFreq]); // 'baseFreq';
- PropertyValue[13] := '400'; // 'normamps';
- PropertyValue[14] := '600'; // 'emergamps';
- PropertyValue[15] := '0.1'; // 'faultrate';
- PropertyValue[16] := '20'; // 'pctperm';
- PropertyValue[17] := '3'; // 'Hrs to repair';
- PropertyValue[18] := 'N'; // 'Kron';
- PropertyValue[19] := '.01805'; // 'Rg';
- PropertyValue[20] := '.155081'; // 'Xg';
- PropertyValue[21] := '100'; // 'rho';
- PropertyValue[22] := '3'; // 'Neutral';
- PropertyValue[23] := '1.2818'; // B1 microS
- PropertyValue[24] := '0.60319'; // B0 microS
- PropertyValue[25] := '1'; // 1 season
- PropertyValue[26] := '[400]'; // 1 rating
- PropertyValue[27] := 'OH';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
end;
procedure TLineCodeObj.DoKronReduction;
var
NewZ, NewYC: TcMatrix;
-
begin
+ if SymComponentsModel then
+ Exit;
+
if FneutralConductor = 0 then
Exit; // Do Nothing
@@ -953,19 +555,18 @@ procedure TLineCodeObj.DoKronReduction;
begin
try
NewZ := Z.Kron(FNeutralConductor); // Perform Kron Reductions into temp space
- { Have to invert the Y matrix to eliminate properly}
+ // Have to invert the Y matrix to eliminate properly
YC.Invert; // Vn = 0 not In
NewYC := YC.Kron(FNeutralConductor);
except
On E: Exception do
- DoSimpleMsg(Format('Kron Reduction failed: LineCode.%s. Attempting to eliminate Neutral Conductor %d.', [Name, FNeutralConductor]), 103);
+ DoSimpleMsg('Kron Reduction failed: %s. Attempting to eliminate Neutral Conductor %d.', [FullName, FNeutralConductor], 103);
end;
// Reallocate into smaller space if Kron was successful
if (NewZ <> NIL) and (NewYC <> NIL) then
begin
-
NewYC.Invert; // Back to Y
Numphases := NewZ.order;
@@ -978,28 +579,22 @@ procedure TLineCodeObj.DoKronReduction;
YC := NewYC;
FNeutralConductor := 0;
- ReduceByKron := FALSE;
-
- {Change Property values to reflect Kron reduction for save circuit function}
- PropertyValue[1] := Format('%d', [FnPhases]);
- PropertyValue[9] := get_Rmatrix;
- PropertyValue[10] := get_Xmatrix;
- PropertyValue[11] := get_Cmatrix;
+ // Change Property values to reflect Kron reduction for save circuit function
+ SetAsNextSeq(ord(TProp.nphases));
+ SetAsNextSeq(ord(TProp.rmatrix));
+ SetAsNextSeq(ord(TProp.xmatrix));
+ SetAsNextSeq(ord(TProp.cmatrix));
end
else
begin
- DoSimpleMsg(Format('Kron Reduction failed: LineCode.%s. Attempting to eliminate Neutral Conductor %d.', [Name, FNeutralConductor]), 103);
+ DoSimpleMsg('Kron Reduction failed: %s. Attempting to eliminate Neutral Conductor %d.', [FullName, FNeutralConductor], 103);
end;
-
end
else
begin
- DoSimpleMsg('Cannot perform Kron Reduction on a 1-phase LineCode: LineCode.' + Name, 103);
+ DoSimpleMsg('Cannot perform Kron Reduction on a 1-phase LineCode: %s', [FullName], 103);
end;
- ;
-
end;
-
-end.
+end.
\ No newline at end of file
diff --git a/src/General/LineConstants.pas b/src/General/LineConstants.pas
index 4dff5a481..8e2759f48 100644
--- a/src/General/LineConstants.pas
+++ b/src/General/LineConstants.pas
@@ -7,44 +7,44 @@
----------------------------------------------------------
}
-{Manages the geometry data and calculates the impedance matrices for an overhead line}
-
-{Usage: Create with Number of conductors you want
- Specify the number of phases. The first conductors you define with
- be the phases. Other conductors may be considered neutral.
-
- Uses GMR for power frequency calcs so that answers match published
- data.
-
- You only have to set R or GMR. The other will default. However, you should set
- both for better accuracy.
-
- When you ask for Zmatrix or YCmatrix you get the full matrix unless you have executed
- a Kron reduction or Reduce function. Reduce eleminates all non phases. If you
- want the full detailed model, DO NOT REDUCE!
-
-}
+// Manages the geometry data and calculates the impedance matrices for an overhead line
+
+// Usage: Create with Number of conductors you want
+// Specify the number of phases. The first conductors you define with
+// be the phases. Other conductors may be considered neutral.
+//
+// Uses GMR for power frequency calcs so that answers match published
+// data.
+//
+// You only have to set R or GMR. The other will default. However, you should set
+// both for better accuracy.
+//
+// When you ask for Zmatrix or YCmatrix you get the full matrix unless you have executed
+// a Kron reduction or Reduce function. Reduce eleminates all non phases. If you
+// want the full detailed model, DO NOT REDUCE!
interface
uses
Arraydef,
Ucmatrix,
- Ucomplex,
+ UComplex, DSSUcomplex,
LineUnits;
type
-{This class returns a matrix ordered by phases first then remaining conductors
- Assumes phases are defined first}
+ // This class returns a matrix ordered by phases first then remaining conductors
+ // Assumes phases are defined first
TLineConstants = class(TObject)
- PRIVATE
-
PROTECTED
-
FNumConds: Integer;
- FNumPhases: Integer;
+ FNPhases: Integer;
+
+ FData: pDouble;
+
+ // Memory for the arrays below is shared in FData above;
+
FX: pDoubleArray;
FY: pDoubleArray;
FRdc: pDoubleArray; // ohms/m
@@ -89,7 +89,7 @@ TLineConstants = class(TObject)
function Get_Capradius(i, units: Integer): Double;
procedure Set_Capradius(i, units: Integer; const Value: Double);
- {These can only be called privately}
+ // These can only be called privately
property Frequency: Double READ FFrequency WRITE Set_Frequency;
procedure set_Nphases(const Value: Integer);
@@ -112,12 +112,12 @@ TLineConstants = class(TObject)
property Ze[i, j, EarthModel: Integer]: Complex READ Get_Ze; // Earth return impedance at present frequency for ij element
property rhoearth: Double READ Frhoearth WRITE Set_Frhoearth;
- {These two properties will auto recalc the impedance matrices if frequency is different}
- {Converts to desired units when executed; Returns Pointer to Working Verstion}
+ // These two properties will auto recalc the impedance matrices if frequency is different
+ // Converts to desired units when executed; Returns Pointer to Working Verstion
property Zmatrix[f, Lngth: Double; Units, EarthModel: Integer]: Tcmatrix READ Get_Zmatrix;
property YCmatrix[f, Lngth: Double; Units: Integer]: Tcmatrix READ Get_YCmatrix;
- property Nphases: Integer READ FNumPhases WRITE set_Nphases;
+ property Nphases: Integer READ FNPhases WRITE set_Nphases;
property Nconductors: Integer READ FNumConds;
constructor Create(NumConductors: Integer);
@@ -140,15 +140,20 @@ implementation
DSSClass,
DSSHelper;
-var
- C1_j1: Complex;
- b1, b2, b3, b4, d2, d4, c2, c4: Double;
-
+const
+ C1_j1: Complex = (re: 1.0; im: 1.0);
+ b1: Double = 1.0 / (3.0 * sqrt(2.0));
+ b2: Double = 1.0 / 16.0;
+ b3: Double = {b1}(1.0 / (3.0 * sqrt(2.0))) / 3.0 / 5.0;
+ b4: Double = {b2}(1.0 / 16.0) / 4.0 / 6.0;
+ d2: Double = {b2}(1.0 / 16.0) * pi / 4.0;
+ d4: Double = {b4}((1.0 / 16.0) / 4.0 / 6.0) * pi / 4.0;
+ c2: Double = 1.3659315;
+ c4: Double = {c2}(1.3659315) + 1.0 / 4.0 + 1.0 / 6.0;
-{ TLineConstants }
procedure TLineConstants.Calc(f: Double; EarthModel: Integer);
-{Compute base Z and YC matrices in ohms/m for this frequency and earth impedance}
+// Compute base Z and YC matrices in ohms/m for this frequency and earth impedance
var
Zi, Zspacing: Complex;
PowerFreq: Boolean;
@@ -158,8 +163,7 @@ procedure TLineConstants.Calc(f: Double; EarthModel: Integer);
ReducedSize: Integer;
begin
-
- // RhoEarth := rho;
+ // RhoEarth := rho;
Frequency := f; // this has side effects
if assigned(FZreduced) then
@@ -177,7 +181,7 @@ procedure TLineConstants.Calc(f: Double; EarthModel: Integer);
FZmatrix.Clear;
FYCMatrix.Clear;
- {For less than 1 kHz use GMR to better match published data}
+ // For less than 1 kHz use GMR to better match published data
LFactor := Cmplx(0.0, Fw * mu0 / twopi);
if (f < 1000.0) and (f > 40.0) then
@@ -185,7 +189,7 @@ procedure TLineConstants.Calc(f: Double; EarthModel: Integer);
else
PowerFreq := FALSE;
- {Self Impedances}
+ // Self Impedances
for i := 1 to FNumConds do
begin
@@ -193,36 +197,36 @@ procedure TLineConstants.Calc(f: Double; EarthModel: Integer);
if PowerFreq then
begin // for less than 1 kHz, use published GMR
Zi.im := 0.0;
- Zspacing := CmulReal(Lfactor, ln(1.0 / FGMR^[i])); // use GMR
+ Zspacing := Lfactor * ln(1.0 / FGMR^[i]); // use GMR
end
else
begin
- Zspacing := CmulReal(Lfactor, ln(1.0 / Fradius^[i]));
+ Zspacing := Lfactor * ln(1.0 / Fradius^[i]);
end;
- FZmatrix.SetElement(i, i, Cadd(Zi, Cadd(Zspacing, Get_Ze(i, i, EarthModel))));
+ FZmatrix.SetElement(i, i, Zi + Zspacing + Get_Ze(i, i, EarthModel));
end;
- {Mutual IMpedances}
+ // Mutual IMpedances
for i := 1 to FNumConds do
begin
for j := 1 to i - 1 do
begin
Dij := sqrt(sqr(Fx^[i] - Fx^[j]) + sqr(Fy^[i] - Fy^[j]));
- FZmatrix.SetElemSym(i, j, Cadd(Cmulreal(Lfactor, ln(1.0 / Dij)), Get_Ze(i, j, EarthModel)));
+ FZmatrix.SetElemSym(i, j, Lfactor * ln(1.0 / Dij) + Get_Ze(i, j, EarthModel));
end;
end;
- {Capacitance Matrix}
+ // Capacitance Matrix
Pfactor := -1.0 / twopi / e0 / Fw; // include frequency
- {Construct P matrix and then invert}
+ // Construct P matrix and then invert
- {Self uses capradius, which defaults to actual conductor radius. But
- in case of bundled conductors can be specified different in Wiredata.}
+ // Self uses capradius, which defaults to actual conductor radius. But
+ // in case of bundled conductors can be specified different in Wiredata.
for i := 1 to FnumConds do
begin
@@ -247,10 +251,9 @@ procedure TLineConstants.Calc(f: Double; EarthModel: Integer);
if ReducedSize > 0 then
Kron(ReducedSize); // Was reduced so reduce again to same size
- {Else the Zmatrix is OK as last computed}
+ // Else the Zmatrix is OK as last computed
FRhoChanged := FALSE;
-
end;
function TLineConstants.ConductorsInSameSpace(var ErrorMessage: String): Boolean;
@@ -258,10 +261,10 @@ function TLineConstants.ConductorsInSameSpace(var ErrorMessage: String): Boolean
i, j: Integer;
Dij: Double;
begin
-{Check all conductors to make sure none occupy the same space or are defined at 0,0}
+ // Check all conductors to make sure none occupy the same space or are defined at 0,0
Result := FALSE;
- {Check for 0 Y coordinate}
+ // Check for 0 Y coordinate
for i := 1 to FNumConds do
begin
if (FY^[i] <= 0.0) then
@@ -272,7 +275,7 @@ function TLineConstants.ConductorsInSameSpace(var ErrorMessage: String): Boolean
end;
end;
- {Check for overlapping conductors}
+ // Check for overlapping conductors
for i := 1 to FNumConds do
begin
for j := i + 1 to FNumConds do
@@ -293,19 +296,22 @@ constructor TLineConstants.Create(NumConductors: Integer);
var
i: Integer;
begin
-
FNumConds := NumConductors;
NPhases := FNumConds;
- FX := Allocmem(Sizeof(FX^[1]) * FNumConds);
- FY := Allocmem(Sizeof(Fy^[1]) * FNumConds);
- FGMR := Allocmem(Sizeof(FGMR^[1]) * FNumConds);
- Fradius := Allocmem(Sizeof(Fradius^[1]) * FNumConds);
- Fcapradius := Allocmem(Sizeof(Fcapradius^[1])*FNumConds);
- FRdc := Allocmem(Sizeof(FRdc^[1]) * FNumConds);
- FRac := Allocmem(Sizeof(FRac^[1]) * FNumConds);
+ // Data for FX, FY, FGMR, Fradius, Fcapradius, FRdc, FRac,
+ // FZMatrix, FYCMatrix
+ FData := Allocmem(Sizeof(Double) * (FNumConds * 7 + FNumconds * FNumconds * (2 * 2)));
+
+ FX := pDoubleArray(FData);
+ FY := pDoubleArray(FData + FNumConds);
+ FGMR := pDoubleArray(FData + FNumConds * 2);
+ Fradius := pDoubleArray(FData + FNumConds * 3);
+ Fcapradius := pDoubleArray(FData + FNumConds * 4);
+ FRdc := pDoubleArray(FData + FNumConds * 5);
+ FRac := pDoubleArray(FData + FNumConds * 6);
- {Initialize to not set}
+ // Initialize to not set
for i := 1 to FNumConds do
FGMR^[i] := -1.0;
for i := 1 to FNumConds do
@@ -315,8 +321,8 @@ constructor TLineConstants.Create(NumConductors: Integer);
for i := 1 to FNumConds do
FRdc^[i] := -1.0;
- FZMatrix := TCMatrix.CreateMatrix(FNumconds);
- FYCMatrix := TCMatrix.CreateMatrix(FNumconds);
+ FZMatrix := TCMatrix.CreateMatrixInplace(FNumconds, pComplex(FData + FNumConds * 7));
+ FYCMatrix := TCMatrix.CreateMatrixInPlace(FNumconds, pComplex(FData + FNumConds * 7 + FNumconds * FNumconds * 2));
FFrequency := -1.0; // not computed
Frhoearth := 100.0; // default value
@@ -324,12 +330,10 @@ constructor TLineConstants.Create(NumConductors: Integer);
FZreduced := NIL;
FYCreduced := NIL;
-
end;
destructor TLineConstants.Destroy;
begin
-
if assigned(FZmatrix) then
FZmatrix.Free;
if assigned(FYCmatrix) then
@@ -339,16 +343,9 @@ destructor TLineConstants.Destroy;
if assigned(FYCreduced) then
FYCreduced.Free;
- Reallocmem(FX, 0);
- Reallocmem(FY, 0);
- Reallocmem(FGMR, 0);
- Reallocmem(Fradius, 0);
- Reallocmem(Fcapradius, 0);
- Reallocmem(FRdc, 0);
- Reallocmem(FRac, 0);
+ Reallocmem(FData, 0);
inherited;
-
end;
function TLineConstants.Get_Capradius(i, units: Integer): Double;
@@ -388,24 +385,18 @@ function TLineConstants.Get_Y(i, units: Integer): Double;
function TLineConstants.Get_YCmatrix(f, Lngth: Double;
Units: Integer): Tcmatrix;
-{Makes a new YCmatrix and correct for lengths and units as it copies}
-{Uses the reduced Zmatrix by default if it exists}
-
+// Makes a new YCmatrix and correct for lengths and units as it copies
+// Uses the reduced Zmatrix by default if it exists
var
Newsize, i: Integer;
UnitLengthConversion: Double;
YC: TCMatrix;
YCValues: pComplexArray;
-
begin
if assigned(FYCreduced) then
- begin
- YC := FYCReduced;
- end
+ YC := FYCReduced
else
- begin
YC := FYCmatrix;
- end;
NewSize := YC.order;
Result := TCmatrix.CreateMatrix(Newsize);
@@ -414,8 +405,7 @@ function TLineConstants.Get_YCmatrix(f, Lngth: Double;
YCvalues := Result.GetValuesArrayPtr(Newsize);
UnitLengthConversion := From_per_meter(Units) * lngth;
for i := 1 to NewSize * NewSize do
- CmulRealAccum(YCValues^[i], UnitLengthConversion);
-
+ YCValues^[i] *= UnitLengthConversion;
end;
function TLineConstants.Get_Ze(i, j, EarthModel: Integer): Complex;
@@ -424,7 +414,6 @@ function TLineConstants.Get_Ze(i, j, EarthModel: Integer): Complex;
mij, thetaij, Dij, Fyi, Fyj: Double;
term1, term2, term3, term4, term5: Double;
begin
-
Fyi := Abs(Fy^[i]);
Fyj := Abs(Fy^[j]);
@@ -438,7 +427,7 @@ function TLineConstants.Get_Ze(i, j, EarthModel: Integer): Complex;
FULLCARSON:
begin
- {notation from Tleis book Power System Modelling and Fault Analysis}
+ // notation from Tleis book Power System Modelling and Fault Analysis
if i = j then
begin
thetaij := 0.0;
@@ -461,7 +450,7 @@ function TLineConstants.Get_Ze(i, j, EarthModel: Integer): Complex;
Result.im := term1 + term2 + term3 + term4 + term5;
Result.im := Result.im + 0.5 * ln(Dij); // correction term to work with DSS structure
- Result := CmulReal(Result, Fw * Mu0 / pi);
+ Result := Result * (Fw * Mu0 / pi);
// {****} WriteDLLDebugFile(Format('Full: Z(%d,%d) = %.8g +j %.8g; Dij=%.8g, thetaij=%.8g, mij=%.8g, Terms= %.8g, %.8g, %.8g, %.8g, %.8g',[i,j, Result.re, result.im, Dij, thetaij*180.0/pi, mij, term1, term2, term3, term4, term5]));
@@ -471,15 +460,15 @@ function TLineConstants.Get_Ze(i, j, EarthModel: Integer): Complex;
begin
if i <> j then
begin
- hterm := Cadd(cmplx(Fyi + Fyj, 0.0), CmulReal(Cinv(Fme), 2.0));
+ hterm := cmplx(Fyi + Fyj, 0.0) + Cinv(Fme) * 2.0;
xterm := cmplx(Fx^[i] - Fx^[j], 0.0);
- LnArg := Csqrt(Cadd(Cmul(hterm, hterm), cmul(xterm, xterm)));
- Result := Cmul(Cmplx(0.0, Fw * Mu0 / twopi), Cln(lnArg));
+ LnArg := Csqrt(hterm * hterm + xterm * xterm);
+ Result := Cmplx(0.0, Fw * Mu0 / twopi) * Cln(lnArg);
end
else
begin
- hterm := Cadd(cmplx(Fyi, 0.0), Cinv(Fme));
- Result := Cmul(Cmplx(0.0, Fw * Mu0 / twopi), Cln(CmulReal(hterm, 2.0)));
+ hterm := cmplx(Fyi, 0.0) + Cinv(Fme);
+ Result := Cmplx(0.0, Fw * Mu0 / twopi) * Cln(hterm * 2.0);
end;
// {****} WriteDLLDebugFile(Format('Deri: Z(%d,%d) = %.8g +j %.8g; hterm= %.8g + j %.8g',[i,j, Result.re, result.im, hterm.re, hterm.im]));
end;
@@ -490,93 +479,78 @@ function TLineConstants.Get_Zint(i, EarthModel: Integer): Complex;
var
Alpha, I0I1: Complex;
begin
-
case EarthModel of
SIMPLECARSON:
begin
Result := cmplx(FRac^[i], Fw * Mu0 / (8 * pi));
end;
FULLCARSON:
- begin // no skin effect
+ begin // no skin effect
Result := cmplx(FRac^[i], Fw * Mu0 / (8 * pi));
end;
DERI:
- begin // with skin effect model
-
- {Assume round conductor}
- Alpha := CmulReal(c1_j1, sqrt(FFrequency * mu0 / FRDC^[i]));
+ begin // with skin effect model
+ // Assume round conductor
+ Alpha := c1_j1 * sqrt(FFrequency * mu0 / FRDC^[i]);
if Cabs(Alpha) > 35.0 then
I0I1 := CONE
else
- I0I1 := CDiv(Bessel_I0(Alpha), Bessel_I1(Alpha));
+ I0I1 := Bessel_I0(Alpha) / Bessel_I1(Alpha);
- Result := CmulReal(Cmul(C1_j1, I0I1), Sqrt(FRdc^[i] * FFrequency * mu0) / 2.0);
+ Result := C1_j1 * I0I1 * (Sqrt(FRdc^[i] * FFrequency * mu0) / 2.0);
end;
end;
end;
function TLineConstants.Get_Zmatrix(f, Lngth: Double;
Units, EarthModel: Integer): Tcmatrix;
-
-{Makes a new Zmatrix and correct for lengths and units as it copies}
-{Uses the reduced Zmatrix by default if it exists}
-
+// Makes a new Zmatrix and correct for lengths and units as it copies
+// Uses the reduced Zmatrix by default if it exists
var
Newsize, i: Integer;
UnitLengthConversion: Double;
Z: TCMatrix;
ZValues: pComplexArray;
-
begin
-
if (F <> FFrequency) or FRhoChanged then
Calc(f, EarthModel); // only recalcs if f changed or rho earth changed
if assigned(FZreduced) then
- begin
- Z := FZReduced;
- end
+ Z := FZReduced
else
- begin
Z := FZmatrix;
- end;
NewSize := Z.order;
Result := TCmatrix.CreateMatrix(Newsize);
Result.CopyFrom(Z); // gets ohms/meter
Zvalues := Result.GetValuesArrayPtr(Newsize); // ptr to the values in the new copy
- {Convert the values by units and length}
+ // Convert the values by units and length
UnitLengthConversion := From_per_meter(Units) * lngth;
for i := 1 to NewSize * NewSize do
- CmulRealAccum(ZValues^[i], UnitLengthConversion);
-
+ ZValues^[i] *= UnitLengthConversion;
end;
procedure TLineConstants.Kron(Norder: Integer);
-
var
Ztemp: TCmatrix;
FirstTime: Boolean;
i, j: Integer;
begin
-
Ztemp := FZMatrix;
FirstTime := TRUE;
if (FFrequency >= 0.0) and (Norder > 0) and (Norder < FnumConds) then
begin
-
if Assigned(FZreduced) then
FZreduced.Free;
if Assigned(FYCreduced) then
FYCReduced.Free;
- {Reduce computed matrix one row/col at a time until it is norder}
+ // Reduce computed matrix one row/col at a time until it is norder
while Ztemp.Order > Norder do
begin
-
FZReduced := Ztemp.Kron(ZTemp.Order); // Eliminate last row
if not FirstTime then
@@ -587,26 +561,20 @@ procedure TLineConstants.Kron(Norder: Integer);
FirstTime := FALSE;
end;
- {Extract norder x norder portion of Yc matrx}
+ // Extract norder x norder portion of Yc matrx
FYCreduced := TCmatrix.CreateMatrix(Norder);
for i := 1 to Norder do
for j := 1 to Norder do
FYCreduced.SetElement(i, j, FYCmatrix.GetElement(i, j));
- {Left with reduced matrix}
-
+ // Left with reduced matrix
end;
-
end;
procedure TLineConstants.Reduce;
-
-{Performs a Kron reduction to get rid of neutral conductors}
-
+// Performs a Kron reduction to get rid of neutral conductors
begin
-
- Kron(FNumPhases);
-
+ Kron(FNPhases);
end;
procedure TLineConstants.Set_Capradius(i, units: Integer; const Value: Double);
@@ -643,7 +611,7 @@ procedure TLineConstants.Set_GMR(i, units: Integer; const Value: Double);
procedure TLineConstants.set_Nphases(const Value: Integer);
begin
- FNumPhases := Value;
+ FNPhases := Value;
end;
procedure TLineConstants.Set_Rac(i, units: Integer; const Value: Double);
@@ -681,18 +649,4 @@ procedure TLineConstants.Set_Y(i, units: Integer; const Value: Double);
FY^[i] := Value * To_Meters(units);
end;
-initialization
-
- C1_j1 := Cmplx(1.0, 1.0);
-
- b1 := 1.0 / (3.0 * sqrt(2.0));
- b2 := 1.0 / 16.0;
- b3 := b1 / 3.0 / 5.0;
- b4 := b2 / 4.0 / 6.0;
- d2 := b2 * pi / 4.0;
- d4 := b4 * pi / 4.0;
- c2 := 1.3659315;
- c4 := c2 + 1.0 / 4.0 + 1.0 / 6.0;
-
-
end.
diff --git a/src/General/LineGeometry.pas b/src/General/LineGeometry.pas
index 4cc21f692..9ed37f404 100644
--- a/src/General/LineGeometry.pas
+++ b/src/General/LineGeometry.pas
@@ -9,17 +9,15 @@
interface
-{The LineGeometry object is a general DSS object used by all circuits
- as a reference for obtaining line impedances.
-
- The values are set by the normal New and Edit procedures for any DSS object.
-
- The values are retrieved by setting the Code Property in the LineGeometry Class.
- This sets the active LineGeometry object to be the one referenced by the Code Property;
-
- Then the values of that code can be retrieved via the public variables.
-
- }
+// The LineGeometry object is a general DSS object used by all circuits
+// as a reference for obtaining line impedances.
+//
+// The values are set by the normal New and Edit procedures for any DSS object.
+//
+// The values are retrieved by setting the Code Property in the LineGeometry Class.
+// This sets the active LineGeometry object to be the one referenced by the Code Property;
+//
+// Then the values of that code can be retrieved via the public variables.
uses
Classes,
@@ -36,56 +34,65 @@ interface
LineSpacing;
type
+{$SCOPEDENUMS ON}
+ TLineGeometryProp = (
+ INVALID = 0,
+ nconds = 1,
+ nphases = 2,
+ cond = 3,
+ wire = 4,
+ x = 5,
+ h = 6,
+ units = 7,
+ normamps = 8,
+ emergamps = 9,
+ reduce = 10,
+ spacing = 11,
+ wires = 12,
+ cncable = 13,
+ tscable = 14,
+ cncables = 15,
+ tscables = 16,
+ Seasons = 17,
+ Ratings = 18,
+ LineType = 19
+ );
+{$SCOPEDENUMS OFF}
+
ELineGeometryProblem = class(Exception);
TLineGeometry = class(TDSSClass)
- PRIVATE
-
- function Get_Code: String; // Returns active line code string
- procedure Set_Code(const Value: String); // sets the active LineGeometry
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const LineName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
- LineTypeList: TCommandList;
-
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
-
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
-
- // Set this property to point ActiveLineGeometryObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-
TLineGeometryObj = class(TDSSObject)
-{$IFNDEF DSS_CAPI}
- PRIVATE
-{$ELSE}
PUBLIC
-{$ENDIF}
- FPhaseChoice: pConductorChoiceArray;
+ FPhaseChoice: pConductorChoiceArray; // TODO: remove -- somewhat redundant with FWireData
FNConds: Integer;
FNPhases: Integer;
- FCondName: pStringArray;
FWireData: pConductorDataArray;
FX: pDoubleArray;
FY: pDoubleArray;
FUnits: pIntegerArray;
FLastUnit: Integer;
-{$IFNDEF DSS_CAPI}
DataChanged: Boolean;
- FReduce: Boolean;
-{$ENDIF}
+ FReduce: LongBool;
FActiveCond: Integer;
- FSpacingType: String;
FLineData: TLineConstants;
+
+ NormAmps: Double;
+ EmergAmps: Double;
+ NumAmpRatings: Integer;
+ AmpRatings: array of Double;
+ FLineType: Integer; // Pointer to code for type of line
+ LineSpacingObj: TLineSpacingObj;
+
procedure ChangeLineConstantsType(newPhaseChoice: ConductorChoice);
procedure set_Nconds(const Value: Integer);
@@ -104,31 +111,19 @@ TLineGeometryObj = class(TDSSObject)
function Get_FUnits(i: Integer): Integer;
function Get_ConductorName(i: Integer): String;
function Get_ConductorData(i: Integer): TConductorDataObj;
-{$IFDEF DSS_CAPI}
+ //TODO: remove
procedure Set_FX(i: Integer; Value: Double);
procedure Set_FY(i: Integer; Value: Double);
procedure Set_FUnits(i: Integer; Value: Integer);
-{$ENDIF}
- function Get_PhaseChoice(i: Integer): ConductorChoice;
-
- PUBLIC
-{$IFDEF DSS_CAPI}
- DataChanged: Boolean;
- FReduce: Boolean;
-{$ENDIF}
- NormAmps: Double;
- EmergAmps: Double;
- NumAmpRatings: Integer;
- AmpRatings: array of Double;
- FLineType: Integer; // Pointer to code for type of line
+ function Get_PhaseChoice(i: Integer): ConductorChoice;
constructor Create(ParClass: TDSSClass; const LineGeometryName: String);
destructor Destroy; OVERRIDE;
-
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
+
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
procedure SaveWrite(F: TFileStream); OVERRIDE;
// called from a Line object that has its own Spacing and Wires input
@@ -143,21 +138,9 @@ TLineGeometryObj = class(TDSSObject)
property RhoEarth: Double READ Get_RhoEarth WRITE Set_RhoEarth;
// CIM XML accessors
- property Xcoord[i: Integer]: Double READ Get_FX
-{$IFDEF DSS_CAPI}
- WRITE Set_FX
-{$ENDIF}
- ;
- property Ycoord[i: Integer]: Double READ Get_FY
-{$IFDEF DSS_CAPI}
- WRITE Set_FY
-{$ENDIF}
- ;
- property Units[i: Integer]: Integer READ Get_FUnits
-{$IFDEF DSS_CAPI}
- WRITE Set_FUnits
-{$ENDIF}
- ;
+ property Xcoord[i: Integer]: Double READ Get_FX WRITE Set_FX;
+ property Ycoord[i: Integer]: Double READ Get_FY WRITE Set_FY;
+ property Units[i: Integer]: Integer READ Get_FUnits WRITE Set_FUnits;
property ConductorName[i: Integer]: String READ Get_ConductorName;
property ConductorData[i: Integer]: TConductorDataObj READ Get_ConductorData;
property NWires: Integer READ FNConds;
@@ -167,10 +150,9 @@ TLineGeometryObj = class(TDSSObject)
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
- Ucomplex,
+ UComplex, DSSUcomplex,
Utilities,
LineUnits,
OHLineConstants,
@@ -181,468 +163,379 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TLineGeometryObj;
+ TProp = TLineGeometryProp;
const
- NumPropsThisClass = 19;
-
- LINE_TYPES: Array of String = [
- 'oh', 'ug', 'ug_ts', 'ug_cn', 'swt_ldbrk', 'swt_fuse',
- 'swt_sect', 'swt_rec', 'swt_disc', 'swt_brk', 'swt_elbow'
- ];
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TLineGeometry.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TLineGeometry.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'LineGeometry';
- DSSClassType := DSS_OBJECT;
- ActiveElement := 0;
-
- DefineProperties;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
-
- LineTypeList := TCommandList.Create(LINE_TYPES);
- LineTypeList.Abbrev := TRUE; // Allow abbreviations for line type code
+ inherited Create(dssContext, DSS_OBJECT, 'LineGeometry');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TLineGeometry.Destroy;
-
begin
- LineTypeList.Free();
-
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLineGeometry.DefineProperties;
+
+procedure SetWires(obj: TObj; Value: TDSSObjectPtr; ValueCount: Integer);
+var
+ i, istart, istop: Integer;
begin
+ with Obj do
+ begin
+ istart := 1;
+ istop := FNConds;
+ if FPhaseChoice[ActiveCond] = Unknown then
+ ChangeLineConstantsType(Overhead)
+ else if FPhaseChoice[ActiveCond] <> Overhead then
+ // these are buried neutral wires
+ // (only when the phase conductors not overhead)
+ istart := FNPhases + 1;
+
+ // Validate number of elements
+ if (istop - istart + 1) <> ValueCount then
+ begin
+ DoSimpleMsg('%s: Unexpected number (%d) of objects; expected %d objects.',
+ [FullName, ValueCount, (istop - istart + 1)], 18102);
+ Exit;
+ end;
+ for i := istart to istop do
+ begin
+ FWireData[i] := TConductorDataObj(Value^);
+ Inc(Value);
+ end;
+ FActiveCond := istop;
+ end;
+end;
+
+procedure TLineGeometry.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
+begin
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- PropertyName[1] := 'nconds';
- PropertyName[2] := 'nphases';
- PropertyName[3] := 'cond';
- PropertyName[4] := 'wire';
- PropertyName[5] := 'x';
- PropertyName[6] := 'h';
- PropertyName[7] := 'units';
- PropertyName[8] := 'normamps';
- PropertyName[9] := 'emergamps';
- PropertyName[10] := 'reduce';
- PropertyName[11] := 'spacing';
- PropertyName[12] := 'wires';
- PropertyName[13] := 'cncable';
- PropertyName[14] := 'tscable';
- PropertyName[15] := 'cncables';
- PropertyName[16] := 'tscables';
- PropertyName[17] := 'Seasons';
- PropertyName[18] := 'Ratings';
- PropertyName[19] := 'LineType';
-
- PropertyHelp[1] := 'Number of conductors in this geometry. Default is 3. Triggers memory allocations. Define first!';
- PropertyHelp[2] := 'Number of phases. Default =3; All other conductors are considered neutrals and might be reduced out.';
- PropertyHelp[3] := 'Set this = number of the conductor you wish to define. Default is 1.';
- PropertyHelp[4] := 'Code from WireData. MUST BE PREVIOUSLY DEFINED. no default.' + CRLF +
- 'Specifies use of Overhead Line parameter calculation,' + CRLF +
- 'Unless Tape Shield cable previously assigned to phases, and this wire is a neutral.';
- PropertyHelp[5] := 'x coordinate.';
- PropertyHelp[6] := 'Height of conductor.';
- PropertyHelp[7] := 'Units for x and h: {mi|kft|km|m|Ft|in|cm } Initial default is "ft", but defaults to last unit defined';
- PropertyHelp[8] := 'Normal ampacity, amperes for the line. Defaults to first conductor if not specified.';
- PropertyHelp[9] := 'Emergency ampacity, amperes. Defaults to first conductor if not specified.';
- PropertyHelp[10] := '{Yes | No} Default = no. Reduce to Nphases (Kron Reduction). Reduce out neutrals.';
- PropertyHelp[11] := 'Reference to a LineSpacing for use in a line constants calculation.' + CRLF +
- 'Alternative to x, h, and units. MUST BE PREVIOUSLY DEFINED.' + CRLF +
- 'Must match "nconds" as previously defined for this geometry.' + CRLF +
- 'Must be used in conjunction with the Wires property.';
- PropertyHelp[12] := 'Array of WireData names for use in a line constants calculation.' + CRLF +
- 'Alternative to individual wire inputs. ALL MUST BE PREVIOUSLY DEFINED.' + CRLF +
- 'Must match "nconds" as previously defined for this geometry,' + CRLF +
- 'unless TSData or CNData were previously assigned to phases, and these wires are neutrals.' + CRLF +
- 'Must be used in conjunction with the Spacing property.';
- PropertyHelp[13] := 'Code from CNData. MUST BE PREVIOUSLY DEFINED. no default.' + CRLF +
- 'Specifies use of Concentric Neutral cable parameter calculation.';
- PropertyHelp[14] := 'Code from TSData. MUST BE PREVIOUSLY DEFINED. no default.' + CRLF +
- 'Specifies use of Tape Shield cable parameter calculation.';
- PropertyHelp[15] := 'Array of CNData names for cable parameter calculation.' + CRLF +
- 'All must be previously defined, and match "nphases" for this geometry.' + CRLF +
- 'You can later define "nconds-nphases" wires for bare neutral conductors.';
- PropertyHelp[16] := 'Array of TSData names for cable parameter calculation.' + CRLF +
- 'All must be previously defined, and match "nphases" for this geometry.' + CRLF +
- 'You can later define "nconds-nphases" wires for bare neutral conductors.';
- PropertyHelp[17] := 'Defines the number of ratings to be defined for the wire, to be used only when defining seasonal ratings using the ' +
- '"Ratings" property. Defaults to first conductor if not specified.' ;
- PropertyHelp[18] := 'An array of ratings to be used when the seasonal ratings flag is True. It can be used to insert' +
- CRLF + 'multiple ratings to change during a QSTS simulation to evaluate different ratings in lines.' +
- 'Defaults to first conductor if not specified.';
- PropertyHelp[19] := 'Code designating the type of line. ' + CRLF +
- 'One of: OH, UG, UG_TS, UG_CN, SWT_LDBRK, SWT_FUSE, SWT_SECT, SWT_REC, SWT_DISC, SWT_BRK, SWT_ELBOW' + CRLF + CRLF +
- 'OpenDSS currently does not use this internally. For whatever purpose the user defines. Default is OH.' ;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ PropertyStructArrayCountOffset := ptruint(@obj.FNconds);
+ PropertyStructArrayIndexOffset := ptruint(@obj.FActiveCond);
+ PropertyStructArrayIndexOffset2 := ptruint(@obj.FNPhases);
+
+ // list of objects
+ PropertyType[ord(TProp.tscables)] := TPropertyType.DSSObjectReferenceArrayProperty;
+ PropertyOffset[ord(TProp.tscables)] := ptruint(@obj.FWireData);
+ PropertyOffset2[ord(TProp.tscables)] := ptruint(DSS.TSDataClass);
+ PropertyFlags[ord(TProp.tscables)] := [TPropertyFlag.AltIndex, TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.tscables)] := ord(TProp.tscable);
+
+ PropertyType[ord(TProp.cncables)] := TPropertyType.DSSObjectReferenceArrayProperty;
+ PropertyOffset[ord(TProp.cncables)] := ptruint(@obj.FWireData);
+ PropertyOffset2[ord(TProp.cncables)] := ptruint(DSS.CNDataClass);
+ PropertyFlags[ord(TProp.cncables)] := [TPropertyFlag.AltIndex, TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.cncables)] := ord(TProp.cncable);
+
+ PropertyType[ord(TProp.wires)] := TPropertyType.DSSObjectReferenceArrayProperty;
+ PropertyOffset[ord(TProp.wires)] := ptruint(@obj.FWireData);
+ PropertyOffset2[ord(TProp.wires)] := ptruint(DSS.WireDataClass);
+ PropertyWriteFunction[ord(TProp.wires)] := @SetWires;
+ PropertyFlags[ord(TProp.wires)] := [TPropertyFlag.WriteByFunction, TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.wires)] := ord(TProp.wire);
+
+ // enums
+ PropertyType[ord(TProp.units)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.units)] := ptruint(@obj.FUnits);
+ PropertyOffset2[ord(TProp.units)] := PtrInt(DSS.UnitsEnum);
+ PropertyFlags[ord(TProp.units)] := [TPropertyFlag.OnArray];
+
+ PropertyType[ord(TProp.linetype)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.linetype)] := ptruint(@obj.FLineType);
+ PropertyOffset2[ord(TProp.linetype)] := PtrInt(DSS.LineTypeEnum);
+
+ // object properties
+ PropertyType[ord(TProp.spacing)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.spacing)] := ptruint(@obj.LineSpacingObj);
+ PropertyOffset2[ord(TProp.spacing)] := ptruint(DSS.LineSpacingClass);
+
+ PropertyType[ord(TProp.Ratings)] := TPropertyType.DoubleDArrayProperty;
+ PropertyOffset[ord(TProp.Ratings)] := ptruint(@obj.AmpRatings);
+ PropertyOffset2[ord(TProp.Ratings)] := ptruint(@obj.NumAmpRatings);
+
+ PropertyType[ord(TProp.reduce)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.reduce)] := ptruint(@obj.Freduce);
+
+ PropertyType[ord(TProp.nphases)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.Seasons)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.nconds)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.cond)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.nphases)] := ptruint(@obj.FNphases);
+ PropertyOffset[ord(TProp.Seasons)] := ptruint(@obj.NumAmpRatings);
+ PropertyOffset[ord(TProp.nconds)] := ptruint(@obj.FNconds);
+ PropertyOffset[ord(TProp.cond)] := ptruint(@obj.FActiveCond);
+ PropertyFlags[ord(TProp.nphases)] := [TPropertyFlag.NonNegative]; // phases can be zero (e.g. only neutral cables)
+ PropertyFlags[ord(TProp.nconds)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+ PropertyFlags[ord(TProp.cond)] := [TPropertyFlag.IntegerStructIndex];
+
+ PropertyType[ord(TProp.wire)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.cncable)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.tscable)] := TPropertyType.DSSObjectReferenceProperty;
+
+ PropertyOffset[ord(TProp.wire)] := ptruint(@obj.FWireData);
+ PropertyOffset[ord(TProp.cncable)] := ptruint(@obj.FWireData);
+ PropertyOffset[ord(TProp.tscable)] := ptruint(@obj.FWireData);
+
+ PropertyOffset2[ord(TProp.wire)] := ptruint(DSS.WireDataClass);
+ PropertyOffset2[ord(TProp.cncable)] := ptruint(DSS.CNDataClass);
+ PropertyOffset2[ord(TProp.tscable)] := ptruint(DSS.TSDataClass);
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
+ PropertyFlags[ord(TProp.wire)] := [TPropertyFlag.OnArray, TPropertyFlag.FullNameAsArray];
+ PropertyFlags[ord(TProp.cncable)] := [TPropertyFlag.Redundant, TPropertyFlag.OnArray];
+ PropertyFlags[ord(TProp.tscable)] := [TPropertyFlag.Redundant, TPropertyFlag.OnArray];
+ PropertyRedundantWith[ord(TProp.cncable)] := ord(TProp.wire);
+ PropertyRedundantWith[ord(TProp.tscable)] := ord(TProp.wire);
+
+ PropertyType[ord(TProp.x)] := TPropertyType.DoubleOnArrayProperty;
+ PropertyType[ord(TProp.h)] := TPropertyType.DoubleOnArrayProperty;
+ PropertyOffset[ord(TProp.x)] := ptruint(@obj.FX); PropertyOffset2[ord(TProp.x)] := ptruint(@obj.FActiveCond);
+ PropertyOffset[ord(TProp.h)] := ptruint(@obj.FY); PropertyOffset2[ord(TProp.h)] := ptruint(@obj.FActiveCond);
+ PropertyOffset[ord(TProp.NormAmps)] := ptruint(@obj.NormAmps);
+ PropertyOffset[ord(TProp.EmergAmps)] := ptruint(@obj.EmergAmps);
+
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLineGeometry.NewObject(const ObjName: String): Integer;
+
+function TLineGeometry.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // create a new object of this class and add to list
- DSS.ActiveDSSObject := TLineGeometryObj.Create(Self, ObjName);
- Result := AddObjectToList(DSS.ActiveDSSObject);
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLineGeometry.Edit: Integer;
+procedure TLineGeometryObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
- i, istart, istop: Integer;
-
+ tmpName: String;
+ i: Integer;
+ conductorObj: TConductorDataObj = NIL;
begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveLineGeometryObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActiveLineGeometryObj;
-
- with DSS.ActiveLineGeometryObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
+ case Idx of
+ ord(TProp.nphases):
+ if FLineData <> NIL then
+ begin
+ FLineData.Nphases := FNPhases;
+ if (FLineData.Nphases > FNconds) then
+ FLineData.Nphases := FNConds;
+ end;
+ ord(TProp.cond):
+ if Funits^[FactiveCond] = -1 then
+ Funits^[FactiveCond] := FLastUnit; // makes this a sticky value so you don't have to repeat it
+ ord(TProp.wire):
+ if FPhaseChoice^[ActiveCond] = Unknown then
+ ChangeLineConstantsType(Overhead);
+ ord(TProp.units):
+ FLastUnit := FUnits^[ActiveCond];
+ ord(TProp.cncable), ord(TProp.cncables):
+ ChangeLineConstantsType(ConcentricNeutral);
+ ord(TProp.tscable), ord(TProp.tscables):
+ ChangeLineConstantsType(TapeShield);
+ ord(TProp.nconds):
begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
+ if previousIntVal <> FNConds then
+ begin
+ if Assigned(FLineData) then
+ FreeAndNil(FLineData);
+
+ // Allocations
+ Reallocmem(FWireData, Sizeof(FWireData^[1]) * FNconds);
+ Reallocmem(FX, Sizeof(FX^[1]) * FNconds);
+ Reallocmem(FY, Sizeof(FY^[1]) * FNconds);
+ Reallocmem(FUnits, Sizeof(Funits^[1]) * FNconds);
+ Reallocmem(FPhaseChoice, Sizeof(FPhaseChoice^[1]) * FNconds);
+ end
else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 10101);
- 1:
- NConds := Parser.IntValue; // Use property value to force reallocations
- 2:
- FNphases := Parser.IntValue;
- 3:
- ActiveCond := Parser.IntValue;
- 4:
- begin
- FCondName^[ActiveCond] := Param;
- if FPhaseChoice^[ActiveCond] = Unknown then
- ChangeLineConstantsType(Overhead);
- end;
- 5:
- FX^[ActiveCond] := Parser.DblValue;
- 6:
- FY^[ActiveCond] := Parser.DblValue;
- 7:
- begin
- FUnits^[ActiveCond] := GetUnitsCode(Param);
- FLastUnit := FUnits^[ActiveCond];
- end;
- 8:
- NormAmps := Parser.DblValue;
- 9:
- EmergAmps := Parser.DblValue;
- 10:
- Freduce := InterpretYesNo(Param);
- 11:
+ begin
+ for i := 1 to FNconds do
+ FWireData[i] := NIL;
+ end;
+
+ if FNconds > previousIntVal then
+ for i := Max(1, previousIntVal) to FNconds do
+ FPhaseChoice^[i] := Unknown;
+
+ for i := 1 to FNconds do
+ begin
+ FActiveCond := i;
+ ChangeLineConstantsType(Overhead); // works on activecond
+ end;
+ // Reset the active conductor
+ FActiveCond := 1;
+
+ // Initialize Allocations
+ for i := 1 to FNconds do
+ FPhaseChoice^[i] := Overhead;
+ for i := 1 to FNconds do
+ FWireData^[i] := NIL;
+ for i := 1 to FNconds do
+ FX^[i] := 0.0;
+ for i := 1 to FNconds do
+ FY^[i] := 0.0;
+ for i := 1 to FNconds do
+ FUnits^[i] := -1; // default to ft
+ FLastUnit := UNITS_FT;
+ end;
+ ord(TProp.spacing):
+ if LineSpacingObj <> NIL then
+ begin
+ if (FNConds = LineSpacingObj.NWires) then
begin
- FSpacingType := Parser.StrValue;
- if DSS.LineSpacingClass.SetActive(FSpacingType) then
+ FLastUnit := LineSpacingObj.Units;
+ for i := 1 to FNConds do
begin
- DSS.ActiveLineSpacingObj := DSS.LineSpacingClass.GetActiveObj;
- if (FNConds = DSS.ActiveLineSpacingObj.NWires) then
- begin
- FLastUnit := DSS.ActiveLineSpacingObj.Units;
- for i := 1 to FNConds do
- begin
- FX^[i] := DSS.ActiveLineSpacingObj.Xcoord[i];
- FY^[i] := DSS.ActiveLineSpacingObj.Ycoord[i];
- FUnits^[i] := FLastUnit;
- end
- end
- else
- DoSimpleMsg('LineSpacing object ' + FSpacingType + ' has the wrong number of wires.', 10103);
+ FX^[i] := LineSpacingObj.Xcoord[i];
+ FY^[i] := LineSpacingObj.Ycoord[i];
+ FUnits^[i] := FLastUnit;
end
- else
- DoSimpleMsg('LineSpacing object ' + FSpacingType + ' has not been defined.', 10103);
- end;
- 13:
+ end
+ else
+ DoSimpleMsg('LineSpacing object %s has the wrong number of wires.', [LineSpacingObj.Name], 10103);
+ end;
+ end;
+ case Idx of
+ ord(TProp.wires), ord(TProp.cncables), ord(TProp.tscables):
+ begin
+ i := 1;
+ if Idx = ord(TProp.wires) then
+ begin
+ if FPhaseChoice^[ActiveCond] = Unknown then
begin
- FCondName^[ActiveCond] := Param;
- ChangeLineConstantsType(ConcentricNeutral)
- end;
- 14:
+ // no other cables set for ActiveCond
+ end
+ else
+ if FPhaseChoice^[ActiveCond] <> Overhead then
+ // these are buried neutral wires
+ // (only when the phase conductors not overhead)
+ i := FNPhases + 1;
+ end;
+ if i = 1 then
+ begin
+ conductorObj := FWireData^[1];
+ if (conductorObj.NormAmps > 0.0) and (Normamps = 0.0) then
+ Normamps := conductorObj.NormAmps;
+
+ if (conductorObj.Emergamps > 0.0) and (Emergamps = 0.0) then
+ Emergamps := conductorObj.EmergAmps;
+
+ if (conductorObj.NumAmpRatings > 1) and (NumAmpRatings = 1) then
+ NumAmpRatings := conductorObj.NumAmpRatings;
+
+ if (Length(conductorObj.AmpRatings) > 1) and (length(AmpRatings) = 1) then
begin
- FCondName^[ActiveCond] := Param;
- ChangeLineConstantsType(TapeShield)
+ AmpRatings := Copy(conductorObj.AmpRatings, 0, Length(conductorObj.AmpRatings));
end;
- 12, 15, 16:
+ end;
+ end;
+ ord(TProp.wire), ord(TProp.cncable), ord(TProp.tscable):
+ begin
+ conductorObj := FWireData[ActiveCond];
+ if Assigned(conductorObj) then
+ begin
+ FWireData^[ActiveCond] := conductorObj;
+ // Default the current ratings for this geometry to the rating of the first conductor
+ if (ActiveCond = 1) then
begin
- istart := 1;
- istop := FNConds;
- if ParamPointer = 15 then
- begin
- ChangeLineConstantsType(ConcentricNeutral);
- istop := FNPhases;
- end
- else
- if ParamPointer = 16 then
+ if (conductorObj.NormAmps > 0.0) and (Normamps = 0.0) then
+ Normamps := conductorObj.NormAmps;
+ if (conductorObj.Emergamps > 0.0) and (Emergamps = 0.0) then
+ Emergamps := conductorObj.EmergAmps;
+ if (conductorObj.NumAmpRatings > 1) and (NumAmpRatings = 1) then
+ NumAmpRatings := conductorObj.NumAmpRatings;
+ if (length(conductorObj.AmpRatings) > 1) and (length(AmpRatings) = 1) then
begin
- ChangeLineConstantsType(TapeShield);
- istop := FNPhases;
- end
- else
- if ParamPointer = 12 then
- begin
- if FPhaseChoice^[ActiveCond] = Unknown then
- ChangeLineConstantsType(Overhead)
- else if FPhaseChoice^[ActiveCond] <> Overhead then
- // these are buried neutral wires
- // (only when the phase conductors not overhead)
- istart := FNPhases + 1;
+ SetLength(AmpRatings, NumAmpRatings);
+ AmpRatings := Copy(conductorObj.AmpRatings,
+ 0, Min(Length(conductorObj.AmpRatings), NumAmpRatings)
+ );
end;
-
- AuxParser.CmdString := Parser.StrValue;
- for i := istart to istop do
- begin
- AuxParser.NextParam; // ignore any parameter name not expecting any
- FCondName[i] := AuxParser.StrValue;
- if ParamPointer = 15 then
- DSS.CNDataClass.code := FCondName[i]
- else
- if ParamPointer = 16 then
- DSS.TSDataClass.code := FCondName[i]
- else
- DSS.WireDataClass.Code := FCondName[i];
- if Assigned(DSS.ActiveConductorDataObj) then
- begin
- FWireData^[i] := DSS.ActiveConductorDataObj;
- if (i = 1) then with DSS do
- begin
- if (ActiveConductorDataObj.NormAmps > 0.0) and (Normamps = 0.0) then
- Normamps := ActiveConductorDataObj.NormAmps;
- if (ActiveConductorDataObj.Emergamps > 0.0) and (Emergamps = 0.0) then
- Emergamps := ActiveConductorDataObj.EmergAmps;
- if (ActiveConductorDataObj.NumAmpRatings > 1) and (NumAmpRatings = 1) then
- NumAmpRatings := ActiveConductorDataObj.NumAmpRatings;
- if (Length(ActiveConductorDataObj.AmpRatings) > 1) and (length(AmpRatings) = 1) then
- begin
- SetLength(AmpRatings, NumAmpRatings);
- AmpRatings := ActiveConductorDataObj.AmpRatings;
- end;
- end;
- end
- else
- if ParamPointer = 15 then
- DoSimpleMsg('CNData Object "' + FCondName[i] + '" not defined. Must be previously defined.', 10103)
- else
- if ParamPointer = 16 then
- DoSimpleMsg('TSData Object "' + FCondName[i] + '" not defined. Must be previously defined.', 10103)
- else
- DoSimpleMsg('WireData Object "' + FCondName[i] + '" not defined. Must be previously defined.', 10103);
- end;
- FActiveCond := istop;
- end;
- 17:
- begin
- NumAmpRatings := Parser.IntValue;
- setlength(AmpRatings, NumAmpRatings);
- end;
- 18:
- begin
- setlength(AmpRatings, NumAmpRatings);
- Param := Parser.StrValue;
- NumAmpRatings := InterpretDblArray(Param, NumAmpRatings, Pointer(AmpRatings));
end;
- 19:
- begin
- FLineType := LineTypeList.Getcommand(Param);
- end
+ end
else
- // Inherited parameters
- ClassEdit(DSS.ActiveLineGeometryObj, Parampointer - NumPropsThisClass)
- end;
-
- {Set defaults}
- case ParamPointer of
-
- 2:
- if (FNPhases > FNconds) then
- FNPhases := FNConds;
- 3:
- if (ActiveCond < 1) or (ActiveCond > FNconds) then
- DoSimpleMsg('Illegal cond= specification in Line Geometry:' + CRLF + Parser.cmdstring, 10102);
- 4, 13, 14:
- begin
- if ParamPointer = 4 then
- DSS.WireDataClass.code := Param
- else
- if ParamPointer = 13 then
- DSS.CNDataClass.code := Param
- else
- DSS.TSDataClass.Code := Param;
- if Assigned(DSS.ActiveConductorDataObj) then
- begin
- FWireData^[ActiveCond] := DSS.ActiveConductorDataObj;
- {Default the current ratings for this geometry to the rating of the first conductor}
- if (ActiveCond = 1) then with DSS do
- begin
- if (ActiveConductorDataObj.NormAmps > 0.0) and (Normamps = 0.0) then
- Normamps := ActiveConductorDataObj.NormAmps;
- if (ActiveConductorDataObj.Emergamps > 0.0) and (Emergamps = 0.0) then
- Emergamps := ActiveConductorDataObj.EmergAmps;
- if (ActiveConductorDataObj.NumAmpRatings > 1) and (NumAmpRatings = 1) then
- NumAmpRatings := ActiveConductorDataObj.NumAmpRatings;
- if (length(ActiveConductorDataObj.AmpRatings) > 1) and (length(AmpRatings) = 1) then
- begin
- SetLength(AmpRatings, NumAmpRatings);
- AmpRatings := Copy(ActiveConductorDataObj.AmpRatings,
- 0, Min(Length(ActiveConductorDataObj.AmpRatings), NumAmpRatings)
- );
- end;
- end;
- end
- else
- if ParamPointer = 4 then
- DoSimpleMsg('WireData Object "' + param + '" not defined. Must be previously defined.', 10103)
- else
- if ParamPointer = 13 then
- DoSimpleMsg('CNData Object "' + param + '" not defined. Must be previously defined.', 10103)
- else
- DoSimpleMsg('TSData Object "' + param + '" not defined. Must be previously defined.', 10103);
- end;
- end;
-
- case ParamPointer of
- 1, 4..7, 11..16:
- DataChanged := TRUE;
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ DoSimpleMsg('WireData/CNData/TSData object was not defined. Must be previously defined.', [tmpName], 10103);
end;
-
+ ord(TProp.Seasons):
+ setlength(AmpRatings, NumAmpRatings);
end;
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLineGeometry.MakeLike(const LineName: String): Integer;
-var
- OtherLineGeometry: TLineGeometryObj;
- i: Integer;
-begin
- Result := 0;
- {See if we can find this line code in the present collection}
- OtherLineGeometry := Find(LineName);
- if OtherLineGeometry <> NIL then
- with DSS.ActiveLineGeometryObj do
- begin
- NConds := OtherLineGeometry.NWires; // allocates
- FNphases := OtherLineGeometry.FNphases;
- FSpacingType := OtherLineGeometry.FSpacingType;
- FLineType := OtherLineGeometry.FLineType; //TODO: check original
- for i := 1 to FNConds do
- FPhaseChoice^[i] := OtherLineGeometry.FPhaseChoice^[i];
- for i := 1 to FNConds do
- FCondName^[i] := OtherLineGeometry.FCondName^[i];
- for i := 1 to FNConds do
- FWireData^[i] := OtherLineGeometry.FWireData^[i];
- for i := 1 to FNConds do
- FX^[i] := OtherLineGeometry.FX^[i];
- for i := 1 to FNConds do
- FY^[i] := OtherLineGeometry.FY^[i];
- for i := 1 to FNConds do
- FUnits^[i] := OtherLineGeometry.FUnits^[i];
+ case Idx of
+ 1, 4..7, 11..16:
DataChanged := TRUE;
- NormAmps := OtherLineGeometry.NormAmps;
- EmergAmps := OtherLineGeometry.EmergAmps;
-
- UpdateLineGeometryData(activecircuit.solution.Frequency);
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherLineGeometry.PropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in LineGeometry MakeLike: "' + LineName + '" Not Found.', 102);
-
-
-end;
+ end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLineGeometry.Get_Code: String; // Returns active line code string
-begin
- Result := TLineGeometryObj(ElementList.Active).Name;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-procedure TLineGeometry.Set_Code(const Value: String); // sets the active LineGeometry
+procedure TLineGeometryObj.MakeLike(OtherPtr: Pointer);
var
- LineGeometryObj: TLineGeometryObj;
+ Other: TObj;
+ i: Integer;
begin
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ NConds := Other.NWires; // allocates
+ FNphases := Other.FNphases;
+ LineSpacingObj := Other.LineSpacingObj;
+ FLineType := Other.FLineType; //TODO: check original
+ for i := 1 to FNConds do
+ FPhaseChoice^[i] := Other.FPhaseChoice^[i];
+ for i := 1 to FNConds do
+ FWireData^[i] := Other.FWireData^[i];
+ for i := 1 to FNConds do
+ FX^[i] := Other.FX^[i];
+ for i := 1 to FNConds do
+ FY^[i] := Other.FY^[i];
+ for i := 1 to FNConds do
+ FUnits^[i] := Other.FUnits^[i];
+ DataChanged := TRUE;
+ NormAmps := Other.NormAmps;
+ EmergAmps := Other.EmergAmps;
+ FReduce := Other.FReduce;
- DSS.ActiveLineGeometryObj := NIL;
- LineGeometryObj := ElementList.First;
- while LineGeometryObj <> NIL do
- begin
-
- if CompareText(LineGeometryObj.Name, Value) = 0 then
- begin
- DSS.ActiveLineGeometryObj := LineGeometryObj;
- Exit;
- end;
-
- LineGeometryObj := ElementList.Next;
- end;
-
- DoSimpleMsg('LineGeometry: "' + Value + '" not Found.', 103);
-
+ UpdateLineGeometryData(activecircuit.solution.Frequency);
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TLineGeometry Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
constructor TLineGeometryObj.Create(ParClass: TDSSClass; const LineGeometryName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(LineGeometryName);
+ Name := AnsiLowerCase(LineGeometryName);
DSSObjType := ParClass.DSSClassType;
DataChanged := TRUE;
FPhaseChoice := NIL;
- FCondName := NIL;
FWireData := NIL;
FX := NIL;
FY := NIL;
Funits := NIL;
FLineData := NIL;
- FSpacingType := '';
+ LineSpacingObj := NIL;
+
+ // was causing unnecessary allocations (was leaving dangling memory)
+ // Nconds := 3; // Allocates terminals
+ // FNphases := 3;
-(* was causing unnecessary allocations (was leaving dangling memory)
- Nconds := 3; // Allocates terminals
- FNphases := 3;
-*)
FNconds := 0;
FNPhases := 0;
-// ActiveCond := 1;
+ // ActiveCond := 1;
FActiveCond := 1;
FLastUnit := UNITS_FT;
Normamps := 0.0;
@@ -653,16 +546,12 @@ constructor TLineGeometryObj.Create(ParClass: TDSSClass; const LineGeometryName:
NumAmpRatings := 1;
setlength(AmpRatings, NumAmpRatings);
AmpRatings[0] := NormAmps;
-
- InitPropertyValues(0);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TLineGeometryObj.Destroy;
begin
-
- FLineData.Free;
- FreeStringArray(FCondName, FnConds);
+ if FLineData <> NIL then
+ FLineData.Free;
Reallocmem(Fwiredata, 0);
Reallocmem(FY, 0);
Reallocmem(FX, 0);
@@ -672,88 +561,31 @@ destructor TLineGeometryObj.Destroy;
inherited destroy;
end;
-
-procedure TLineGeometryObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
+procedure TLineGeometryObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i, j: Integer;
-
begin
+ FSWriteln(F, '! WARNING: when mixing wire/cable types, "wires", "cncables" and "tscables" may not make sense in this dump');
+
inherited DumpProperties(F, Complete);
- with ParentClass do
+ for i := 1 to 2 do
begin
- for i := 1 to 2 do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + GetPropertyValue(i));
- end;
- for j := 1 to FNConds do
- begin
- ActiveCond := j;
- FSWriteln(F, '~ ' + PropertyName^[3] + '=' + GetPropertyValue(3));
- FSWriteln(F, '~ ' + PropertyName^[4] + '=' + GetPropertyValue(4));
- FSWriteln(F, '~ ' + PropertyName^[5] + '=' + GetPropertyValue(5));
- FSWriteln(F, '~ ' + PropertyName^[6] + '=' + GetPropertyValue(6));
- FSWriteln(F, '~ ' + PropertyName^[7] + '=' + GetPropertyValue(7));
- end;
- for i := 8 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + GetPropertyValue(i));
- end;
-
+ FSWriteln(F, '~ ' + ParentClass.PropertyName^[i] + '=' + GetPropertyValue(i));
end;
-
-end;
-
-function TLineGeometryObj.GetPropertyValue(Index: Integer): String;
-var
- j,
- i: Integer;
-{Return Property Value for Active index}
-
-begin
-
- case Index of
- 3:
- Result := Format('%d', [FActiveCond]);
- 4, 13, 14:
- Result := FCondName^[FActiveCond];
- 5:
- Result := Format('%-g', [FX^[FActiveCond]]);
- 6:
- Result := Format('%-g', [FY^[FActiveCond]]);
- 7:
- Result := LineUnitsStr(FUnits^[FActiveCond]);
- 8:
- Result := Format('%-g',[NormAmps]);
- 9:
- Result := Format('%-g',[EmergAmps]);
- 12, 15, 16:
- begin
- Result := '[';
- for i := 1 to FNConds do
- Result := Result + FCondName^[i] + ' ';
- Result := Result + ']';
- end;
- 17:
- Result := inttostr(NumAmpRatings);
- 18:
- begin
- Result := '[';
- for j := 1 to NumAmpRatings do
- Result := Result + floattoStrf(AmpRatings[j - 1], ffgeneral, 8, 4) + ',';
- Result := Result + ']';
- end;
- 19:
- if (FLineType >= 1) and (FLineType <= High(LINE_TYPES)) then
- Result := LINE_TYPES[FLineType]
- else
- Result := '';
- else
- // Inherited parameters
- Result := inherited GetPropertyValue(Index);
+ for j := 1 to FNConds do
+ begin
+ ActiveCond := j;
+ FSWriteln(F, '~ ' + ParentClass.PropertyName^[3] + '=' + GetPropertyValue(3));
+ FSWriteln(F, '~ ' + ParentClass.PropertyName^[4] + '=' + GetPropertyValue(4));
+ FSWriteln(F, '~ ' + ParentClass.PropertyName^[5] + '=' + GetPropertyValue(5));
+ FSWriteln(F, '~ ' + ParentClass.PropertyName^[6] + '=' + GetPropertyValue(6));
+ FSWriteln(F, '~ ' + ParentClass.PropertyName^[7] + '=' + GetPropertyValue(7));
+ end;
+ for i := 8 to ParentClass.NumProperties do
+ begin
+ FSWriteln(F, '~ ' + ParentClass.PropertyName^[i] + '=' + GetPropertyValue(i));
end;
-
end;
function TLineGeometryObj.Get_FX(i: Integer): Double;
@@ -780,7 +612,6 @@ function TLineGeometryObj.Get_FUnits(i: Integer): Integer;
Result := 0;
end;
-{$IFDEF DSS_CAPI}
procedure TLineGeometryObj.Set_FX(i: Integer; Value: Double);
begin
if i <= FNConds then
@@ -799,12 +630,10 @@ procedure TLineGeometryObj.Set_FUnits(i: Integer; Value: Integer);
FUnits^[i] := Value;
end;
-{$ENDIF}
-
function TLineGeometryObj.Get_ConductorName(i: Integer): String;
begin
if i <= FNConds then
- Result := FCondName^[i]
+ Result := FWireData[i].Name
else
Result := '';
end;
@@ -855,97 +684,54 @@ function TLineGeometryObj.Get_Zmatrix(f, Lngth: Double;
Result := FLineData.ZMatrix[F, Lngth, Units, DSS.ActiveEarthModel];
end;
-procedure TLineGeometryObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := '3';
- PropertyValue[2] := '3';
- PropertyValue[3] := '1';
- PropertyValue[4] := '';
- PropertyValue[5] := '0';
- PropertyValue[6] := '32';
- PropertyValue[7] := 'ft';
- PropertyValue[8] := '0';
- PropertyValue[9] := '0';
- PropertyValue[17] := '1'; // 1 season
- PropertyValue[18] := '[400]'; // 1 rating
- PropertyValue[19] := 'OH';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-
procedure TLineGeometryObj.SaveWrite(F: TFileStream);
-{ Override standard SaveWrite}
-{Linegeometry structure not conducive to standard means of saving}
+// Override standard SaveWrite
+// Linegeometry structure not conducive to standard means of saving
var
- TempStr: String;
strPhaseChoice: String;
- j,
iprop: Integer;
i: Integer;
-
+ wroteConds: Boolean = False;
begin
- {Write only properties that were explicitly set in the
- final order they were actually set}
- iProp := GetNextPropertySet(0); // Works on ActiveDSSObject
+ // Write only properties that were explicitly set in the
+ // final order they were actually set
+ iProp := GetNextPropertySet(0);
if iProp > 0 then
FSWriteln(F);
while iProp > 0 do
begin
with ParentClass do
-
- case RevPropertyIdxMap[iProp] of
- 3, 11, 12:
- begin // if cond=, spacing, or wires were ever used write out arrays ...
- for i := 1 to Fnconds do
- begin
- case PhaseChoice[i] of
- Overhead:
+ case iProp of
+ ord(TProp.cond), ord(TProp.spacing), ord(TProp.wires):
+ if not wroteConds then
+ begin // if cond=, spacing, or wires were ever used write out arrays ...
+ for i := 1 to Fnconds do
+ begin
+ if FWireData[i] = NIL then
+ continue; // shouldn't happen in normal conditions
+ if FWireData[i].ParentClass = DSS.TSDataClass then
+ strPhaseChoice := 'tscable'
+ else if FWireData[i].ParentClass = DSS.CNDataClass then
+ strPhaseChoice := 'cncable'
+ else
strPhaseChoice := 'wire';
- ConcentricNeutral:
- strPhaseChoice := 'cncable';
- TapeShield:
- strPhaseChoice := 'tscable';
- else
- strPhaseChoice := 'wire';
+ FSWriteln(F, Format('~ Cond=%d %s=%s X=%.7g h=%.7g units=%s',
+ [i, strPhaseChoice, FWireData[i].Name, FX^[i], FY^[i], LineUnitsStr(FUnits^[i])]));
end;
- FSWriteln(F, Format('~ Cond=%d %s=%s X=%.7g h=%.7g units=%s',
- [i, strPhaseChoice, FCondName^[i], FX^[i], FY^[i], LineUnitsStr(FUnits^[i])]));
+ wroteConds := True;
end;
- end;
- 4..7:
-{do Nothing}; // Ignore these properties;
- 8:
- FSWriteln(F, Format('~ normamps=%.4g', [NormAmps]));
- 9:
- FSWriteln(F, Format('~ emergamps=%.4g', [EmergAmps]));
- 10:
+ ord(TProp.reduce):
if FReduce then
FSWriteln(F, '~ Reduce=Yes');
- 13..14: ; {do Nothing} // Ignore these properties;
- 18:
- begin
- TempStr := '[';
- for j := 1 to NumAmpRatings do
- TempStr := TempStr + floattoStrf(AmpRatings[j - 1], ffgeneral, 8, 4) + ',';
- TempStr := TempStr + ']';
- FSWriteln(F, 'ratings=' + TempStr);
- end;
- 19:
- begin
- if (FLineType >= 1) and (FLineType <= (High(LINE_TYPES) + 1)) then
- FSWriteln(F, '~ LineType=' + LINE_TYPES[FLineType - 1]);
- end
+ ord(TProp.wire), ord(TProp.x), ord(TProp.h), ord(TProp.units),
+ ord(TProp.cncable), ord(TProp.tscable):
+ ; // Ignore these properties;
else
- FSWriteln(F, Format('~ %s=%s', [PropertyName^[RevPropertyIdxMap[iProp]], CheckForBlanks(PropertyValue[iProp])]));
+ FSWriteln(F, Format('~ %s=%s', [PropertyName[iProp], CheckForBlanks(PropertyValue[iProp])]));
end;
- iProp := GetNextPropertySet(iProp);
+ iProp := GetNextPropertySet(iProp);
end;
-
-
end;
procedure TLineGeometryObj.set_ActiveCond(const Value: Integer);
@@ -966,12 +752,12 @@ procedure TLineGeometryObj.ChangeLineConstantsType(newPhaseChoice: ConductorChoi
begin
newLineData := NIL;
needNew := FALSE;
- if newPhaseChoice <> FPhaseChoice^[ActiveCond] then
- needNew := TRUE;
- if not Assigned(FLineData) then
+
+ if (ActiveCond > 0) and (ActiveCond <= FNConds) and
+ (newPhaseChoice <> FPhaseChoice^[ActiveCond]) then
needNew := TRUE
else
- if FNConds <> FLineData.Nconductors then
+ if (FLineData = NIL) or (FNConds <> FLineData.Nconductors) then
needNew := TRUE;
if needNew then
@@ -990,70 +776,28 @@ procedure TLineGeometryObj.ChangeLineConstantsType(newPhaseChoice: ConductorChoi
begin
newLineData.Nphases := FLineData.Nphases;
newLineData.rhoearth := FLineData.rhoearth;
- end
- else
- FreeAndNil(FLineData);
+ end;
+ FreeAndNil(FLineData);
FLineData := newLineData;
end;
- FPhaseChoice^[ActiveCond] := newPhaseChoice;
+ if (ActiveCond > 0) and (ActiveCond <= FNConds) then
+ FPhaseChoice^[ActiveCond] := newPhaseChoice;
end;
procedure TLineGeometryObj.set_Nconds(const Value: Integer);
var
- i: Integer;
+ prev: Integer;
begin
- if Value < 1 then
- begin
- DoSimpleMsg('Invalid number of conductors sent via DSS command. Please enter a value within range.', 185);
- Exit;
- end;
-
- if Assigned(FCondName) then
- FreestringArray(FCondName, FNConds); // dispose of old allocation
-
- FNconds := Value;
- if Assigned(FLineData) then
- FreeAndNil(FLineData);
-
-
- {Allocations}
- Reallocmem(FWireData, Sizeof(FWireData^[1]) * FNconds);
- Reallocmem(FX, Sizeof(FX^[1]) * FNconds);
- Reallocmem(FY, Sizeof(FY^[1]) * FNconds);
- Reallocmem(FUnits, Sizeof(Funits^[1]) * FNconds);
- Reallocmem(FPhaseChoice, Sizeof(FPhaseChoice^[1]) * FNconds);
-
-{Initialize Allocations}
- for i := 1 to FNconds do
- FPhaseChoice^[i] := Overhead;
- for i := 1 to FNconds do
- FWireData^[i] := NIL;
- for i := 1 to FNconds do
- FX^[i] := 0.0;
- for i := 1 to FNconds do
- FY^[i] := 0.0;
- for i := 1 to FNconds do
- FUnits^[i] := -1; // default to ft
- FLastUnit := UNITS_FT;
-
- for i := 1 to FNconds do
- begin
- FActiveCond := i;
- ChangeLineConstantsType(Overhead); // works on activecond
- end;
- // Reset the active conductor
- FActiveCond := 1;
-
- FCondName := AllocStringArray(FNconds);
-
-
+ prev := Fnconds;
+ Fnconds := Value;
+ PropertySideEffects(ord(TProp.nconds), prev)
end;
procedure TLineGeometryObj.set_Nphases(const Value: Integer);
begin
if Value < 1 then
begin
- DoSimpleMsg('Invalid number of phases sent via DSS command. Please enter a value within range.', 186);
+ DoSimpleMsg(_('Invalid number of phases sent via DSS command. Please enter a value within range.'), 186);
Exit;
end;
@@ -1073,7 +817,6 @@ procedure TLineGeometryObj.UpdateLineGeometryData(f: Double);
cnd: TCNDataObj;
tsd: TTSDataObj;
begin
-
for i := 1 to FNconds do
begin
FLineData.X[i, Funits^[i]] := FX^[i];
@@ -1118,15 +861,15 @@ procedure TLineGeometryObj.UpdateLineGeometryData(f: Double);
FLineData.Nphases := FNphases;
DataChanged := FALSE;
- {Before we calc, check for bad conductor definitions}
+ // Before we calc, check for bad conductor definitions
if FLineData.ConductorsInSameSpace(LineGeomErrMsg) then
begin
- raise ELineGeometryProblem.Create('Error in LineGeometry.' + Name + ': ' + LineGeomErrMsg);
+ raise ELineGeometryProblem.Create(Format(_('Error in %s: %s'), [FullName, LineGeomErrMsg]));
DSS.SolutionAbort := TRUE;
end
else
begin
- FLineData.Calc(f, DSS.ActiveEarthModel); {***** Line impedance calc'd here ****}
+ FLineData.Calc(f, DSS.ActiveEarthModel); // ***** Line impedance calc'd here ****
if FReduce then
FLineData.Reduce; // reduce out neutrals
end;
@@ -1139,7 +882,7 @@ procedure TLineGeometryObj.LoadSpacingAndWires(Spc: TLineSpacingObj; Wires: pCon
begin
NConds := Spc.NWires; // allocates
FNphases := Spc.Nphases;
- FSpacingType := Spc.Name;
+ LineSpacingObj := Spc;
if FNConds > FNPhases then
FReduce := TRUE;
@@ -1153,8 +896,6 @@ procedure TLineGeometryObj.LoadSpacingAndWires(Spc: TLineSpacingObj; Wires: pCon
end;
ChangeLineConstantsType(newPhaseChoice);
- for i := 1 to FNConds do
- FCondName^[i] := Wires^[i].Name;
for i := 1 to FNConds do
FWireData^[i] := Wires^[i];
for i := 1 to FNConds do
@@ -1170,4 +911,4 @@ procedure TLineGeometryObj.LoadSpacingAndWires(Spc: TLineSpacingObj; Wires: pCon
UpdateLineGeometryData(activecircuit.solution.Frequency);
end;
-end.
+end.
\ No newline at end of file
diff --git a/src/General/LineSpacing.pas b/src/General/LineSpacing.pas
index baa5a7f49..2bd954b69 100644
--- a/src/General/LineSpacing.pas
+++ b/src/General/LineSpacing.pas
@@ -18,355 +18,186 @@ interface
DSSObject;
type
+{$SCOPEDENUMS ON}
+ TLineSpacingProp = (
+ INVALID = 0,
+ nconds = 1,
+ nphases = 2,
+ x = 3,
+ h = 4,
+ units = 5
+ );
+{$SCOPEDENUMS OFF}
+
SpcParmChoice = (X, H);
TLineSpacing = class(TDSSClass)
- PRIVATE
-
- function Get_Code: String; // Returns active line code string
- procedure Set_Code(const Value: String); // sets the active LineSpacing
- procedure InterpretArray(const S: String; which: SpcParmChoice);
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const LineName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
-
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
-
- // Set this property to point ActiveLineSpacingObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-
TLineSpacingObj = class(TDSSObject)
PUBLIC
FX: pDoubleArray;
FY: pDoubleArray;
- PRIVATE
FNConds: Integer;
- FNPhases: Integer;
- FUnits: Integer;
-{$IFNDEF DSS_CAPI}
- DataChanged: Boolean;
-{$ENDIF}
-
- procedure set_Nwires(const Value: Integer);
+ NPhases: Integer;
+ Units: Integer;
// CIM Accessors
function Get_FX(i: Integer): Double;
function Get_FY(i: Integer): Double;
-{$IFDEF DSS_CAPI}
procedure Set_FX(i: Integer; Value: Double);
procedure Set_FY(i: Integer; Value: Double);
- procedure Set_FUnits(Value: Integer);
-{$ENDIF}
PUBLIC
-{$IFDEF DSS_CAPI}
DataChanged: Boolean;
-{$ENDIF}
constructor Create(ParClass: TDSSClass; const LineSpacingName: String);
destructor Destroy; OVERRIDE;
-
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
// CIM XML accessors
- property Xcoord[i: Integer]: Double READ Get_FX
-{$IFDEF DSS_CAPI}
- WRITE Set_FX
-{$ENDIF}
- ;
- property Ycoord[i: Integer]: Double READ Get_FY
-{$IFDEF DSS_CAPI}
- WRITE Set_FY
-{$ENDIF}
- ;
- property NWires: Integer READ FNConds WRITE set_Nwires;
- property NPhases: Integer READ FNPhases
-{$IFDEF DSS_CAPI}
- WRITE FNPhases
-{$ENDIF}
- ;
- property Units: Integer READ FUnits
-{$IFDEF DSS_CAPI}
- WRITE Set_FUnits
-{$ENDIF}
- ;
+ // TODO: remove
+ property Xcoord[i: Integer]: Double READ Get_FX WRITE Set_FX;
+ property Ycoord[i: Integer]: Double READ Get_FY WRITE Set_FY;
+ property NWires: Integer READ FNConds;
end;
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
- Ucomplex,
+ UComplex, DSSUcomplex,
Utilities,
LineUnits,
DSSHelper,
DSSObjectHelper,
TypInfo;
+type
+ TObj = TLineSpacingObj;
+ TProp = TLineSpacingProp;
const
- NumPropsThisClass = 5;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TLineSpacing.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TLineSpacing.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'LineSpacing';
- DSSClassType := DSS_OBJECT;
- ActiveElement := 0;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, DSS_OBJECT, 'LineSpacing');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TLineSpacing.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
procedure TLineSpacing.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- PropertyName[1] := 'nconds';
- PropertyName[2] := 'nphases';
- PropertyName[3] := 'x';
- PropertyName[4] := 'h';
- PropertyName[5] := 'units';
-
-
- PropertyHelp[1] := 'Number of wires in this geometry. Default is 3. Triggers memory allocations. Define first!';
- PropertyHelp[2] := 'Number of retained phase conductors. If less than the number of wires, list the retained phase coordinates first.';
- PropertyHelp[3] := 'Array of wire X coordinates.';
- PropertyHelp[4] := 'Array of wire Heights.';
- PropertyHelp[5] := 'Units for x and h: {mi|kft|km|m|Ft|in|cm } Initial default is "ft", but defaults to last unit defined';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enums
+ PropertyType[ord(TProp.units)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.units)] := ptruint(@obj.Units);
+ PropertyOffset2[ord(TProp.units)] := PtrInt(DSS.UnitsEnum);
+
+ // integers
+ PropertyType[ord(TProp.nphases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.nphases)] := ptruint(@obj.Nphases);
+ PropertyType[ord(TProp.nconds)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.nconds)] := ptruint(@obj.FNconds);
+
+ // arrays
+ PropertyType[ord(TProp.X)] := TPropertyType.DoubleVArrayProperty;
+ PropertyOffset[ord(TProp.X)] := ptruint(@obj.FX);
+ PropertyOffset2[ord(TProp.X)] := ptruint(@obj.FNconds);
+
+ PropertyType[ord(TProp.H)] := TPropertyType.DoubleVArrayProperty;
+ PropertyOffset[ord(TProp.H)] := ptruint(@obj.FY);
+ PropertyOffset2[ord(TProp.H)] := ptruint(@obj.FNconds);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
-end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLineSpacing.NewObject(const ObjName: String): Integer;
-begin
- // create a new object of this class and add to list
- DSS.ActiveDSSObject := TLineSpacingObj.Create(Self, ObjName);
- Result := AddObjectToList(DSS.ActiveDSSObject);
+ inherited DefineProperties;
end;
-procedure TLineSpacing.InterpretArray(const S: String; which: SpcParmChoice);
+function TLineSpacing.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- Str: String;
- i: Integer;
+ Obj: TObj;
begin
- AuxParser.CmdString := S;
- with DSS.ActiveLineSpacingObj do
- begin
- for i := 1 to NWires do
- begin
- AuxParser.NextParam; // ignore any parameter name not expecting any
- Str := AuxParser.StrValue;
- if Length(Str) > 0 then
- case which of
- X:
- FX^[i] := AuxParser.Dblvalue;
- H:
- FY^[i] := AuxParser.Dblvalue;
- end;
- end;
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLineSpacing.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
+procedure TLineSpacingObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveLineSpacingObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActiveLineSpacingObj;
-
- with DSS.ActiveLineSpacingObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
+ case Idx of
+ ord(TProp.nconds):
begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 10101);
- 1:
- NWires := Parser.IntValue; // Use property value to force reallocations
- 2:
- FNPhases := Parser.IntValue;
- 3:
- InterpretArray(Param, X);
- 4:
- InterpretArray(Param, H);
- 5:
- FUnits := GetUnitsCode(Param);
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveLineSpacingObj, Parampointer - NumPropsThisClass)
- end;
-
- case ParamPointer of
- 1..5:
- DataChanged := TRUE;
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ FX := Allocmem(Sizeof(FX^[1]) * FNconds);
+ FY := Allocmem(Sizeof(FY^[1]) * FNconds);
+ Units := UNITS_FT;
+ DataChanged := TRUE;
end;
-
+ 2..5:
+ DataChanged := TRUE;
end;
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLineSpacing.MakeLike(const LineName: String): Integer;
+procedure TLineSpacingObj.MakeLike(OtherPtr: Pointer);
var
- OtherLineSpacing: TLineSpacingObj;
+ Other: TObj;
i: Integer;
begin
- Result := 0;
- {See if we can find this line code in the present collection}
- OtherLineSpacing := Find(LineName);
- if OtherLineSpacing <> NIL then
- with DSS.ActiveLineSpacingObj do
- begin
-
- NWires := OtherLineSpacing.NWires; // allocates
- FNPhases := OtherLineSpacing.NPhases;
- for i := 1 to FNConds do
- FX^[i] := OtherLineSpacing.FX^[i];
- for i := 1 to FNConds do
- FY^[i] := OtherLineSpacing.FY^[i];
- FUnits := OtherLineSpacing.FUnits;
- DataChanged := TRUE;
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherLineSpacing.PropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in LineSpacing MakeLike: "' + LineName + '" Not Found.', 102);
-
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLineSpacing.Get_Code: String; // Returns active line code string
-begin
- Result := TLineSpacingObj(ElementList.Active).Name;
-end;
-
-procedure TLineSpacing.Set_Code(const Value: String); // sets the active LineSpacing
-var
- LineSpacingObj: TLineSpacingObj;
-begin
-
- DSS.ActiveLineSpacingObj := NIL;
- LineSpacingObj := ElementList.First;
- while LineSpacingObj <> NIL do
- begin
-
- if CompareText(LineSpacingObj.Name, Value) = 0 then
- begin
- DSS.ActiveLineSpacingObj := LineSpacingObj;
- Exit;
- end;
-
- LineSpacingObj := ElementList.Next;
- end;
-
- DoSimpleMsg('LineSpacing: "' + Value + '" not Found.', 103);
-
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNConds := Other.FNConds;
+ PropertySideEffects(ord(TProp.NConds), 0);
+ NPhases := Other.NPhases;
+ for i := 1 to FNConds do
+ FX^[i] := Other.FX^[i];
+ for i := 1 to FNConds do
+ FY^[i] := Other.FY^[i];
+ Units := Other.Units;
+ DataChanged := TRUE;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TLineSpacing Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
constructor TLineSpacingObj.Create(ParClass: TDSSClass; const LineSpacingName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(LineSpacingName);
+ Name := AnsiLowerCase(LineSpacingName);
DSSObjType := ParClass.DSSClassType;
DataChanged := TRUE;
FX := NIL;
FY := NIL;
- Funits := UNITS_FT;
- NWires := 3; // Allocates terminals
- FNPhases := 3;
-
- InitPropertyValues(0);
+ units := UNITS_FT;
+ FNConds := 3;
+ PropertySideEffects(ord(TProp.NConds), 0);
+ NPhases := 3;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TLineSpacingObj.Destroy;
begin
-
Reallocmem(FY, 0);
Reallocmem(FX, 0);
-
inherited destroy;
-
-end;
-
-
-procedure TLineSpacingObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- begin
- for i := 1 to 5 do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + GetPropertyValue(i));
- end;
- end;
-
end;
function ArrayString(pF: pDoubleArray; N: Integer): String;
@@ -382,26 +213,6 @@ function ArrayString(pF: pDoubleArray; N: Integer): String;
Result := r + ']';
end;
-function TLineSpacingObj.GetPropertyValue(Index: Integer): String;
-
-{Return Property Value for Active index}
-
-begin
-
- case Index of
- 3:
- Result := ArrayString(FX, FNConds);
- 4:
- Result := ArrayString(FY, FNConds);
- 5:
- Result := LineUnitsStr(FUnits);
- else
- // Inherited parameters
- Result := inherited GetPropertyValue(Index);
- end;
-
-end;
-
function TLineSpacingObj.Get_FX(i: Integer): Double;
begin
if i <= FNConds then
@@ -418,8 +229,6 @@ function TLineSpacingObj.Get_FY(i: Integer): Double;
Result := 0.0;
end;
-
-{$IFDEF DSS_CAPI}
procedure TLineSpacingObj.Set_FX(i: Integer; Value: Double);
begin
if (i > 0) and (i <= FNConds) then
@@ -432,32 +241,4 @@ procedure TLineSpacingObj.Set_FY(i: Integer; Value: Double);
FY^[i] := Value;
end;
-procedure TLineSpacingObj.Set_FUnits(Value: Integer);
-begin
- FUnits := Value;
-end;
-
-{$ENDIF}
-
-procedure TLineSpacingObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := '3';
- PropertyValue[2] := '3';
- PropertyValue[3] := '0';
- PropertyValue[4] := '32';
- PropertyValue[5] := 'ft';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-procedure TLineSpacingObj.set_NWires(const Value: Integer);
-begin
- FNconds := Value;
- FX := Allocmem(Sizeof(FX^[1]) * FNconds);
- FY := Allocmem(Sizeof(FY^[1]) * FNconds);
- FUnits := UNITS_FT;
-end;
-
-end.
+end.
\ No newline at end of file
diff --git a/src/General/LoadShape.pas b/src/General/LoadShape.pas
index 406a2c378..f796418e9 100644
--- a/src/General/LoadShape.pas
+++ b/src/General/LoadShape.pas
@@ -8,50 +8,47 @@
----------------------------------------------------------
}
-{ 8-18-00 Added call to InterpretDblArrayto allow File=Syntax }
-
interface
-{The LoadShape object is a general DSS object used by all circuits
- as a reference for obtaining yearly, daily, and other load shapes.
-
- The values are set by the normal New and Edit procedures for any DSS object.
-
- The values are retrieved by setting the Code Property in the LoadShape Class.
- This sets the active LoadShape object to be the one referenced by the Code Property;
-
- Then the values of that code can be retrieved via the public variables. Or you
- can pick up the ActiveLoadShapeObj object and save the direct reference to the object.
-
- Loadshapes default to fixed interval data. If the Interval is specified to be 0.0,
- then both time and multiplier data are expected. If the Interval is greater than 0.0,
- the user specifies only the multipliers. The Hour command is ignored and the files are
- assumed to contain only the multiplier data.
-
- The user may place the data in CSV or binary files as well as passing through the
- command interface. Obviously, for large amounts of data such as 8760 load curves, the
- command interface is cumbersome. CSV files are text separated by commas, one interval to a line.
- There are two binary formats permitted: 1) a file of Singles; 2) a file of Doubles.
-
- For fixed interval data, only the multiplier is expected. Therefore, the CSV format would
- contain only one number per line. The two binary formats are packed.
-
- For variable interval data, (hour, multiplier) pairs are expected in both formats.
-
- The Mean and Std Deviation are automatically computed upon demand when new series of points is entered.
-
- The data may also be entered in unnormalized form. The normalize=Yes command will force normalization. That
- is, the multipliers are scaled so that the maximum value is 1.0.
-
- }
+// The LoadShape object is a general DSS object used by all circuits
+// as a reference for obtaining yearly, daily, and other load shapes.
+//
+// The values are set by the normal New and Edit procedures for any DSS object.
+//
+// The values are retrieved by setting the Code Property in the LoadShape Class.
+// This sets the active LoadShape object to be the one referenced by the Code Property;
+//
+// Then the values of that code can be retrieved via the public variables. Or you
+// can pick up the ActiveLoadShapeObj object and save the direct reference to the object.
+//
+// Loadshapes default to fixed interval data. If the Interval is specified to be 0.0,
+// then both time and multiplier data are expected. If the Interval is greater than 0.0,
+// the user specifies only the multipliers. The Hour command is ignored and the files are
+// assumed to contain only the multiplier data.
+//
+// The user may place the data in CSV or binary files as well as passing through the
+// command interface. Obviously, for large amounts of data such as 8760 load curves, the
+// command interface is cumbersome. CSV files are text separated by commas, one interval to a line.
+// There are two binary formats permitted: 1) a file of Singles; 2) a file of Doubles.
+//
+// For fixed interval data, only the multiplier is expected. Therefore, the CSV format would
+// contain only one number per line. The two binary formats are packed.
+//
+// For variable interval data, (hour, multiplier) pairs are expected in both formats.
+//
+// The Mean and Std Deviation are automatically computed upon demand when new series of points is entered.
+//
+// The data may also be entered in unnormalized form. The normalize=Yes command will force normalization. That
+// is, the multipliers are scaled so that the maximum value is 1.0.
uses
Classes,
+ ParserDel,
Command,
DSSClass,
DSSObject,
UcMatrix,
- ucomplex,
+ UComplex, DSSUcomplex,
Arraydef,
Utilities,
{$IFDEF WINDOWS}
@@ -63,46 +60,54 @@ interface
type
{$SCOPEDENUMS ON}
+ TLoadShapeProp = (
+ INVALID = 0,
+ npts = 1, // Number of points to expect
+ interval = 2, // default = 1.0
+ mult = 3, // vector of power multiplier values
+ hour = 4, // vector of hour values
+ mean = 5, // set the mean (otherwise computed)
+ stddev = 6, // set the std dev (otherwise computed)
+ csvfile = 7, // Switch input to a csvfile
+ sngfile = 8, // switch input to a binary file of singles
+ dblfile = 9, // switch input to a binary file of singles
+ action = 10, // actions Normalize
+ qmult = 11, // Q multiplier
+ UseActual = 12, // Flag to signify to use actual value
+ Pmax = 13, // MaxP value
+ Qmax = 14, // MaxQ
+ sinterval = 15, // Interval in seconds
+ minterval = 16, // Interval in minutes
+ Pbase = 17, // for normalization, use peak if 0
+ Qbase = 18, // for normalization, use peak if 0
+ Pmult = 19, // synonym for Mult
+ PQCSVFile = 20, // Redirect to a file with p, q pairs
+ MemoryMapping = 21 // Enable/disable using Memory mapping for this shape
+ );
+
TMMShapeType = (
P = 0,
Q = 1
);
{$SCOPEDENUMS OFF}
-
TLoadShape = class(TDSSClass)
- PRIVATE
-
- function Get_Code: String; // Returns active LoadShape string
- procedure Set_Code(const Value: String); // sets the active LoadShape
-
- procedure DoCSVFile(const FileName: String);
- procedure Do2ColCSVFile(const FileName: String); // for P and Q pairs
- procedure DoSngFile(const FileName: String);
- procedure DoDblFile(const FileName: String);
PROTECTED
- procedure DefineProperties;
- function MakeLike(const ShapeName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
function Find(const ObjName: String; const ChangeActive: Boolean=True): Pointer; OVERRIDE; // Find an obj of this class by name
-
- function CreateMMF(const S: String; Destination: TMMShapeType): Boolean;
- // Set this property to point ActiveLoadShapeObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
-
end;
TLoadShapeObj = class(TDSSObject)
PRIVATE
LastValueAccessed,
FNumPoints: Integer; // Number of points in curve -- TODO: int64
- ArrayPropertyIndex: Integer;
FStdDevCalculated: Boolean;
FMean,
@@ -131,11 +136,13 @@ TLoadShapeObj = class(TDSSObject)
MaxP, MaxQ, BaseP, BaseQ: Double;
MaxQSpecified: Boolean;
- Enabled, UseActual, ExternalMemory: Boolean;
+ Enabled: Boolean;
+ ExternalMemory: LongBool;
+ UseActual: LongBool;
Stride: Integer;
// Memory mapping variables
- UseMMF: Boolean; // Flag to indicated that the user wants to use MMF
+ UseMMF: LongBool; // Flag to indicated that the user wants to use MMF
{$IFDEF WINDOWS}
mmMMF, mmQMMF: THandle; // Handle for the memory map (P, Q)
mmFile, mmQFile: THandle; // Handle for the file to be mapped (P, Q)
@@ -151,8 +158,14 @@ TLoadShapeObj = class(TDSSObject)
mmDataSize, mmDataSizeQ, // The total data size expected (P, Q)
mmViewLen, mmViewLenQ: Int64; // Memory View size in bytes (P)
+ csvfile, dblfile, sngfile, pqcsvfile: String;
+
constructor Create(ParClass: TDSSClass; const LoadShapeName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
+ procedure CustomSetRaw(Idx: Integer; Value: String); override;
+ procedure SaveWrite(F: TFileStream); override;
function GetMultAtHour(hr: Double): Complex; // Get multiplier at specified time
function Mult(i: Integer): Double; // get multiplier by index -- used in SolutionAlgs, updates LastValueAccessed
@@ -166,8 +179,6 @@ TLoadShapeObj = class(TDSSObject)
procedure LoadFileFeatures(ShapeType: TMMShapeType);
function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
property NumPoints: Integer READ FNumPoints WRITE FNumPoints;
property PresentInterval: Double READ Get_Interval;
@@ -183,13 +194,12 @@ TLoadShapeObj = class(TDSSObject)
implementation
uses
- ParserDel,
+ BufStream,
DSSClassDefs,
DSSGlobals,
Sysutils,
MathUtil,
Math,
- BufStream,
DSSPointerList,
DSSHelper,
DSSObjectHelper,
@@ -198,148 +208,200 @@ implementation
type
ELoadShapeError = class(Exception); // Raised to abort solution
-const
- NumPropsThisClass = 21;
+type
+ TObj = TLoadShapeObj;
+ TProp = TLoadShapeProp;
+{$PUSH}
+{$Z4} // keep enums as int32 values
+ TLoadShapeAction = (
+ Normalize = 0,
+ DblSave = 1,
+ SngSave = 2
+ );
+{$POP}
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TLoadShape.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
-begin
- inherited Create(dssContext);
- Class_Name := 'LoadShape';
- DSSClassType := DSS_OBJECT;
+const
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
+ ActionEnum: TDSSEnum;
- ActiveElement := 0;
+procedure Do2ColCSVFile(Obj: TObj; const FileName: String);forward;
+procedure DoDblFile(Obj: TObj; const FileName: String);forward;
+procedure DoSngFile(Obj: TObj; const FileName: String);forward;
+procedure DoCSVFile(Obj: TObj; const FileName: String);forward;
- DefineProperties;
+constructor TLoadShape.Create(dssContext: TDSSContext);
+begin
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ ActionEnum := TDSSEnum.Create('LoadShape: Action', True, 1, 1,
+ ['Normalize', 'DblSave', 'SngSave'],
+ [ord(TLoadShapeAction.Normalize), ord(TLoadShapeAction.DblSave), ord(TLoadShapeAction.SngSave)]);
+ end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, DSS_OBJECT, 'LoadShape');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
destructor TLoadShape.Destroy;
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLoadShape.DefineProperties;
+
+function GetMean(obj: TObj): Double;
+begin
+ Result := obj.Mean;
+end;
+
+function GetStdDev(obj: TObj): Double;
+begin
+ Result := obj.StdDev;
+end;
+
+procedure DoAction(Obj: TObj; action: TLoadShapeAction);
+begin
+ case action of
+ TLoadShapeAction.Normalize:
+ Obj.Normalize;
+ TLoadShapeAction.DblSave:
+ Obj.SaveToDblFile;
+ TLoadShapeAction.SngSave:
+ Obj.SaveToSngFile;
+ end;
+end;
+
+procedure SetNumPoints(Obj: TObj; Value: Integer);
begin
+ with Obj do
+ if ExternalMemory then
+ begin
+ DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61102);
+ Exit;
+ end
+ else
+ NumPoints := Value;
+end;
+procedure TLoadShape.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
+begin
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
- PropertyName[1] := 'npts'; // Number of points to expect
- PropertyName[2] := 'interval'; // default = 1.0;
- PropertyName[3] := 'mult'; // vector of power multiplier values
- PropertyName[4] := 'hour'; // vextor of hour values
- PropertyName[5] := 'mean'; // set the mean (otherwise computed)
- PropertyName[6] := 'stddev'; // set the std dev (otherwise computed)
- PropertyName[7] := 'csvfile'; // Switch input to a csvfile
- PropertyName[8] := 'sngfile'; // switch input to a binary file of singles
- PropertyName[9] := 'dblfile'; // switch input to a binary file of doubles
- PropertyName[10] := 'action'; // actions Normalize
- PropertyName[11] := 'qmult'; // Q multiplier
- PropertyName[12] := 'UseActual'; // Flag to signify to use actual value
- PropertyName[13] := 'Pmax'; // MaxP value
- PropertyName[14] := 'Qmax'; // MaxQ
- PropertyName[15] := 'sinterval'; // Interval in seconds
- PropertyName[16] := 'minterval'; // Interval in minutes
- PropertyName[17] := 'Pbase'; // for normalization, use peak if 0
- PropertyName[18] := 'Qbase'; // for normalization, use peak if 0
- PropertyName[19] := 'Pmult'; // synonym for Mult
- PropertyName[20] := 'PQCSVFile'; // Redirect to a file with p, q pairs
- PropertyName[21] := 'MemoryMapping'; // Enable/disable using Memory mapping for this shape
-
- // define Property help values
-
- PropertyHelp[1] := 'Max number of points to expect in load shape vectors. This gets reset to the number of multiplier values found (in files only) if less than specified.'; // Number of points to expect
- PropertyHelp[2] := 'Time interval for fixed interval data, hrs. Default = 1. ' +
- 'If Interval = 0 then time data (in hours) may be at either regular or irregular intervals and time value must be specified using either the Hour property or input files. ' +
- 'Then values are interpolated when Interval=0, but not for fixed interval data. ' + CRLF + CRLF +
- 'See also "sinterval" and "minterval".'; // default = 1.0;
- PropertyHelp[3] := 'Array of multiplier values for active power (P) or other key value (such as pu V for Vsource). ' + CRLF + CRLF +
- 'You can also use the syntax: ' + CRLF + CRLF +
- 'mult = (file=filename) !for text file one value per line' + CRLF +
- 'mult = (dblfile=filename) !for packed file of doubles' + CRLF +
- 'mult = (sngfile=filename) !for packed file of singles ' + CRLF +
- 'mult = (file=MyCSVFile.CSV, col=3, header=yes) !for multicolumn CSV files ' + CRLF + CRLF +
- 'Note: this property will reset Npts if the number of values in the files are fewer.' + CRLF + CRLF +
- 'Same as Pmult'; // vector of power multiplier values
- PropertyHelp[4] := 'Array of hour values. Only necessary to define for variable interval data (Interval=0).' +
- ' If you set Interval>0 to denote fixed interval data, DO NOT USE THIS PROPERTY. ' +
- 'You can also use the syntax: ' + CRLF +
- 'hour = (file=filename) !for text file one value per line' + CRLF +
- 'hour = (dblfile=filename) !for packed file of doubles' + CRLF +
- 'hour = (sngfile=filename) !for packed file of singles '; // vextor of hour values
- PropertyHelp[5] := 'Mean of the active power multipliers. This is computed on demand the first time a ' +
- 'value is needed. However, you may set it to another value independently. ' +
- 'Used for Monte Carlo load simulations.'; // set the mean (otherwise computed)
- PropertyHelp[6] := 'Standard deviation of active power multipliers. This is computed on demand the first time a ' +
- 'value is needed. However, you may set it to another value independently.' +
- 'Is overwritten if you subsequently read in a curve' + CRLF + CRLF +
- 'Used for Monte Carlo load simulations.'; // set the std dev (otherwise computed)
- PropertyHelp[7] := 'Switch input of active power load curve data to a CSV text file ' +
- 'containing (hour, mult) points, or simply (mult) values for fixed time interval data, one per line. ' +
- 'NOTE: This action may reset the number of points to a lower value.'; // Switch input to a csvfile
- PropertyHelp[8] := 'Switch input of active power load curve data to a binary file of singles ' +
- 'containing (hour, mult) points, or simply (mult) values for fixed time interval data, packed one after another. ' +
- 'NOTE: This action may reset the number of points to a lower value.'; // switch input to a binary file of singles
- PropertyHelp[9] := 'Switch input of active power load curve data to a binary file of doubles ' +
- 'containing (hour, mult) points, or simply (mult) values for fixed time interval data, packed one after another. ' +
- 'NOTE: This action may reset the number of points to a lower value.'; // switch input to a binary file of singles
- PropertyHelp[10] := '{NORMALIZE | DblSave | SngSave} After defining load curve data, setting action=normalize ' +
- 'will modify the multipliers so that the peak is 1.0. ' +
- 'The mean and std deviation are recomputed.' + CRLF + CRLF +
- 'Setting action=DblSave or SngSave will cause the present mult and qmult values to be written to ' +
- 'either a packed file of double or single. The filename is the loadshape name. The mult array will have a ' +
- '"_P" appended on the file name and the qmult array, if it exists, will have "_Q" appended.'; // Action
- PropertyHelp[11] := 'Array of multiplier values for reactive power (Q). You can also use the syntax: ' + CRLF +
- 'qmult = (file=filename) !for text file one value per line' + CRLF +
- 'qmult = (dblfile=filename) !for packed file of doubles' + CRLF +
- 'qmult = (sngfile=filename) !for packed file of singles ' + CRLF + // vector of qmultiplier values
- 'qmult = (file=MyCSVFile.CSV, col=4, header=yes) !for multicolumn CSV files ';
- PropertyHelp[12] := '{Yes | No* | True | False*} If true, signifies to Load, Generator, Vsource, or other objects to ' +
- 'use the return value as the actual kW, kvar, kV, or other value rather than a multiplier. ' +
- 'Nominally for AMI Load data but may be used for other functions.';
- PropertyHelp[13] := 'kW value at the time of max power. Is automatically set upon reading in a loadshape. ' +
- 'Use this property to override the value automatically computed or to retrieve the value computed.';
- PropertyHelp[14] := 'kvar value at the time of max kW power. Is automatically set upon reading in a loadshape. ' +
- 'Use this property to override the value automatically computed or to retrieve the value computed.';
- PropertyHelp[15] := 'Specify fixed interval in SECONDS. Alternate way to specify Interval property.';
- PropertyHelp[16] := 'Specify fixed interval in MINUTES. Alternate way to specify Interval property.';
- PropertyHelp[17] := 'Base P value for normalization. Default is zero, meaning the peak will be used.';
- PropertyHelp[18] := 'Base Q value for normalization. Default is zero, meaning the peak will be used.';
- PropertyHelp[19] := 'Synonym for "mult".';
- PropertyHelp[20] := 'Switch input to a CSV text file containing (active, reactive) power (P, Q) multiplier pairs, one per row. ' + CRLF +
- 'If the interval=0, there should be 3 items on each line: (hour, Pmult, Qmult)';
- PropertyHelp[21] := '{Yes | No* | True | False*} Enables the memory mapping functionality for dealing with large amounts of load shapes. ' + CRLF +
- 'By default is False. Use it to accelerate the model loading when the containing a large number of load shapes.';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // boolean properties
+ PropertyType[ord(TProp.MemoryMapping)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.UseActual)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.MemoryMapping)] := ptruint(@obj.UseMMF);
+ PropertyOffset[ord(TProp.UseActual)] := ptruint(@obj.UseActual);
+
+ // advanced doubles
+ PropertyOffset[ord(TProp.sinterval)] := ptruint(@obj.Interval);
+ PropertyScale[ord(TProp.sinterval)] := 1 / 3600.0;
+ PropertyFlags[ord(TProp.sinterval)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.sinterval)] := ord(TProp.interval);
+
+ PropertyOffset[ord(TProp.minterval)] := ptruint(@obj.Interval);
+ PropertyScale[ord(TProp.minterval)] := 1 / 60.0;
+ PropertyFlags[ord(TProp.minterval)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.minterval)] := ord(TProp.interval);
+
+ // double properties
+ PropertyOffset[ord(TProp.interval)] := ptruint(@obj.Interval);
+ PropertyOffset[ord(TProp.Pmax)] := ptruint(@obj.MaxP);
+ PropertyOffset[ord(TProp.Qmax)] := ptruint(@obj.MaxQ);
+ PropertyOffset[ord(TProp.Pbase)] := ptruint(@obj.BaseP);
+ PropertyOffset[ord(TProp.Qbase)] := ptruint(@obj.BaseQ);
+
+ PropertyOffset[ord(TProp.mean)] := ptruint(@obj.FMean);
+ PropertyReadFunction[ord(TProp.mean)] := @GetMean;
+ PropertyFlags[ord(TProp.mean)] := [TPropertyFlag.ReadByFunction];
+
+ PropertyOffset[ord(TProp.stddev)] := ptruint(@obj.FStdDev);
+ PropertyReadFunction[ord(TProp.stddev)] := @GetStdDev;
+ PropertyFlags[ord(TProp.stddev)] := [TPropertyFlag.ReadByFunction];
+
+ // double arrays, special
+ PropertyType[ord(TProp.hour)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.hour)] := ptruint(@obj.dH);
+ PropertyOffset2[ord(TProp.hour)] := ptruint(@obj.FNumPoints);
+ PropertyOffset3[ord(TProp.hour)] := ptruint(@obj.ExternalMemory);
+ PropertyFlags[ord(TProp.hour)] := [TPropertyFlag.CustomSetRaw, TPropertyFlag.CustomGet, TPropertyFlag.ConditionalReadOnly];
+
+ PropertyType[ord(TProp.mult)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.mult)] := ptruint(@obj.dP);
+ PropertyOffset2[ord(TProp.mult)] := ptruint(@obj.FNumPoints);
+ PropertyOffset3[ord(TProp.mult)] := ptruint(@obj.ExternalMemory);
+ PropertyFlags[ord(TProp.mult)] := [TPropertyFlag.CustomSetRaw, TPropertyFlag.CustomGet, TPropertyFlag.ConditionalReadOnly, TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.mult)] := ord(TProp.pmult);
+
+ PropertyType[ord(TProp.Pmult)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.Pmult)] := ptruint(@obj.dP);
+ PropertyOffset2[ord(TProp.Pmult)] := ptruint(@obj.FNumPoints);
+ PropertyOffset3[ord(TProp.Pmult)] := ptruint(@obj.ExternalMemory);
+ PropertyFlags[ord(TProp.Pmult)] := [TPropertyFlag.CustomSetRaw, TPropertyFlag.CustomGet, TPropertyFlag.ConditionalReadOnly];
+
+ PropertyType[ord(TProp.Qmult)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.Qmult)] := ptruint(@obj.dQ);
+ PropertyOffset2[ord(TProp.Qmult)] := ptruint(@obj.FNumPoints);
+ PropertyOffset3[ord(TProp.Qmult)] := ptruint(@obj.ExternalMemory);
+ PropertyFlags[ord(TProp.Qmult)] := [TPropertyFlag.CustomSetRaw, TPropertyFlag.CustomGet, TPropertyFlag.ConditionalReadOnly];
+
+ // integer
+ Propertytype[ord(TProp.npts)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.npts)] := ptruint(@obj.FNumPoints);
+ PropertyWriteFunction[ord(TProp.npts)] := @SetNumPoints;
+ PropertyFlags[ord(TProp.npts)] := [TPropertyFlag.WriteByFunction];
+
+ // enum action
+ PropertyType[ord(TProp.Action)] := TPropertyType.StringEnumActionProperty;
+ PropertyOffset[ord(TProp.Action)] := ptruint(@DoAction);
+ PropertyOffset2[ord(TProp.Action)] := PtrInt(ActionEnum);
+
+ // strings
+ PropertyType[ord(TProp.csvfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.csvfile)] := ptruint(@obj.csvfile);
+ PropertyFlags[ord(TProp.csvfile)] := [TPropertyFlag.IsFilename];
+
+ PropertyType[ord(TProp.dblfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.dblfile)] := ptruint(@obj.dblfile);
+ PropertyFlags[ord(TProp.dblfile)] := [TPropertyFlag.IsFilename];
+
+ PropertyType[ord(TProp.sngfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.sngfile)] := ptruint(@obj.sngfile);
+ PropertyFlags[ord(TProp.sngfile)] := [TPropertyFlag.IsFilename];
+
+ PropertyType[ord(TProp.PQCSVFile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.PQCSVFile)] := ptruint(@obj.pqcsvfile);
+ PropertyFlags[ord(TProp.PQCSVFile)] := [TPropertyFlag.IsFilename];
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLoadShape.NewObject(const ObjName: String): Integer;
+
+function TLoadShape.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // create a new object of this class and add to list
- DSS.ActiveDSSObject := TLoadShapeObj.Create(Self, ObjName);
- Result := AddObjectToList(DSS.ActiveDSSObject);
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
// Loads the mapped file features into local variables for further use
procedure TLoadShapeObj.LoadFileFeatures(ShapeType: TMMShapeType);
var
LocalCol: Integer;
- fileType: TLSFileType;
+ fileType: TLSFileType = TLSFileType.PlainText;
ParmName,
Param: String;
begin
- AuxParser.CmdString := mmFileCmd;
- ParmName := AuxParser.NextParam;
+ DSS.AuxParser.CmdString := mmFileCmd;
+ ParmName := DSS.AuxParser.NextParam;
LocalCol := 1;
if CompareText(Parmname, 'file') = 0 then
@@ -347,14 +409,14 @@ procedure TLoadShapeObj.LoadFileFeatures(ShapeType: TMMShapeType);
fileType := TLSFileType.PlainText;
// Look for other options (may be in either order)
- ParmName := AuxParser.NextParam;
- Param := AuxParser.StrValue;
+ ParmName := DSS.AuxParser.NextParam;
+ Param := DSS.AuxParser.StrValue;
while Length(Param) > 0 do
begin
if CompareTextShortest(ParmName, 'column') = 0 then
- LocalCol := AuxParser.IntValue;
- ParmName := AuxParser.NextParam;
- Param := AuxParser.StrValue;
+ LocalCol := DSS.AuxParser.IntValue;
+ ParmName := DSS.AuxParser.NextParam;
+ Param := DSS.AuxParser.StrValue;
end;
end
else if CompareText(Parmname, 'dblfile') = 0 then
@@ -425,19 +487,19 @@ procedure TLoadShapeObj.LoadMMFView(const Parmname: String; Destination: TMMShap
end;
// Creates the Memory mapping for the file specified
-function TLoadShape.CreateMMF(const S: String; Destination: TMMShapeType): Boolean;
+function CreateMMF(Obj: TObj; const S: String; Destination: TMMShapeType): Boolean;
var
ParmName,
Param: String;
begin
- with DSS.ActiveLoadShapeObj do
+ with Obj do
try
- AuxParser.CmdString := S;
- ParmName := AuxParser.NextParam;
- Param := AdjustInputFilePath(AuxParser.StrValue);
+ DSS.AuxParser.CmdString := S;
+ ParmName := DSS.AuxParser.NextParam;
+ Param := AdjustInputFilePath(DSS.AuxParser.StrValue);
if not FileExists(Param) then
begin
- DoSimpleMsg(Format('The file "%s" does not exist. Process cancelled.', [Param]), 800002);
+ DoSimpleMsg('The file "%s" does not exist. Process cancelled.', [Param], 800002);
Result := False;
end;
@@ -481,185 +543,118 @@ function TLoadShape.CreateMMF(const S: String; Destination: TMMShapeType): Boole
LoadMMFView(ParmName, Destination);
Result := True;
except
- DoSimpleMsg(Format('There was a problem mapping file "%s". Process cancelled.', [Param]), 800001);
+ DoSimpleMsg('There was a problem mapping file "%s". Process cancelled.', [Param], 800001);
Result := False;
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLoadShape.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveLoadShapeObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActiveLoadShapeObj;
- with DSS.ActiveLoadShapeObj do
- begin
+procedure TLoadShapeObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
+begin
+ case Idx of
+ ord(TProp.csvfile):
+ DoCSVFile(self, csvfile);
+ ord(TProp.sngfile):
+ DoSngFile(self, sngfile);
+ ord(TProp.dblfile):
+ DoDblFile(self, dblfile);
+ ord(TProp.PQCSVFile):
+ Do2ColCSVFile(self, pqcsvfile);
+ end;
+ case Idx of
+ ord(TProp.npts):
+ // Force as the always first property when saving in a later point
+ PrpSequence[Idx] := -10;
+ ord(TProp.mult), ord(TProp.Pmult), ord(TProp.csvfile), ord(TProp.sngfile), ord(TProp.dblfile), ord(TProp.qmult):
+ begin
+ FStdDevCalculated := FALSE; // now calculated on demand
+ NumPoints := FNumPoints; // Keep Properties in order for save command
+ end;
+ ord(TProp.Qmax):
+ MaxQSpecified := TRUE;
+ ord(TProp.MemoryMapping):
+ if UseMMF then
+ UseFloat64;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
+procedure TLoadShapeObj.CustomSetRaw(Idx: Integer; Value: String);
+begin
+ case Idx of
+ ord(TProp.mult), ord(TProp.Pmult):
begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 610);
- 1: // npts:
- if DSS.ActiveLoadShapeObj.ExternalMemory then
- begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61102);
- Exit;
- end
- else
- begin
- NumPoints := Parser.Intvalue;
- // Force as the always first property when saving in a later point
- PrpSequence[ParamPointer] := -10;
- end;
- 2: // interval:
- Interval := Parser.DblValue;
- 3, 19: // Pmult, mult:
- begin
- if DSS.ActiveLoadShapeObj.ExternalMemory then
- begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61102);
- Exit;
- end;
- if UseMMF then
- begin
- if not CreateMMF(Param, TMMShapeType.P) then
- Exit; // CreateMMF throws an error message already
- LoadFileFeatures(TMMShapeType.P);
- mmDataSize := NumPoints;
- ReAllocmem(dP, sizeof(Double) * 2);
- Exit;
- end;
-
- // Otherwise, follow the traditional technique for loading up load shapes
- UseFloat64;
- ReAllocmem(dP, Sizeof(Double) * NumPoints);
- // Allow possible Resetting (to a lower value) of num points when specifying multipliers not Hours
- NumPoints := InterpretDblArray(Param, NumPoints, PDoubleArray(dP)); // Parser.ParseAsVector(Npts, Multipliers);
- end;
- 4: // hour:
- begin
- if DSS.ActiveLoadShapeObj.ExternalMemory then
- begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61102);
- Exit;
- end;
- UseFloat64;
- ReAllocmem(dH, Sizeof(Double) * NumPoints);
- InterpretDblArray(Param, NumPoints, PDoubleArray(dH)); // Parser.ParseAsVector(Npts, Hours);
- Interval := 0.0;
- end;
- 5:
- Mean := Parser.DblValue;
- 6:
- StdDev := Parser.DblValue;
- 7:
- DoCSVFile(AdjustInputFilePath(Param));
- 8:
- DoSngFile(AdjustInputFilePath(Param));
- 9:
- DoDblFile(AdjustInputFilePath(Param));
- 10:
- case lowercase(Param)[1] of
- 'n':
- Normalize;
- 'd':
- SaveToDblFile;
- 's':
- SaveToSngFile;
- end;
- 11: // qmult:
- begin
- if DSS.ActiveLoadShapeObj.ExternalMemory then
- begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61105);
- Exit;
- end;
- if UseMMF then
- begin
- if not CreateMMF(Param, TMMShapeType.Q) then
- Exit; // CreateMMF throws an error message already
- LoadFileFeatures(TMMShapeType.Q);
- if Assigned(dP) then
- mmDataSizeQ := mmDataSize
- else
- mmDataSizeQ := NumPoints;
- ReAllocmem(dQ, sizeof(Double) * 2);
- Exit;
- end;
- // Otherwise, follow the traditional technique for loading up load shapes
- UseFloat64;
- ReAllocmem(dQ, Sizeof(Double) * NumPoints);
- InterpretDblArray(Param, NumPoints, PDoubleArray(dQ)); // Parser.ParseAsVector(Npts, Multipliers);
- end;
- 12: // UseActual:
- UseActual := InterpretYesNo(Param);
- 13:
- MaxP := Parser.DblValue;
- 14:
- MaxQ := Parser.DblValue;
- 15:
- Interval := Parser.DblValue / 3600.0; // Convert seconds to hr
- 16:
- Interval := Parser.DblValue / 60.0; // Convert minutes to hr
- 17:
- BaseP := Parser.DblValue;
- 18:
- BaseQ := Parser.DblValue;
- 20:
- Do2ColCSVFile(AdjustInputFilePath(Param));
- 21:
- begin
- if InterpretYesNo(Param) then
- begin
- UseMMF := True;
- UseFloat64;
- end
- else
- begin
- UseMMF := False;
- end;
- end;
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveLoadShapeObj, ParamPointer - NumPropsThisClass)
+ if ExternalMemory then
+ begin
+ DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61102);
+ Exit;
+ end;
+ if UseMMF then
+ begin
+ if not CreateMMF(self, Value, TMMShapeType.P) then
+ Exit; // CreateMMF throws an error message already
+ LoadFileFeatures(TMMShapeType.P);
+ mmDataSize := NumPoints;
+ ReAllocmem(dP, sizeof(Double) * 2);
+ Exit;
end;
- case ParamPointer of
- 3, 7, 8, 9, 11:
- begin
- FStdDevCalculated := FALSE; // now calculated on demand
- ArrayPropertyIndex := ParamPointer;
- NumPoints := FNumPoints; // Keep Properties in order for save command
- end;
- 14:
- MaxQSpecified := TRUE;
-
+ // Otherwise, follow the traditional technique for loading up load shapes
+ UseFloat64;
+ ReAllocmem(dP, Sizeof(Double) * NumPoints);
+ // Allow possible Resetting (to a lower value) of num points when specifying multipliers not Hours
+ NumPoints := InterpretDblArray(DSS, Value, NumPoints, PDoubleArray(dP)); // TODO: different from the rest and conditional
+ end;
+ ord(TProp.hour):
+ begin
+ if ExternalMemory then
+ begin
+ DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61102);
+ Exit;
end;
+ UseFloat64;
+ ReAllocmem(dH, Sizeof(Double) * NumPoints);
+ InterpretDblArray(DSS, Value, NumPoints, PDoubleArray(dH)); // TODO: different from the rest and conditional
+ Interval := 0.0;
+ end;
+ ord(TProp.qmult):
+ begin
+ if ExternalMemory then
+ begin
+ DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61105);
+ Exit;
+ end;
+ if UseMMF then
+ begin
+ if not CreateMMF(self, Value, TMMShapeType.Q) then
+ Exit; // CreateMMF throws an error message already
+ LoadFileFeatures(TMMShapeType.Q);
+ if Assigned(dP) then
+ mmDataSizeQ := mmDataSize
+ else
+ mmDataSizeQ := NumPoints;
+ ReAllocmem(dQ, sizeof(Double) * 2);
+ Exit;
+ end;
+ // Otherwise, follow the traditional technique for loading up load shapes
+ UseFloat64;
+ ReAllocmem(dQ, Sizeof(Double) * NumPoints);
+ InterpretDblArray(DSS, Value, NumPoints, PDoubleArray(dQ)); // Parser.ParseAsVector(Npts, Multipliers);
+ end;
+ else
+ inherited CustomSetRaw(Idx, Value);
+ end;
+end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end; {WHILE}
-
+function TLoadShape.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
+ begin
if Assigned(dP) or Assigned(sP) then
SetMaxPandQ;
- end; {WITH}
+
+ Exclude(Flags, Flg.EditionActive);
+ end;
+ Result := True;
end;
function TLoadShape.Find(const ObjName: String; const ChangeActive: Boolean): Pointer;
@@ -670,170 +665,125 @@ function TLoadShape.Find(const ObjName: String; const ChangeActive: Boolean): Po
Result := inherited Find(ObjName, ChangeActive);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLoadShape.MakeLike(const ShapeName: String): Integer;
+procedure TLoadShapeObj.MakeLike(OtherPtr: Pointer);
var
- OtherLoadShape: TLoadShapeObj;
+ Other: TObj;
i: Integer;
begin
- Result := 0;
- {See if we can find this line code in the present collection}
- OtherLoadShape := Find(ShapeName);
- if OtherLoadShape <> NIL then
- with DSS.ActiveLoadShapeObj do
- begin
- if ExternalMemory then
- begin
- // There is no point in copying a static loadshape,
- // so we assume the user would want to modify the data
- dP := nil;
- dQ := nil;
- dH:= nil;
- sP := nil;
- sQ := nil;
- sH:= nil;
- ExternalMemory := False;
- end;
-
- NumPoints := OtherLoadShape.NumPoints;
- Interval := OtherLoadShape.Interval;
- Stride := 1;
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ if ExternalMemory then
+ begin
+ // There is no point in copying a static loadshape,
+ // so we assume the user would want to modify the data
+ dP := nil;
+ dQ := nil;
+ dH:= nil;
+ sP := nil;
+ sQ := nil;
+ sH:= nil;
+ ExternalMemory := False;
+ end;
- // Double versions
- if Assigned(OtherLoadShape.dP) then
- begin
- ReallocMem(dP, SizeOf(Double) * NumPoints);
- //Move(OtherLoadShape.dP[0], dP[0], SizeOf(Double) * NumPoints);
- for i := 1 to NumPoints do
- dP[i] := OtherLoadShape.dP[Stride * i];
- end
- else
- ReallocMem(dP, 0);
+ NumPoints := Other.NumPoints;
+ Interval := Other.Interval;
+ Stride := 1;
- if Assigned(OtherLoadShape.dQ) then
- begin
- ReallocMem(dQ, SizeOf(Double) * NumPoints);
- //Move(OtherLoadShape.dQ[0], dQ[0], SizeOf(Double) * NumPoints);
- for i := 1 to NumPoints do
- dQ[i] := OtherLoadShape.dQ[Stride * i];
-
- end;
+ // Double versions
+ if Assigned(Other.dP) then
+ begin
+ ReallocMem(dP, SizeOf(Double) * NumPoints);
+ //Move(Other.dP[0], dP[0], SizeOf(Double) * NumPoints);
+ for i := 1 to NumPoints do
+ dP[i] := Other.dP[Stride * i];
+ end
+ else
+ ReallocMem(dP, 0);
- if Interval > 0.0 then
- ReallocMem(dH, 0)
- else
- begin
- ReallocMem(dH, SizeOf(Double) * NumPoints);
- // Move(OtherLoadShape.dH[0], dH[0], SizeOf(Double) * NumPoints);
- for i := 1 to NumPoints do
- dH[i] := OtherLoadShape.dH[Stride * i];
- end;
+ if Assigned(Other.dQ) then
+ begin
+ ReallocMem(dQ, SizeOf(Double) * NumPoints);
+ //Move(Other.dQ[0], dQ[0], SizeOf(Double) * NumPoints);
+ for i := 1 to NumPoints do
+ dQ[i] := Other.dQ[Stride * i];
+
+ end;
- // Single versions
- if Assigned(OtherLoadShape.sP) then
- begin
- ReallocMem(sP, SizeOf(Single) * NumPoints);
- // Move(OtherLoadShape.sP[0], sP[0], SizeOf(Single) * NumPoints);
- for i := 1 to NumPoints do
- sP[i] := OtherLoadShape.sP[Stride * i];
- end
- else
- ReallocMem(sP, 0);
+ if Interval > 0.0 then
+ ReallocMem(dH, 0)
+ else
+ begin
+ ReallocMem(dH, SizeOf(Double) * NumPoints);
+ // Move(Other.dH[0], dH[0], SizeOf(Double) * NumPoints);
+ for i := 1 to NumPoints do
+ dH[i] := Other.dH[Stride * i];
+ end;
- if Assigned(OtherLoadShape.sQ) then
- begin
- ReallocMem(sQ, SizeOf(Single) * NumPoints);
- // Move(OtherLoadShape.sQ[0], sQ[0], SizeOf(Single) * NumPoints);
- for i := 1 to NumPoints do
- sQ[i] := OtherLoadShape.sQ[Stride * i];
- end;
+ // Single versions
+ if Assigned(Other.sP) then
+ begin
+ ReallocMem(sP, SizeOf(Single) * NumPoints);
+ // Move(Other.sP[0], sP[0], SizeOf(Single) * NumPoints);
+ for i := 1 to NumPoints do
+ sP[i] := Other.sP[Stride * i];
+ end
+ else
+ ReallocMem(sP, 0);
- if Interval > 0.0 then
- ReallocMem(sH, 0)
- else
- begin
- ReallocMem(sH, SizeOf(Single) * NumPoints);
- // Move(OtherLoadShape.sH[0], sH[0], SizeOf(Single) * NumPoints);
- for i := 1 to NumPoints do
- sH[i] := OtherLoadShape.sH[Stride * i];
- end;
+ if Assigned(Other.sQ) then
+ begin
+ ReallocMem(sQ, SizeOf(Single) * NumPoints);
+ // Move(Other.sQ[0], sQ[0], SizeOf(Single) * NumPoints);
+ for i := 1 to NumPoints do
+ sQ[i] := Other.sQ[Stride * i];
+ end;
- UseActual := OtherLoadShape.UseActual;
- UseMMF := OtherLoadShape.UseMMF;
- BaseP := OtherLoadShape.BaseP;
- BaseQ := OtherLoadShape.BaseQ;
- SetMaxPandQ;
-
- // MaxP := OtherLoadShape.MaxP;
- // MaxQ := OtherLoadShape.MaxQ;
- // MaxQSpecified := OtherLoadShape.MaxQSpecified;
- // Mean := OtherLoadShape.Mean;
- // StdDev := OtherLoadShape.StdDev;
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherLoadShape.PropertyValue[i];
- end
+ if Interval > 0.0 then
+ ReallocMem(sH, 0)
else
- DoSimpleMsg('Error in LoadShape MakeLike: "' + ShapeName + '" Not Found.', 611);
-end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLoadShape.Get_Code: String; // Returns active line code string
-var
- LoadShapeObj: TLoadShapeObj;
-begin
- LoadShapeObj := ElementList.Active;
- Result := LoadShapeObj.Name;
-end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLoadShape.Set_Code(const Value: String); // sets the active LoadShape
-var
- LoadShapeObj: TLoadShapeObj;
-begin
- DSS.ActiveLoadShapeObj := NIL;
- LoadShapeObj := ElementList.First;
- while LoadShapeObj <> NIL do
begin
- if CompareText(LoadShapeObj.Name, Value) = 0 then
- begin
- DSS.ActiveLoadShapeObj := LoadShapeObj;
- Exit;
- end;
- LoadShapeObj := ElementList.Next;
+ ReallocMem(sH, SizeOf(Single) * NumPoints);
+ // Move(Other.sH[0], sH[0], SizeOf(Single) * NumPoints);
+ for i := 1 to NumPoints do
+ sH[i] := Other.sH[Stride * i];
end;
- DoSimpleMsg('LoadShape: "' + Value + '" not Found.', 612);
+
+ UseActual := Other.UseActual;
+ UseMMF := Other.UseMMF;
+ BaseP := Other.BaseP;
+ BaseQ := Other.BaseQ;
+ SetMaxPandQ;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLoadShape.Do2ColCSVFile(const FileName: String);
+procedure Do2ColCSVFile(Obj: TObj; const FileName: String);
// Process 2-column CSV file (3-col if time expected)
var
- F: TBufferedFileStream = nil;
+ F: TStream = nil;
i: Integer;
s: String;
begin
- if DSS.ActiveLoadShapeObj.ExternalMemory then
+ if Obj.ExternalMemory then
begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61102);
+ DoSimpleMsg(Obj.DSS, 'Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61102);
Exit;
end;
try
- F := TBufferedFileStream.Create(FileName, fmOpenRead);
+ F := Obj.DSS.GetROFileStream(FileName);
except
- DoSimpleMsg('Error Opening File: "' + FileName, 613);
+ DoSimpleMsg(Obj.DSS, 'Error opening file: "%s"', [FileName], 613);
FreeAndNil(F);
Exit;
end;
- with DSS.ActiveLoadShapeObj do
+ with Obj do
try
if UseMMF then
begin
FreeAndNil(F);
mmDataSize := NumPoints;
mmFileCmd := 'file=' + FileName + ' column=1'; // Command for P
- if not CreateMMF(mmFileCmd, TMMShapeType.P) then // Creates MMF for the whole file
+ if not CreateMMF(Obj, mmFileCmd, TMMShapeType.P) then // Creates MMF for the whole file
Exit; // CreateMMF throws an error message already
mmViewQ := mmView;
@@ -858,8 +808,8 @@ procedure TLoadShape.Do2ColCSVFile(const FileName: String);
begin
Inc(i);
FSReadln(F, s); // read entire line and parse with AuxParser
- {AuxParser allows commas or white space}
- with AuxParser do
+ // AuxParser allows commas or white space
+ with DSS.AuxParser do
begin
CmdString := s;
if Interval = 0.0 then
@@ -880,40 +830,39 @@ procedure TLoadShape.Do2ColCSVFile(const FileName: String);
except
On E: Exception do
begin
- DoSimpleMsg('Error Processing CSV File: "' + FileName + '. ' + E.Message, 614);
+ DoSimpleMsg(_('Error Processing CSV File: "%s". %s'), [FileName, E.Message], 614);
FreeAndNil(F);
Exit;
end;
end;
end;
-procedure TLoadShape.DoCSVFile(const FileName: String);
+procedure DoCSVFile(Obj: TObj; const FileName: String);
var
- F: TBufferedFileStream = nil;
+ F: TStream = nil;
i: Integer;
s: String;
begin
- if DSS.ActiveLoadShapeObj.ExternalMemory then
+ if Obj.ExternalMemory then
begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61102);
+ DoSimpleMsg(Obj.DSS, 'Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61102);
Exit;
end;
-
try
- F := TBufferedFileStream.Create(FileName, fmOpenRead);
+ F := Obj.DSS.GetROFileStream(FileName);
except
- DoSimpleMsg('Error Opening File: "' + FileName, 613);
+ DoSimpleMsg(Obj.DSS, 'Error opening file: "%s"', [FileName], 613);
FreeAndNil(F);
Exit;
end;
- with DSS.ActiveLoadShapeObj do
+ with Obj do
try
if UseMMF then
begin
FreeAndNil(F);
s := 'file=' + FileName;
- if CreateMMF(s, TMMShapeType.P) then
+ if CreateMMF(Obj, s, TMMShapeType.P) then
Exit; // CreateMMF throws an error message already
LoadFileFeatures(TMMShapeType.P);
@@ -931,8 +880,8 @@ procedure TLoadShape.DoCSVFile(const FileName: String);
begin
Inc(i);
FSReadln(F, s); // read entire line and parse with AuxParser
- {AuxParser allows commas or white space}
- with AuxParser do
+ // AuxParser allows commas or white space
+ with DSS.AuxParser do
begin
CmdString := s;
if Interval = 0.0 then
@@ -951,42 +900,40 @@ procedure TLoadShape.DoCSVFile(const FileName: String);
except
On E: Exception do
begin
- DoSimpleMsg('Error Processing CSV File: "' + FileName + '. ' + E.Message, 614);
+ DoSimpleMsg(_('Error Processing CSV File: "%s". %s'), [FileName, E.Message], 614);
FreeAndNil(F);
Exit;
end;
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLoadShape.DoSngFile(const FileName: String);
+procedure DoSngFile(Obj: TObj; const FileName: String);
var
s: String;
- F: TFileStream;
+ F: TStream = NIL;
Hr, M: Single;
i: Integer;
bytesRead: Int64;
begin
- if DSS.ActiveLoadShapeObj.ExternalMemory then
+ if Obj.ExternalMemory then
begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61102);
+ DoSimpleMsg(Obj.DSS, _('Data cannot be changed for LoadShapes with external memory! Reset the data first.'), 61102);
Exit;
end;
- F := nil;
try
- F := TFileStream.Create(FileName, fmOpenRead);
+ F := Obj.DSS.GetROFileStream(FileName);
except
- DoSimpleMsg('Error Opening File: "' + FileName, 615);
+ DoSimpleMsg(Obj.DSS, 'Error opening file: "%s"', [FileName], 615);
Exit;
end;
- with DSS.ActiveLoadShapeObj do
+ with Obj do
try
if UseMMF then
begin
FreeAndNil(F);
s := 'sngfile=' + FileName;
- if not CreateMMF(s, TMMShapeType.P) then
+ if not CreateMMF(Obj, s, TMMShapeType.P) then
Exit; // CreateMMF throws an error message already
LoadFileFeatures(TMMShapeType.P);
@@ -1055,40 +1002,38 @@ procedure TLoadShape.DoSngFile(const FileName: String);
end;
FreeAndNil(F);
except
- DoSimpleMsg('Error Processing LoadShape File: "' + FileName, 616);
+ DoSimpleMsg('Error Processing LoadShape File: "%s"', [FileName], 616);
if F <> nil then
F.Free();
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLoadShape.DoDblFile(const FileName: String);
+procedure DoDblFile(Obj: TObj; const FileName: String);
var
s: String;
- F: TFileStream;
+ F: TStream = NIL;
i: Integer;
bytesRead: Int64;
begin
- if DSS.ActiveLoadShapeObj.ExternalMemory then
+ if Obj.ExternalMemory then
begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61102);
+ DoSimpleMsg(Obj.DSS, _('Data cannot be changed for LoadShapes with external memory! Reset the data first.'), 61102);
Exit;
end;
- F := nil;
try
- F := TFileStream.Create(FileName, fmOpenRead);
+ F := Obj.DSS.GetROFileStream(FileName);
except
- DoSimpleMsg('Error Opening File: "' + FileName, 617);
+ DoSimpleMsg(Obj.DSS, 'Error opening file: "%s"', [FileName], 617);
Exit;
end;
- with DSS.ActiveLoadShapeObj do
+ with Obj do
try
if UseMMF then
begin
FreeAndNil(F);
s := 'dblfile=' + FileName;
- if not CreateMMF(s, TMMShapeType.P) then
+ if not CreateMMF(Obj, s, TMMShapeType.P) then
Exit; // CreateMMF throws an error message already
LoadFileFeatures(TMMShapeType.P);
@@ -1124,19 +1069,14 @@ procedure TLoadShape.DoDblFile(const FileName: String);
if F <> nil then
F.Free();
except
- DoSimpleMsg('Error Processing LoadShape File: "' + FileName, 618);
+ DoSimpleMsg('Error Processing LoadShape File: "%s"', [FileName], 618);
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TLoadShape Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TLoadShapeObj.Create(ParClass: TDSSClass; const LoadShapeName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(LoadShapeName);
+ Name := AnsiLowerCase(LoadShapeName);
DSSObjType := ParClass.DSSClassType;
ExternalMemory := False;
@@ -1170,15 +1110,14 @@ constructor TLoadShapeObj.Create(ParClass: TDSSClass; const LoadShapeName: Strin
mmFile := 0;
mmQFile := 0;
- mmViewLen := 1000; // 1kB by default, it may change for not missing a row
-
- ArrayPropertyIndex := 0;
-
- InitPropertyValues(0);
+ csvfile := '';
+ dblfile := '';
+ sngfile := '';
+ pqcsvfile := '';
+ mmViewLen := 1000; // 1kB by default, it may change for not missing a row
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TLoadShapeObj.Destroy;
begin
if not ExternalMemory then
@@ -1220,7 +1159,6 @@ destructor TLoadShapeObj.Destroy;
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function TLoadShapeObj.GetMultAtHour(hr: Double): Complex;
// This function returns a multiplier for the given hour.
// If no points exist in the curve, the result is 1.0
@@ -1371,7 +1309,7 @@ function TLoadShapeObj.GetMultAtHour(hr: Double): Complex;
end;
end;
-// If we fall through the loop, just use last value
+ // If we fall through the loop, just use last value
LastValueAccessed := FNumPoints - 2;
Result.re := dP[Stride * LastValueAccessed];
if Assigned(dQ) then
@@ -1380,7 +1318,6 @@ function TLoadShapeObj.GetMultAtHour(hr: Double): Complex;
Result.im := Set_Result_im(Result.re);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TLoadShapeObj.Normalize;
// normalize this load shape
var
@@ -1425,9 +1362,9 @@ procedure TLoadShapeObj.Normalize;
end;
begin
- if UseMMF or ExternalMemory then //TODO: disallow MMF?
+ if UseMMF or ExternalMemory then //TODO: disallow MMF?
begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory or memory-mapped files! Reset the data first.', 61102);
+ DoSimpleMsg(_('Data cannot be changed for LoadShapes with external memory or memory-mapped files! Reset the data first.'), 61102);
Exit;
end;
@@ -1453,7 +1390,6 @@ procedure TLoadShapeObj.Normalize;
UseActual := FALSE; // not likely that you would want to use the actual if you normalized it.
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TLoadShapeObj.CalcMeanandStdDev;
begin
if UseMMF or ExternalMemory then
@@ -1476,16 +1412,13 @@ procedure TLoadShapeObj.CalcMeanandStdDev;
CurveMeanAndStdDevSingle(PSingleArray(sP), PSingleArray(sH), FNumPoints, FMean, FStdDev);
End;
end;
- PropertyValue[5] := Format('%.8g', [FMean]);
- PropertyValue[6] := Format('%.8g', [FStdDev]);
FStdDevCalculated := TRUE;
- { No Action is taken on Q multipliers}
+ // No Action is taken on Q multipliers
end;
function TLoadShapeObj.Get_Interval: Double;
begin
-
if Interval > 0.0 then
Result := Interval
else
@@ -1500,8 +1433,6 @@ function TLoadShapeObj.Get_Interval: Double;
else
Result := 0.0;
end;
-
-
end;
function TLoadShapeObj.Get_Mean: Double;
@@ -1573,7 +1504,6 @@ function TLoadShapeObj.QMult(i: Integer; var m: Double): Boolean;
m := 0.0;
end;
-
function TLoadShapeObj.Hour(i: Integer): Double;
begin
dec(i);
@@ -1602,34 +1532,12 @@ function TLoadShapeObj.Hour(i: Integer): Double;
end;
end;
-
-procedure TLoadShapeObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
-
-end;
-
function TLoadShapeObj.GetPropertyValue(Index: Integer): String;
begin
Result := '';
case Index of
- 1:
- Result := IntToStr(FNumPoints);
- 2:
- Result := Format('%.8g', [Interval]);
- 3, 19:
+ ord(TProp.mult), ord(TProp.Pmult):
begin
if UseMMF then
begin
@@ -1641,16 +1549,12 @@ function TLoadShapeObj.GetPropertyValue(Index: Integer): String;
else if sP <> NIL then
Result := GetDSSArray_Single(FNumPoints, pSingleArray(sP));
end;
- 4:
+ ord(TProp.hour):
if dH <> NIL then
Result := GetDSSArray_Real(FNumPoints, pDoubleArray(dH))
else if sH <> NIL then
Result := GetDSSArray_Single(FNumPoints, pSingleArray(sH));
- 5:
- Result := Format('%.8g', [Mean]);
- 6:
- Result := Format('%.8g', [StdDev]);
- 11:
+ ord(TProp.qmult):
begin
if UseMMF then
begin
@@ -1662,63 +1566,12 @@ function TLoadShapeObj.GetPropertyValue(Index: Integer): String;
else if Assigned(sQ) then
Result := GetDSSArray_Single(FNumPoints, pSingleArray(sQ));
end;
- 12:
- Result := StrYorN(UseActual);
- 13:
- Result := Format('%.8g', [MaxP]);
- 14:
- Result := Format('%.8g', [MaxQ]);
- 15:
- Result := Format('%.8g', [Interval * 3600.0]);
- 16:
- Result := Format('%.8g', [Interval * 60.0]);
- 17:
- Result := Format('%.8g', [BaseP]);
- 18:
- Result := Format('%.8g', [BaseQ]);
- 21:
- if UseMMF then
- Result := 'Yes'
- else
- Result := 'No';
else
Result := inherited GetPropertyValue(index);
end;
-
-end;
-
-procedure TLoadShapeObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := '0'; // Number of points to expect
- PropertyValue[2] := '1'; // default = 1.0 hr;
- PropertyValue[3] := ''; // vector of multiplier values
- PropertyValue[4] := ''; // vextor of hour values
- PropertyValue[5] := '0'; // set the mean (otherwise computed)
- PropertyValue[6] := '0'; // set the std dev (otherwise computed)
- PropertyValue[7] := ''; // Switch input to a csvfile
- PropertyValue[8] := ''; // switch input to a binary file of singles
- PropertyValue[9] := ''; // switch input to a binary file of singles
- PropertyValue[10] := ''; // action option .
- PropertyValue[11] := ''; // Qmult.
- PropertyValue[12] := 'No';
- PropertyValue[13] := '0';
- PropertyValue[14] := '0';
- PropertyValue[15] := '3600'; // seconds
- PropertyValue[16] := '60'; // minutes
- PropertyValue[17] := '0';
- PropertyValue[18] := '0';
- PropertyValue[19] := ''; // same as 3
- PropertyValue[20] := ''; // switch input to csv file of P, Q pairs
- PropertyValue[21] := 'No'; // memory mapped load shape
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
end;
procedure TLoadShapeObj.SaveToDblFile;
-
var
myDBL: Double;
F: TFileStream = nil;
@@ -1727,54 +1580,54 @@ procedure TLoadShapeObj.SaveToDblFile;
begin
//TODO: disallow when ExternalMemory?
UseFloat64;
- if Assigned(dP) then
+ if not Assigned(dP) then
+ begin
+ DoSimpleMsg('%s P multipliers not defined.', [FullName], 622);
+ Exit;
+ end;
+
+ try
+ FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s_P.dbl', [Name]);
+ F := TBufferedFileStream.Create(FName, fmCreate);
+ if UseMMF then
+ begin
+ for i := 1 to NumPoints do
+ begin
+ myDBL := InterpretDblArrayMMF(DSS, mmView, mmFileType, mmColumn, i, mmLineLen);
+ F.Write(myDBL, sizeOf(myDBL));
+ end;
+ end
+ else
+ begin
+ for i := 1 to NumPoints do
+ F.Write(dP[Stride * i], sizeOf(Double));
+ end;
+ DSS.GlobalResult := 'mult=[dblfile=' + FName + ']';
+ finally
+ FreeAndNil(F);
+ end;
+
+ if Assigned(dQ) then
begin
try
- FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s_P.dbl', [Name]);
- F := TFileStream.Create(FName, fmCreate);
+ FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s_Q.dbl', [Name]);
+ F := TBufferedFileStream.Create(FName, fmCreate);
if UseMMF then
begin
for i := 1 to NumPoints do
begin
- myDBL := InterpretDblArrayMMF(DSS, mmView, mmFileType, mmColumn, i, mmLineLen);
+ myDBL := InterpretDblArrayMMF(DSS, mmViewQ, mmFileTypeQ, mmColumnQ, i, mmLineLenQ);
F.Write(myDBL, sizeOf(myDBL));
end;
end
else
- begin
for i := 1 to NumPoints do
- F.Write(dP[Stride * i], sizeOf(Double));
- end;
- DSS.GlobalResult := 'mult=[dblfile=' + FName + ']';
+ F.Write(dQ[Stride * i], sizeOf(Double));
+ AppendGlobalResult(DSS, ' Qmult=[dblfile=' + FName + ']');
finally
FreeAndNil(F);
end;
-
- if Assigned(dQ) then
- begin
- try
- FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s_Q.dbl', [Name]);
- F := TFileStream.Create(FName, fmCreate);
- if UseMMF then
- begin
- for i := 1 to NumPoints do
- begin
- myDBL := InterpretDblArrayMMF(DSS, mmViewQ, mmFileTypeQ, mmColumnQ, i, mmLineLenQ);
- F.Write(myDBL, sizeOf(myDBL));
- end;
- end
- else
- for i := 1 to NumPoints do
- F.Write(dQ[Stride * i], sizeOf(Double));
- AppendGlobalResult(DSS, ' Qmult=[dblfile=' + FName + ']');
- finally
- FreeAndNil(F);
- end;
- end;
-
- end
- else
- DoSimpleMsg('Loadshape.' + Name + ' P multipliers not defined.', 622);
+ end;
end;
procedure TLoadShapeObj.SaveToSngFile;
@@ -1785,45 +1638,46 @@ procedure TLoadShapeObj.SaveToSngFile;
Temp: Single;
begin
UseFloat64;
- if Assigned(dP) then
+ if not Assigned(dP) then
+ begin
+ DoSimpleMsg('%s P multipliers not defined.', [FullName], 623);
+ Exit;
+ end;
+
+ try
+ FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s_P.sng', [Name]);
+ F := TBufferedFileStream.Create(FName, fmCreate);
+ for i := 1 to NumPoints do
+ begin
+ if UseMMF then
+ Temp := InterpretDblArrayMMF(DSS, mmView, mmFileType, mmColumn, i, mmLineLen)
+ else
+ Temp := dP[Stride * i];
+ F.Write(Temp, SizeOf(Temp));
+ end;
+ DSS.GlobalResult := 'mult=[sngfile=' + FName + ']';
+ finally
+ FreeAndNil(F);
+ end;
+
+ if Assigned(dQ) then
begin
try
- FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s_P.sng', [Name]);
- F := TFileStream.Create(FName, fmCreate);
+ FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s_Q.sng', [Name]);
+ F := TBufferedFileStream.Create(FName, fmCreate);
for i := 1 to NumPoints do
begin
if UseMMF then
- Temp := InterpretDblArrayMMF(DSS, mmView, mmFileType, mmColumn, i, mmLineLen)
+ Temp := InterpretDblArrayMMF(DSS, mmViewQ, mmFileTypeQ, mmColumnQ, i, mmLineLenQ)
else
- Temp := dP[Stride * i];
+ Temp := dQ[Stride * i];
F.Write(Temp, SizeOf(Temp));
end;
- DSS.GlobalResult := 'mult=[sngfile=' + FName + ']';
+ AppendGlobalResult(DSS, ' Qmult=[sngfile=' + FName + ']');
finally
FreeAndNil(F);
end;
-
- if Assigned(dQ) then
- begin
- try
- FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s_Q.sng', [Name]);
- F := TFileStream.Create(FName, fmCreate);
- for i := 1 to NumPoints do
- begin
- if UseMMF then
- Temp := InterpretDblArrayMMF(DSS, mmViewQ, mmFileTypeQ, mmColumnQ, i, mmLineLenQ)
- else
- Temp := dQ[Stride * i];
- F.Write(Temp, SizeOf(Temp));
- end;
- AppendGlobalResult(DSS, ' Qmult=[sngfile=' + FName + ']');
- finally
- FreeAndNil(F);
- end;
- end;
- end
- else
- DoSimpleMsg('Loadshape.' + Name + ' P multipliers not defined.', 623);
+ end;
end;
procedure TLoadShapeObj.SetMaxPandQ;
@@ -1877,18 +1731,12 @@ procedure TLoadShapeObj.SetDataPointers(HoursPtr: PDouble; PMultPtr: PDouble; QM
begin
if not ExternalMemory then
begin
- if Assigned(dH) then
- ReallocMem(dH, 0);
- if Assigned(dP) then
- ReallocMem(dP, 0);
- if Assigned(dQ) then
- ReallocMem(dQ, 0);
- if Assigned(sH) then
- ReallocMem(sH, 0);
- if Assigned(sP) then
- ReallocMem(sP, 0);
- if Assigned(sQ) then
- ReallocMem(sQ, 0);
+ ReallocMem(dH, 0);
+ ReallocMem(dP, 0);
+ ReallocMem(dQ, 0);
+ ReallocMem(sH, 0);
+ ReallocMem(sP, 0);
+ ReallocMem(sQ, 0);
end;
sH := nil;
sP := nil;
@@ -1915,18 +1763,12 @@ procedure TLoadShapeObj.SetDataPointersSingle(HoursPtr: PSingle; PMultPtr: PSing
begin
if not ExternalMemory then
begin
- if Assigned(dH) then
- ReallocMem(dH, 0);
- if Assigned(dP) then
- ReallocMem(dP, 0);
- if Assigned(dQ) then
- ReallocMem(dQ, 0);
- if Assigned(sH) then
- ReallocMem(sH, 0);
- if Assigned(sP) then
- ReallocMem(sP, 0);
- if Assigned(sQ) then
- ReallocMem(sQ, 0);
+ ReallocMem(dH, 0);
+ ReallocMem(dP, 0);
+ ReallocMem(dQ, 0);
+ ReallocMem(sH, 0);
+ ReallocMem(sP, 0);
+ ReallocMem(sQ, 0);
end;
dH := nil;
dP := nil;
@@ -1953,15 +1795,15 @@ procedure TLoadShapeObj.UseFloat32;
var
i: Integer;
begin
- if DSS.ActiveLoadShapeObj.UseMMF then
+ if UseMMF then
begin
- DoSimpleMsg('Data cannot be toggled to 32-bit floats when memory-mapping is enabled.', 61106);
+ DoSimpleMsg(_('Data cannot be toggled to 32-bit floats when memory-mapping is enabled.'), 61106);
Exit;
end;
- if DSS.ActiveLoadShapeObj.ExternalMemory then
+ if ExternalMemory then
begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory! Reset the data first.', 61103);
+ DoSimpleMsg(_('Data cannot be changed for LoadShapes with external memory! Reset the data first.'), 61103);
Exit;
end;
@@ -1989,7 +1831,6 @@ procedure TLoadShapeObj.UseFloat32;
FreeMem(dQ);
dQ := nil;
end;
-
end;
procedure TLoadShapeObj.UseFloat64;
@@ -1998,34 +1839,43 @@ procedure TLoadShapeObj.UseFloat64;
begin
if UseMMF then // data has to be already using float64, we can skip this
Exit;
-
+
if ExternalMemory then
begin
- DoSimpleMsg('Data cannot be changed for LoadShapes with external memory or memory-mapped files! Reset the data first.', 61104);
+ DoSimpleMsg(_('Data cannot be changed for LoadShapes with external memory or memory-mapped files! Reset the data first.'), 61104);
Exit;
end;
if Assigned(sH) then
begin
- ReallocMem(dH, FNumPoints * SizeOf(Double));
- for i := 0 to FNumPoints - 1 do
- dH[i] := sH[i];
+ if dH = NIL then
+ begin
+ ReallocMem(dH, FNumPoints * SizeOf(Double));
+ for i := 0 to FNumPoints - 1 do
+ dH[i] := sH[i];
+ end;
FreeMem(sH);
sH := nil;
end;
if Assigned(sP) then
begin
- ReallocMem(dP, FNumPoints * SizeOf(Double));
- for i := 0 to FNumPoints - 1 do
- dP[i] := sP[i];
+ if dP = NIL then
+ begin
+ ReallocMem(dP, FNumPoints * SizeOf(Double));
+ for i := 0 to FNumPoints - 1 do
+ dP[i] := sP[i];
+ end;
FreeMem(sP);
sP := nil;
end;
if Assigned(sQ) then
begin
- ReallocMem(dQ, FNumPoints * SizeOf(Double));
- for i := 0 to FNumPoints - 1 do
- dQ[i] := sQ[i];
+ if dQ = NIL then
+ begin
+ ReallocMem(dQ, FNumPoints * SizeOf(Double));
+ for i := 0 to FNumPoints - 1 do
+ dQ[i] := sQ[i];
+ end;
FreeMem(sQ);
sQ := nil;
end;
@@ -2038,7 +1888,7 @@ function TLoadShapeObj.GetMultAtHourSingle(hr: Double): Complex;
poffset: Int64; // previous index including stride
function Set_Result_im(const realpart: Double): Double;
- {Set imaginary part of Result when Qmultipliers not defined}
+ // Set imaginary part of Result when Qmultipliers not defined
begin
if UseActual then
Set_Result_im := 0.0 // if actual, assume zero
@@ -2133,4 +1983,12 @@ function TLoadShapeObj.GetMultAtHourSingle(hr: Double): Complex;
Result.im := Set_Result_im(Result.re);
end;
+procedure TLoadShapeObj.SaveWrite(F: TFileStream);
+begin
+ PrpSequence[ord(TProp.npts)] := -999; // make sure Npts prop is first
+ inherited SaveWrite(F);
+end;
+
+finalization
+ ActionEnum.Free;
end.
diff --git a/src/General/NamedObject.pas b/src/General/NamedObject.pas
index 66173d7c2..5220caf58 100644
--- a/src/General/NamedObject.pas
+++ b/src/General/NamedObject.pas
@@ -16,26 +16,19 @@ interface
TNamedObject = class(TObject)
PROTECTED
pUuid: ^TUuid; // compliant to RFC 4122, v4
- PRIVATE
- PName: String; // path name, or class name for DSS
LName: String; // localName is unique within a class, like the old FName
- DName: String; // for optional display, does not have to be unique
-
- function Get_QualifiedName: String;
- function Get_DisplayName: String;
- procedure Set_DisplayName(const Value: String);
+ PRIVATE
function Get_UUID: TUuid;
function Get_ID: String;
function Get_CIM_ID: String;
procedure Set_UUID(const Value: TUuid);
PUBLIC
- constructor Create(ClassName: String);
- destructor Destroy; OVERRIDE;
+ DisplayName: String; // TODO: remove
- property DSSClassName: String READ PName WRITE PName;
+ constructor Create(ClassName_: String);
+ destructor Destroy; OVERRIDE;
+
property LocalName: String READ LName WRITE LName;
- property QualifiedName: String READ Get_QualifiedName;
- property DisplayName: String READ Get_DisplayName WRITE Set_DisplayName;
property UUID: TUuid READ Get_UUID WRITE Set_UUID;
property ID: String READ Get_ID;
property CIM_ID: String READ Get_CIM_ID;
@@ -77,12 +70,11 @@ function UUIDToCIMString(UUID: TUuid): String;
Result := '_' + MidStr(s, 2, Length(s) - 2);
end;
-constructor TNamedObject.Create(ClassName: String);
+constructor TNamedObject.Create(ClassName_: String);
begin
inherited Create;
- PName := ClassName;
LName := '';
- DName := '';
+ DisplayName := '';
pUuid := NIL;
end;
@@ -93,25 +85,6 @@ destructor TNamedObject.Destroy;
inherited Destroy;
end;
-
-procedure TNamedObject.Set_DisplayName(const Value: String);
-begin
- DName := Value;
-end;
-
-function TNamedObject.Get_DisplayName: String;
-begin
- if DName = '' then
- Result := PName + '_' + LName
- else
- Result := DName;
-end;
-
-function TNamedObject.Get_QualifiedName: String;
-begin
- Result := PName + '.' + LName
-end;
-
procedure TNamedObject.Set_UUID(const Value: TUuid);
begin
if pUuid = NIL then
diff --git a/src/General/OHLineConstants.pas b/src/General/OHLineConstants.pas
index f8b8c5e36..75fd993b8 100644
--- a/src/General/OHLineConstants.pas
+++ b/src/General/OHLineConstants.pas
@@ -7,19 +7,17 @@
----------------------------------------------------------
}
-{Manages the geometry data and calculates the impedance matrices for an overhead line}
-
+// Manages the geometry data and calculates the impedance matrices for an overhead line
interface
uses
Arraydef,
Ucmatrix,
- Ucomplex,
+ UComplex, DSSUcomplex,
LineUnits,
LineConstants;
type
-
TOHLineConstants = class(TLineConstants)
PRIVATE
@@ -42,6 +40,4 @@ destructor TOHLineConstants.Destroy;
inherited;
end;
-initialization
-
end.
diff --git a/src/General/PriceShape.pas b/src/General/PriceShape.pas
index b49c53ee9..734cddfe5 100644
--- a/src/General/PriceShape.pas
+++ b/src/General/PriceShape.pas
@@ -6,47 +6,37 @@
All rights reserved.
----------------------------------------------------------
}
-
-{ 2-16-2011 Converted from TempShape.
- Price shapes would generally be defined to correspond to loadshapes
-
-}
-
interface
-{The PriceShape object is a general DSS object used by all circuits
- as a reference for obtaining yearly, daily, and other Price shapes.
-
- The values are set by the normal New and Edit procedures for any DSS object.
-
- The values may be retrieved by setting the Code Property in the PriceShape Class.
- This sets the active PriceShape object to be the one referenced by the Code Property;
-
- Then the values of that code can be retrieved via the public variables. Or you
- can pick up the ActivePriceShapeObj object and save the direct reference to the object.
-
- PriceShapes default to fixed interval data (like Loadshapes). If the Interval is specified to be 0.0,
- then both time and price data are expected. If the Interval is greater than 0.0,
- the user specifies only the prices. The Hour command is ignored and the files are
- assumed to contain only the price data.
-
- The Interval may also be specified in seconds (sinterval) or minutes (minterval).
-
- The user may place the data in CSV or binary files as well as passing through the
- command interface. Obviously, for large amounts of data such as 8760 load curves, the
- command interface is cumbersome. CSV files are text separated by commas, one interval to a line.
- There are two binary formats permitted: 1) a file of Singles; 2) a file of Doubles.
-
- For fixed interval data, only the price values are expected. Therefore, the CSV format would
- contain only one number per line. The two binary formats are packed.
-
- For variable interval data, (hour, price) pairs are expected in both formats.
-
- The Mean and Std Deviation are automatically computed upon demand when new series of points is entered.
-
-
-
- }
+// The PriceShape object is a general DSS object used by all circuits
+// as a reference for obtaining yearly, daily, and other Price shapes.
+//
+// The values are set by the normal New and Edit procedures for any DSS object.
+//
+// The values may be retrieved by setting the Code Property in the PriceShape Class.
+// This sets the active PriceShape object to be the one referenced by the Code Property;
+//
+// Then the values of that code can be retrieved via the public variables. Or you
+// can pick up the ActivePriceShapeObj object and save the direct reference to the object.
+//
+// PriceShapes default to fixed interval data (like Loadshapes). If the Interval is specified to be 0.0,
+// then both time and price data are expected. If the Interval is greater than 0.0,
+// the user specifies only the prices. The Hour command is ignored and the files are
+// assumed to contain only the price data.
+//
+// The Interval may also be specified in seconds (sinterval) or minutes (minterval).
+//
+// The user may place the data in CSV or binary files as well as passing through the
+// command interface. Obviously, for large amounts of data such as 8760 load curves, the
+// command interface is cumbersome. CSV files are text separated by commas, one interval to a line.
+// There are two binary formats permitted: 1) a file of Singles; 2) a file of Doubles.
+//
+// For fixed interval data, only the price values are expected. Therefore, the CSV format would
+// contain only one number per line. The two binary formats are packed.
+//
+// For variable interval data, (hour, price) pairs are expected in both formats.
+//
+// The Mean and Std Deviation are automatically computed upon demand when new series of points is entered.
uses
Classes,
@@ -56,529 +46,292 @@ interface
Arraydef;
type
+{$SCOPEDENUMS ON}
+ TPriceShapeProp = (
+ INVALID = 0,
+ npts = 1, // Number of points to expect
+ interval = 2, // default = 1.0;
+ price = 3, // vector of price values
+ hour = 4, // vector of hour values
+ mean = 5, // set the mean Price (otherwise computed)
+ stddev = 6, // set the std dev of the Price (otherwise computed)
+ csvfile = 7, // Switch input to a csvfile
+ sngfile = 8, // switch input to a binary file of singles
+ dblfile = 9, // switch input to a binary file of doubles
+ sinterval = 10, // Interval in seconds
+ minterval = 11, // Interval in minutes
+ action = 12
+ );
+{$SCOPEDENUMS OFF}
TPriceShape = class(TDSSClass)
- PRIVATE
-
- function Get_Code: String; // Returns active PriceShape string
- procedure Set_Code(const Value: String); // sets the active PriceShape
-
- procedure DoCSVFile(const FileName: String);
- procedure DoSngFile(const FileName: String);
- procedure DoDblFile(const FileName: String);
PROTECTED
- procedure DefineProperties;
- function MakeLike(const ShapeName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
function Find(const ObjName: String; const ChangeActive: Boolean=True): Pointer; OVERRIDE; // Find an obj of this class by name
-
- // Set this property to point ActivePriceShapeObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
-
end;
TPriceShapeObj = class(TDSSObject)
PRIVATE
LastValueAccessed,
FNumPoints: Integer; // Number of points in curve
- ArrayPropertyIndex: Integer;
FStdDevCalculated: Boolean;
FMean,
FStdDev: Double;
function Get_Interval: Double;
- procedure Set_NumPoints(const Value: Integer);
procedure SaveToDblFile;
procedure SaveToSngFile;
procedure CalcMeanandStdDev;
function Get_Mean: Double;
function Get_StdDev: Double;
- procedure Set_Mean(const Value: Double);
- procedure Set_StdDev(const Value: Double); // Normalize the curve presently in memory
-
PUBLIC
-
Interval: Double; //=0.0 then random interval (hr)
Hours, // Time values (hr) if Interval > 0.0 Else nil
PriceValues: pDoubleArray; // Prices
+ csvfile, dblfile, sngfile: String;
constructor Create(ParClass: TDSSClass; const PriceShapeName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
function GetPrice(hr: Double): Double; // Get Prices at specified time, hr
function Price(i: Integer): Double; // get Prices by index
function Hour(i: Integer): Double; // get hour corresponding to point index
-
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
- property NumPoints: Integer READ FNumPoints WRITE Set_NumPoints;
+ property NumPoints: Integer READ FNumPoints;
property PresentInterval: Double READ Get_Interval;
- property Mean: Double READ Get_Mean WRITE Set_Mean;
- property StdDev: Double READ Get_StdDev WRITE Set_StdDev;
-
+ property Mean: Double READ Get_Mean;
+ property StdDev: Double READ Get_StdDev;
end;
implementation
uses
- ParserDel,
+ BufStream,
DSSClassDefs,
DSSGlobals,
Sysutils,
MathUtil,
Utilities,
Math,
- BufStream,
DSSPointerList,
DSSHelper,
DSSObjectHelper,
TypInfo;
+type
+ TObj = TPriceShapeObj;
+ TProp = TPriceShapeProp;
+{$PUSH}
+{$Z4} // keep enums as int32 values
+ TPriceShapeAction = (
+ DblSave = 0,
+ SngSave = 1
+ );
+{$POP}
+
const
- NumPropsThisClass = 12;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
+ ActionEnum: TDSSEnum;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TPriceShape.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TPriceShape.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'PriceShape';
- DSSClassType := DSS_OBJECT;
-
- ActiveElement := 0;
-
- DefineProperties;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ ActionEnum := TDSSEnum.Create('PriceShape: Action', True, 1, 1,
+ ['DblSave', 'SngSave'],
+ [ord(TPriceShapeAction.DblSave), ord(TPriceShapeAction.SngSave)]);
+ end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, DSS_OBJECT, 'PriceShape');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TPriceShape.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TPriceShape.DefineProperties;
-begin
-
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
- PropertyName[1] := 'npts'; // Number of points to expect
- PropertyName[2] := 'interval'; // default = 1.0;
- PropertyName[3] := 'price'; // vector of price values
- PropertyName[4] := 'hour'; // vector of hour values
- PropertyName[5] := 'mean'; // set the mean Price (otherwise computed)
- PropertyName[6] := 'stddev'; // set the std dev of the Price (otherwise computed)
- PropertyName[7] := 'csvfile'; // Switch input to a csvfile
- PropertyName[8] := 'sngfile'; // switch input to a binary file of singles
- PropertyName[9] := 'dblfile'; // switch input to a binary file of singles
- PropertyName[10] := 'sinterval'; // Interval in seconds
- PropertyName[11] := 'minterval'; // Interval in minutes
- PropertyName[12] := 'action'; //
-
- // define Property help values
-
- PropertyHelp[1] := 'Max number of points to expect in price shape vectors. This gets reset to the number of Price values ' +
- 'found if less than specified.'; // Number of points to expect
- PropertyHelp[2] := 'Time interval for fixed interval data, hrs. Default = 1. ' +
- 'If Interval = 0 then time data (in hours) may be at irregular intervals and time value must be specified using either the Hour property or input files. ' +
- 'Then values are interpolated when Interval=0, but not for fixed interval data. ' + CRLF + CRLF +
- 'See also "sinterval" and "minterval".'; // default = 1.0;
- PropertyHelp[3] := 'Array of Price values. Units should be compatible with the object using the data. ' +
- 'You can also use the syntax: ' + CRLF +
- 'Price = (file=filename) !for text file one value per line' + CRLF +
- 'Price = (dblfile=filename) !for packed file of doubles' + CRLF +
- 'Price = (sngfile=filename) !for packed file of singles ' + CRLF + CRLF +
- 'Note: this property will reset Npts if the number of values in the files are fewer.'; // vextor of hour values
- PropertyHelp[4] := 'Array of hour values. Only necessary to define this property for variable interval data.' +
- ' If the data are fixed interval, do not use this property. ' +
- 'You can also use the syntax: ' + CRLF +
- 'hour = (file=filename) !for text file one value per line' + CRLF +
- 'hour = (dblfile=filename) !for packed file of doubles' + CRLF +
- 'hour = (sngfile=filename) !for packed file of singles '; // vextor of hour values
- PropertyHelp[5] := 'Mean of the Price curve values. This is computed on demand the first time a ' +
- 'value is needed. However, you may set it to another value independently. ' +
- 'Used for Monte Carlo load simulations.'; // set the mean (otherwise computed)
- PropertyHelp[6] := 'Standard deviation of the Prices. This is computed on demand the first time a ' +
- 'value is needed. However, you may set it to another value independently.' +
- 'Is overwritten if you subsequently read in a curve' + CRLF + CRLF +
- 'Used for Monte Carlo load simulations.'; // set the std dev (otherwise computed)
- PropertyHelp[7] := 'Switch input of Price curve data to a csv file ' +
- 'containing (hour, Price) points, or simply (Price) values for fixed time interval data, one per line. ' +
- 'NOTE: This action may reset the number of points to a lower value.'; // Switch input to a csvfile
- PropertyHelp[8] := 'Switch input of Price curve data to a binary file of singles ' +
- 'containing (hour, Price) points, or simply (Price) values for fixed time interval data, packed one after another. ' +
- 'NOTE: This action may reset the number of points to a lower value.'; // switch input to a binary file of singles
- PropertyHelp[9] := 'Switch input of Price curve data to a binary file of doubles ' +
- 'containing (hour, Price) points, or simply (Price) values for fixed time interval data, packed one after another. ' +
- 'NOTE: This action may reset the number of points to a lower value.'; // switch input to a binary file of singles
- PropertyHelp[10] := 'Specify fixed interval in SECONDS. Alternate way to specify Interval property.';
- PropertyHelp[11] := 'Specify fixed interval in MINUTES. Alternate way to specify Interval property.';
- PropertyHelp[12] := '{DblSave | SngSave} After defining Price curve data... ' +
- 'Setting action=DblSave or SngSave will cause the present "Price" values to be written to ' +
- 'either a packed file of double or single. The filename is the PriceShape name. '; // Action
-
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TPriceShape.NewObject(const ObjName: String): Integer;
-begin
- // create a new object of this class and add to list
- DSS.ActiveDSSObject := TPriceShapeObj.Create(Self, ObjName);
- Result := AddObjectToList(DSS.ActiveDSSObject);
-end;
-
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TPriceShape.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
+procedure DoAction(Obj: TObj; action: TPriceShapeAction);
begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActivePriceShapeObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActivePriceShapeObj;
-
- with DSS.ActivePriceShapeObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
-
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 58610);
- 1:
- NumPoints := Parser.Intvalue;
- 2:
- Interval := Parser.DblValue;
- 3:
- begin
- ReAllocmem(PriceValues, Sizeof(PriceValues^[1]) * NumPoints);
- // Allow possible Resetting (to a lower value) of num points when specifying Prices not Hours
- NumPoints := InterpretDblArray(Param, NumPoints, PriceValues); // Parser.ParseAsVector(Npts, Prices);
- end;
- 4:
- begin
- ReAllocmem(Hours, Sizeof(Hours^[1]) * NumPoints);
- NumPoints := InterpretDblArray(Param, NumPoints, Hours); // Parser.ParseAsVector(Npts, Hours);
- end;
- 5:
- Mean := Parser.DblValue;
- 6:
- StdDev := Parser.DblValue;
- 7:
- DoCSVFile(AdjustInputFilePath(Param));
- 8:
- DoSngFile(AdjustInputFilePath(Param));
- 9:
- DoDblFile(AdjustInputFilePath(Param));
- 10:
- Interval := Parser.DblValue / 3600.0; // Convert seconds to hr
- 11:
- Interval := Parser.DblValue / 60.0; // Convert minutes to hr
- 12:
- case lowercase(Param)[1] of
- 'd':
- SaveToDblFile;
- 's':
- SaveToSngFile;
- end;
- else
- // Inherited parameters
- ClassEdit(DSS.ActivePriceShapeObj, ParamPointer - NumPropsThisClass)
- end;
-
- case ParamPointer of
- 3, 7, 8, 9:
- begin
- FStdDevCalculated := FALSE; // now calculated on demand
- ArrayPropertyIndex := ParamPointer;
- NumPoints := FNumPoints; // Keep Properties in order for save command
- end;
-
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end; {While}
-
- end; {WITH}
+ case action of
+ TPriceShapeAction.DblSave:
+ Obj.SaveToDblFile;
+ TPriceShapeAction.SngSave:
+ Obj.SaveToSngFile;
+ end;
end;
-function TPriceShape.Find(const ObjName: String; const ChangeActive: Boolean): Pointer;
+function GetMean(obj: TObj): Double;
begin
- if (Length(ObjName) = 0) or (CompareText(ObjName, 'none') = 0) then
- Result := NIL
- else
- Result := inherited Find(ObjName, ChangeActive);
+ Result := obj.Mean;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TPriceShape.MakeLike(const ShapeName: String): Integer;
-var
- OtherPriceShape: TPriceShapeObj;
- i: Integer;
+function GetStdDev(obj: TObj): Double;
begin
- Result := 0;
- {See if we can find this line code in the present collection}
- OtherPriceShape := Find(ShapeName);
- if OtherPriceShape <> NIL then
- with DSS.ActivePriceShapeObj do
- begin
- NumPoints := OtherPriceShape.NumPoints;
- Interval := OtherPriceShape.Interval;
- ReallocMem(PriceValues, SizeOf(PriceValues^[1]) * NumPoints);
- for i := 1 to NumPoints do
- PriceValues^[i] := OtherPriceShape.PriceValues^[i];
- if Interval > 0.0 then
- ReallocMem(Hours, 0)
- else
- begin
- ReallocMem(Hours, SizeOf(Hours^[1]) * NumPoints);
- for i := 1 to NumPoints do
- Hours^[i] := OtherPriceShape.Hours^[i];
- end;
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherPriceShape.PropertyValue[i];
- end
- else
- DoSimpleMsg('Error in PriceShape MakeLike: "' + ShapeName + '" Not Found.', 58611);
-
-
+ Result := obj.StdDev;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TPriceShape.Get_Code: String; // Returns active line code string
-var
- PriceShapeObj: TPriceShapeObj;
+procedure TPriceShape.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
- PriceShapeObj := ElementList.Active;
- Result := PriceShapeObj.Name;
-
+ Numproperties := NumPropsThisClass;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // strings
+ PropertyType[ord(TProp.csvfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.csvfile)] := ptruint(@obj.csvfile);
+ PropertyFlags[ord(TProp.csvfile)] := [TPropertyFlag.IsFilename];
+
+ PropertyType[ord(TProp.dblfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.dblfile)] := ptruint(@obj.dblfile);
+ PropertyFlags[ord(TProp.dblfile)] := [TPropertyFlag.IsFilename];
+
+ PropertyType[ord(TProp.sngfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.sngfile)] := ptruint(@obj.sngfile);
+ PropertyFlags[ord(TProp.sngfile)] := [TPropertyFlag.IsFilename];
+
+ // integer properties
+ PropertyType[ord(TProp.Npts)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Npts)] := ptruint(@obj.FNumPoints);
+
+ // advanced doubles
+ PropertyOffset[ord(TProp.sinterval)] := ptruint(@obj.Interval);
+ PropertyScale[ord(TProp.sinterval)] := 1 / 3600.0;
+ PropertyFlags[ord(TProp.sinterval)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.sinterval)] := ord(TProp.interval);
+
+ PropertyOffset[ord(TProp.minterval)] := ptruint(@obj.Interval);
+ PropertyScale[ord(TProp.minterval)] := 1 / 60.0;
+ PropertyFlags[ord(TProp.minterval)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.minterval)] := ord(TProp.interval);
+
+ // double properties
+ PropertyOffset[ord(TProp.Interval)] := ptruint(@obj.Interval);
+
+ PropertyOffset[ord(TProp.mean)] := ptruint(@obj.FMean);
+ PropertyReadFunction[ord(TProp.mean)] := @GetMean;
+ PropertyFlags[ord(TProp.mean)] := [TPropertyFlag.ReadByFunction];
+
+ PropertyOffset[ord(TProp.stddev)] := ptruint(@obj.FStdDev);
+ PropertyReadFunction[ord(TProp.stddev)] := @GetStdDev;
+ PropertyFlags[ord(TProp.stddev)] := [TPropertyFlag.ReadByFunction];
+
+ // double arrays
+ PropertyType[ord(TProp.price)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.price)] := ptruint(@obj.PriceValues);
+ PropertyOffset2[ord(TProp.price)] := ptruint(@obj.FNumPoints);
+
+ PropertyType[ord(TProp.hour)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.hour)] := ptruint(@obj.Hours);
+ PropertyOffset2[ord(TProp.hour)] := ptruint(@obj.FNumPoints);
+
+ // enum action
+ PropertyType[ord(TProp.Action)] := TPropertyType.StringEnumActionProperty;
+ PropertyOffset[ord(TProp.Action)] := ptruint(@DoAction);
+ PropertyOffset2[ord(TProp.Action)] := PtrInt(ActionEnum);
+
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TPriceShape.Set_Code(const Value: String); // sets the active PriceShape
-
+function TPriceShape.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- PriceShapeObj: TPriceShapeObj;
-
+ Obj: TObj;
begin
-
- DSS.ActivePriceShapeObj := NIL;
- PriceShapeObj := ElementList.First;
- while PriceShapeObj <> NIL do
- begin
-
- if CompareText(PriceShapeObj.Name, Value) = 0 then
- begin
- DSS.ActivePriceShapeObj := PriceShapeObj;
- Exit;
- end;
-
- PriceShapeObj := ElementList.Next;
- end;
-
- DoSimpleMsg('PriceShape: "' + Value + '" not Found.', 58612);
-
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TPriceShape.DoCSVFile(const FileName: String);
-
-var
- F: TBufferedFileStream = nil;
- i: Integer;
- s: String;
-
+procedure TPriceShapeObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- try
- F := TBufferedFileStream.Create(FileName, fmOpenRead);
- except
- DoSimpleMsg('Error Opening File: "' + FileName, 58613);
- FreeAndNil(F);
- Exit;
+ case Idx of
+ ord(TProp.csvfile):
+ DoCSVFile(DSS, Hours, PriceValues, FNumPoints, (Interval <> 0.0), csvfile, ParentClass.Name);
+ ord(TProp.sngfile):
+ DoSngFile(DSS, Hours, PriceValues, FNumPoints, (Interval <> 0.0), sngfile, ParentClass.Name);
+ ord(TProp.dblfile):
+ DoDblFile(DSS, Hours, PriceValues, FNumPoints, (Interval <> 0.0), dblfile, ParentClass.Name);
end;
-
- try
-
- with DSS.ActivePriceShapeObj do
+ case Idx of
+ ord(TProp.npts):
begin
- ReAllocmem(PriceValues, Sizeof(PriceValues^[1]) * NumPoints);
- if Interval = 0.0 then
- ReAllocmem(Hours, Sizeof(Hours^[1]) * NumPoints);
- i := 0;
- while ((F.Position + 1) < F.Size) and (i < FNumPoints) do
- begin
- Inc(i);
- FSReadln(F, s); // read entire line and parse with AuxParser
- {AuxParser allows commas or white space}
- with AuxParser do
- begin
- CmdString := s;
- if Interval = 0.0 then
- begin
- NextParam;
- Hours^[i] := DblValue;
- end;
- NextParam;
- PriceValues^[i] := DblValue;
- end;
- end;
- FreeAndNil(F);
- if i <> FNumPoints then
- NumPoints := i;
- end;
+ // Force as the always first property when saving in a later point
+ PrpSequence[Idx] := -10;
- except
- On E: Exception do
- begin
- DoSimpleMsg('Error Processing CSV File: "' + FileName + '. ' + E.Message, 58614);
- FreeAndNil(F);
- Exit;
+ ReAllocmem(PriceValues, Sizeof(PriceValues^[1]) * FNumPoints);
+ if Interval > 0.0 then
+ ReallocMem(Hours, 0) //TODO: check if required
+ else
+ ReAllocmem(Hours, Sizeof(Hours^[1]) * FNumPoints);
end;
- end;
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TPriceShape.DoSngFile(const FileName: String);
-var
- F: TFileStream = nil;
- Hr,
- M: Single;
- i: Integer;
+ ord(TProp.interval):
+ if Interval > 0.0 then
+ ReallocMem(Hours, 0); //TODO: check if required
+ ord(TProp.hour):
+ Interval := 0;
-begin
- try
- F := TFileStream.Create(FileName, fmOpenRead);
- except
- DoSimpleMsg('Error Opening File: "' + FileName, 58615);
- FreeAndNil(F);
- Exit;
- end;
+ ord(TProp.mean),
+ ord(TProp.stddev):
+ FStdDevCalculated := TRUE;
- try
- with DSS.ActivePriceShapeObj do
+ 3, 7, 8, 9:
begin
- ReAllocmem(PriceValues, Sizeof(PriceValues^[1]) * NumPoints);
- if Interval = 0.0 then
- ReAllocmem(Hours, Sizeof(Hours^[1]) * NumPoints);
- i := 0;
- while ((F.Position + 1) < F.Size) and (i < FNumPoints) do
- begin
- Inc(i);
- if Interval = 0.0 then
- begin
- if F.Read(Hr, SizeOf(Hr)) <> SizeOf(Hr) then
- Break;
- Hours^[i] := Hr;
- end;
- if F.Read(M, SizeOf(M)) <> SizeOf(M) then
- Break;
- PriceValues^[i] := M;
- end;
- FreeAndNil(F);
- if i <> FNumPoints then
- NumPoints := i;
+ FStdDevCalculated := FALSE; // now calculated on demand
+ //TODO: check if needed after full migration: NumPoints := FNumPoints; // Keep Properties in order for save command
end;
- except
- DoSimpleMsg('Error Processing PriceShape File: "' + FileName, 58616);
- FreeAndNil(F);
- Exit;
end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
+function TPriceShape.Find(const ObjName: String; const ChangeActive: Boolean): Pointer;
+begin
+ if (Length(ObjName) = 0) or (CompareText(ObjName, 'none') = 0) then
+ Result := NIL
+ else
+ Result := inherited Find(ObjName, ChangeActive);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TPriceShape.DoDblFile(const FileName: String);
+procedure TPriceShapeObj.MakeLike(OtherPtr: Pointer);
var
- F: TFileStream = nil;
+ Other: TObj;
i: Integer;
-
begin
- try
- F := TFileStream.Create(FileName, fmOpenRead);
- except
- DoSimpleMsg('Error Opening File: "' + FileName, 58617);
- FreeAndNil(F);
- Exit;
- end;
-
- try
- with DSS.ActivePriceShapeObj do
- begin
- ReAllocmem(PriceValues, Sizeof(PriceValues^[1]) * NumPoints);
- if Interval = 0.0 then
- ReAllocmem(Hours, Sizeof(Hours^[1]) * NumPoints);
- i := 0;
- while ((F.Position + 1) < F.Size) and (i < FNumPoints) do
- begin
- Inc(i);
- if Interval = 0.0 then
- if F.Read(Hours^[i], SizeOf(Double)) <> SizeOf(Double) then
- Break;
- if F.Read(PriceValues^[i], SizeOf(Double)) <> SizeOf(Double) then
- Break;
- end;
- FreeAndNil(F);
- if i <> FNumPoints then
- NumPoints := i;
- end;
- except
- DoSimpleMsg('Error Processing PriceShape File: "' + FileName, 58618);
- FreeAndNil(F);
- Exit;
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNumPoints := Other.FNumPoints;
+ Interval := Other.Interval;
+ ReallocMem(PriceValues, SizeOf(PriceValues^[1]) * FNumPoints);
+ for i := 1 to FNumPoints do
+ PriceValues^[i] := Other.PriceValues^[i];
+ if Interval > 0.0 then
+ ReallocMem(Hours, 0)
+ else
+ begin
+ ReallocMem(Hours, SizeOf(Hours^[1]) * FNumPoints);
+ for i := 1 to FNumPoints do
+ Hours^[i] := Other.Hours^[i];
end;
-
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TPriceShape Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TPriceShapeObj.Create(ParClass: TDSSClass; const PriceShapeName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(PriceShapeName);
+ Name := AnsiLowerCase(PriceShapeName);
DSSObjType := ParClass.DSSClassType;
LastValueAccessed := 1;
@@ -589,25 +342,20 @@ constructor TPriceShapeObj.Create(ParClass: TDSSClass; const PriceShapeName: Str
PriceValues := NIL;
FStdDevCalculated := FALSE; // calculate on demand
- ArrayPropertyIndex := 0;
-
- InitPropertyValues(0);
-
+ csvfile := '';
+ dblfile := '';
+ sngfile := '';
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TPriceShapeObj.Destroy;
begin
-
ReallocMem(Hours, 0);
if Assigned(PriceValues) then
ReallocMem(PriceValues, 0);
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function TPriceShapeObj.GetPrice(hr: Double): Double;
-
// This FUNCTION returns the Price for the given hour.
// If no points exist in the curve, the result is 0.0
// If there are fewer points than requested, the curve is simply assumed to repeat
@@ -615,94 +363,81 @@ function TPriceShapeObj.GetPrice(hr: Double): Double;
// same day over and over again.
// The value returned is the nearest to the interval requested. Thus if you request
// hour=12.25 and the interval is 1.0, you will get interval 12.
-
var
Index, i: Integer;
-
begin
-
Result := 0.0; // default return value if no points in curve
- if FNumPoints > 0 then // Handle Exceptional cases
- if FNumPoints = 1 then
+ if FNumPoints <= 0 then // Handle Exceptional cases
+ Exit;
+
+ if FNumPoints = 1 then
+ begin
+ Result := PriceValues^[1];
+ Exit;
+ end;
+
+ if Interval > 0.0 then
+ begin
+ Index := round(hr / Interval);
+ if Index > FNumPoints then
+ Index := Index mod FNumPoints; // Wrap around using remainder
+ if Index = 0 then
+ Index := FNumPoints;
+ Result := PriceValues^[Index];
+ Exit;
+ end;
+
+ // For random interval
+
+ // Start with previous value accessed under the assumption that most
+ // of the time, this FUNCTION will be called sequentially
+
+ // Normalize Hr to max hour in curve to get wraparound
+ if (Hr > Hours^[FNumPoints]) then
+ begin
+ Hr := Hr - Trunc(Hr / Hours^[FNumPoints]) * Hours^[FNumPoints];
+ end;
+
+ if (Hours^[LastValueAccessed] > Hr) then
+ LastValueAccessed := 1; // Start over from Beginning
+ for i := LastValueAccessed + 1 to FNumPoints do
+ begin
+ if (Abs(Hours^[i] - Hr) < 0.00001) then // If close to an actual point, just use it.
begin
- Result := PriceValues^[1];
+ Result := PriceValues^[i];
+ LastValueAccessed := i;
+ Exit;
end
else
+ if (Hours^[i] > Hr) then // Interpolate for Price
begin
- if Interval > 0.0 then
- begin
- Index := round(hr / Interval);
- if Index > FNumPoints then
- Index := Index mod FNumPoints; // Wrap around using remainder
- if Index = 0 then
- Index := FNumPoints;
- Result := PriceValues^[Index];
- end
- else
- begin
- // For random interval
-
- { Start with previous value accessed under the assumption that most
- of the time, this FUNCTION will be called sequentially}
-
- {Normalize Hr to max hour in curve to get wraparound}
- if (Hr > Hours^[FNumPoints]) then
- begin
- Hr := Hr - Trunc(Hr / Hours^[FNumPoints]) * Hours^[FNumPoints];
- end;
-
- if (Hours^[LastValueAccessed] > Hr) then
- LastValueAccessed := 1; // Start over from Beginning
- for i := LastValueAccessed + 1 to FNumPoints do
- begin
- if (Abs(Hours^[i] - Hr) < 0.00001) then // If close to an actual point, just use it.
- begin
- Result := PriceValues^[i];
- LastValueAccessed := i;
- Exit;
- end
- else
- if (Hours^[i] > Hr) then // Interpolate for Price
- begin
- LastValueAccessed := i - 1;
- Result := PriceValues^[LastValueAccessed] +
- (Hr - Hours^[LastValueAccessed]) / (Hours^[i] - Hours^[LastValueAccessed]) *
- (PriceValues^[i] - PriceValues^[LastValueAccessed]);
- Exit;
- end;
- end;
-
- // If we fall through the loop, just use last value
- LastValueAccessed := FNumPoints - 1;
- Result := PriceValues^[FNumPoints];
- end;
+ LastValueAccessed := i - 1;
+ Result := PriceValues^[LastValueAccessed] +
+ (Hr - Hours^[LastValueAccessed]) / (Hours^[i] - Hours^[LastValueAccessed]) *
+ (PriceValues^[i] - PriceValues^[LastValueAccessed]);
+ Exit;
end;
+ end;
+ // If we fall through the loop, just use last value
+ LastValueAccessed := FNumPoints - 1;
+ Result := PriceValues^[FNumPoints];
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TPriceShapeObj.CalcMeanandStdDev;
-
begin
-
if FNumPoints > 0 then
if Interval > 0.0 then
RCDMeanandStdDev(PriceValues, FNumPoints, FMean, FStdDev)
else
CurveMeanAndStdDev(PriceValues, Hours, FNumPoints, FMean, FStdDev);
- PropertyValue[5] := Format('%.8g', [FMean]);
- PropertyValue[6] := Format('%.8g', [FStdDev]);
-
FStdDevCalculated := TRUE;
end;
-
function TPriceShapeObj.Get_Interval: Double;
begin
-
if Interval > 0.0 then
Result := Interval
else
@@ -712,7 +447,6 @@ function TPriceShapeObj.Get_Interval: Double;
else
Result := 0.0;
end;
-
end;
function TPriceShapeObj.Get_Mean: Double;
@@ -731,7 +465,6 @@ function TPriceShapeObj.Get_StdDev: Double;
function TPriceShapeObj.Price(i: Integer): Double;
begin
-
if (i <= FNumPoints) and (i > 0) then
begin
Result := PriceValues^[i];
@@ -739,12 +472,10 @@ function TPriceShapeObj.Price(i: Integer): Double;
end
else
Result := 0.0;
-
end;
function TPriceShapeObj.Hour(i: Integer): Double;
begin
-
if Interval = 0 then
begin
if (i <= FNumPoints) and (i > 0) then
@@ -760,151 +491,55 @@ function TPriceShapeObj.Hour(i: Integer): Double;
Result := Hours^[i] * Interval;
LastValueAccessed := i;
end;
-
-end;
-
-
-procedure TPriceShapeObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
-
-end;
-
-function TPriceShapeObj.GetPropertyValue(Index: Integer): String;
-begin
- Result := '';
-
- case Index of
- 2:
- Result := Format('%.8g', [Interval]);
- 3:
- Result := GetDSSArray_Real(FNumPoints, PriceValues);
- 4:
- if Hours <> NIL then
- Result := GetDSSArray_Real(FNumPoints, Hours);
- 5:
- Result := Format('%.8g', [Mean]);
- 6:
- Result := Format('%.8g', [StdDev]);
- 10:
- Result := Format('%.8g', [Interval * 3600.0]);
- 11:
- Result := Format('%.8g', [Interval * 60.0]);
- else
- Result := inherited GetPropertyValue(index);
- end;
-
-end;
-
-procedure TPriceShapeObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := '0'; // Number of points to expect
- PropertyValue[2] := '1'; // default = 1.0 hr;
- PropertyValue[3] := ''; // vector of multiplier values
- PropertyValue[4] := ''; // vextor of hour values
- PropertyValue[5] := '0'; // set the mean (otherwise computed)
- PropertyValue[6] := '0'; // set the std dev (otherwise computed)
- PropertyValue[7] := ''; // Switch input to a csvfile
- PropertyValue[8] := ''; // switch input to a binary file of singles
- PropertyValue[9] := ''; // switch input to a binary file of singles
- PropertyValue[10] := '3600'; // seconds
- PropertyValue[11] := '60'; // minutes
- PropertyValue[12] := ''; // action option .
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
end;
procedure TPriceShapeObj.SaveToDblFile;
-
var
F: TFileStream = nil;
Fname: String;
begin
- if Assigned(PriceValues) then
+ if not Assigned(PriceValues) then
begin
- try
- FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s.dbl', [Name]);
- F := TFileStream.Create(FName, fmCreate);
- F.WriteBuffer(PriceValues^[1], NumPoints * SizeOf(Double));
- DSS.GlobalResult := 'Price=[dblfile=' + FName + ']';
- finally
- FreeAndNil(F);
- end;
+ DoSimpleMsg('%s Prices not defined.', [FullName], 58622);
+ Exit;
+ end;
- end
- else
- DoSimpleMsg('PriceShape.' + Name + ' Prices not defined.', 58622);
+ try
+ FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s.dbl', [Name]);
+ F := TBufferedFileStream.Create(FName, fmCreate);
+ F.WriteBuffer(PriceValues^[1], NumPoints * SizeOf(Double));
+ DSS.GlobalResult := 'Price=[dblfile=' + FName + ']';
+ finally
+ FreeAndNil(F);
+ end;
end;
procedure TPriceShapeObj.SaveToSngFile;
-
var
F: TFileStream = nil;
i: Integer;
Fname: String;
sngPrice: Single;
-
begin
- if Assigned(PriceValues) then
+ if not Assigned(PriceValues) then
begin
- try
- FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s.sng', [Name]);
- F := TFileStream.Create(FName, fmCreate);
- for i := 1 to NumPoints do
- begin
- sngPrice := PriceValues^[i];
- F.Write(sngPrice, sizeof(sngPrice));
- end;
- DSS.GlobalResult := 'Price=[sngfile=' + FName + ']';
- finally
- FreeAndNil(F);
- end;
-
-
- end
- else
- DoSimpleMsg('PriceShape.' + Name + ' Prices not defined.', 58623);
-
-
-end;
-
-
-procedure TPriceShapeObj.Set_Mean(const Value: Double);
-begin
- FStdDevCalculated := TRUE;
- FMean := Value;
-end;
-
-procedure TPriceShapeObj.Set_NumPoints(const Value: Integer);
-begin
- PropertyValue[1] := IntToStr(Value); // Update property list variable
-
- // Reset array property values to keep them in propoer order in Save
-
- if ArrayPropertyIndex > 0 then
- PropertyValue[ArrayPropertyIndex] := PropertyValue[ArrayPropertyIndex];
-
- FNumPoints := Value; // Now assign the value
-end;
+ DoSimpleMsg('%s Prices not defined.', [FullName], 58623);
+ Exit;
+ end;
-procedure TPriceShapeObj.Set_StdDev(const Value: Double);
-begin
- FStdDevCalculated := TRUE;
- FStdDev := Value;
+ try
+ FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s.sng', [Name]);
+ F := TBufferedFileStream.Create(FName, fmCreate);
+ for i := 1 to NumPoints do
+ begin
+ sngPrice := PriceValues^[i];
+ F.Write(sngPrice, sizeof(sngPrice));
+ end;
+ DSS.GlobalResult := 'Price=[sngfile=' + FName + ']';
+ finally
+ FreeAndNil(F);
+ end;
end;
+finalization ActionEnum.Free;
end.
diff --git a/src/General/Spectrum.pas b/src/General/Spectrum.pas
index 81782a767..72b6e5754 100644
--- a/src/General/Spectrum.pas
+++ b/src/General/Spectrum.pas
@@ -7,14 +7,10 @@
----------------------------------------------------------
}
-{ Created 10/25/00
-
- Harmonic Spectrum specified as Harmonic, pct magnitude and angle
-
- Spectrum is shifted by the fundamental angle and stored in MultArray
- so that the fundamental is at zero degrees phase shift
-
-}
+// Harmonic Spectrum specified as Harmonic, pct magnitude and angle
+//
+// Spectrum is shifted by the fundamental angle and stored in MultArray
+// so that the fundamental is at zero degrees phase shift
interface
@@ -24,30 +20,37 @@ interface
DSSClass,
DSSObject,
Arraydef,
- ucomplex;
+ UComplex, DSSUcomplex;
type
+{$SCOPEDENUMS ON}
+ TSpectrumProp = (
+ INVALID = 0,
+ NumHarm = 1,
+ harmonic = 2,
+ pctmag = 3,
+ angle = 4,
+ CSVFile = 5
+ );
+{$SCOPEDENUMS OFF}
+
+ TSpectrumObj = class;
TSpectrum = class(TDSSClass)
- PRIVATE
-
- function Get_Code: String; // Returns active spectrum code string
- procedure Set_Code(const Value: String); // sets the active Spectrum
- procedure DoCSVFile(const FileName: String);
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const LineName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
+ DefaultGeneral: TSpectrumObj;
+ DefaultLoad: TSpectrumObj;
+ DefaultGen: TSpectrumObj;
+ DefaultVSource: TSpectrumObj;
+
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
- // Set this property to point ActiveSpectrumObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
-
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
+ procedure BindDefaults();
end;
TSpectrumObj = class(TDSSObject)
@@ -55,6 +58,7 @@ TSpectrumObj = class(TDSSObject)
puMagArray,
AngleArray: pDoubleArray;
MultArray: pComplexArray;
+ csvfile: string;
procedure SetMultArray;
function HarmArrayHasaZero(var zeropoint: Integer): Boolean;
@@ -65,280 +69,177 @@ TSpectrumObj = class(TDSSObject)
constructor Create(ParClass: TDSSClass; const SpectrumName: String);
destructor Destroy; OVERRIDE;
+ procedure MakeLike(OtherPtr: Pointer); override;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
function GetMult(const h: Double): Complex;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
-
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
end;
-
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Sysutils,
Utilities,
- BufStream,
DSSHelper,
DSSObjectHelper,
TypInfo;
+type
+ TObj = TSpectrumObj;
+ TProp = TSpectrumProp;
const
- NumPropsThisClass = 5;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TSpectrum.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
-begin
- inherited Create(dssContext);
- Class_Name := 'Spectrum';
- DSSClassType := DSS_OBJECT;
- ActiveElement := 0;
+procedure DoCSVFile(Obj: TObj; const FileName: String);forward;
- DefineProperties;
+constructor TSpectrum.Create(dssContext: TDSSContext);
+begin
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, DSS_OBJECT, 'Spectrum');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TSpectrum.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TSpectrum.DefineProperties;
-begin
- NumProperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- PropertyName[1] := 'NumHarm';
- PropertyName[2] := 'harmonic';
- PropertyName[3] := '%mag';
- PropertyName[4] := 'angle';
- PropertyName[5] := 'CSVFile';
-
- PropertyHelp[1] := 'Number of frequencies in this spectrum. (See CSVFile)';
- PropertyHelp[2] := 'Array of harmonic values. You can also use the syntax' + CRLF +
- 'harmonic = (file=filename) !for text file one value per line' + CRLF +
- 'harmonic = (dblfile=filename) !for packed file of doubles' + CRLF +
- 'harmonic = (sngfile=filename) !for packed file of singles ';
- PropertyHelp[3] := 'Array of magnitude values, assumed to be in PERCENT. You can also use the syntax' + CRLF +
- '%mag = (file=filename) !for text file one value per line' + CRLF +
- '%mag = (dblfile=filename) !for packed file of doubles' + CRLF +
- '%mag = (sngfile=filename) !for packed file of singles ';
- PropertyHelp[4] := 'Array of phase angle values, degrees.You can also use the syntax' + CRLF +
- 'angle = (file=filename) !for text file one value per line' + CRLF +
- 'angle = (dblfile=filename) !for packed file of doubles' + CRLF +
- 'angle = (sngfile=filename) !for packed file of singles ';
- PropertyHelp[5] := 'File of spectrum points with (harmonic, magnitude-percent, angle-degrees) values, one set of 3 per line, in CSV format. ' +
- 'If fewer than NUMHARM frequencies found in the file, NUMHARM is set to the smaller value.';
-
-
- ActiveProperty := NumPropsThisClass;
- inherited; // Add defs of inherited properties to bottom of list
-
-end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TSpectrum.NewObject(const ObjName: String): Integer;
+procedure TSpectrum.BindDefaults();
begin
- // create a new object of this class and add to list
- DSS.ActiveDSSObject := TSpectrumObj.Create(Self, ObjName);
- Result := AddObjectToList(DSS.ActiveDSSObject);
+ DefaultGeneral := Find('default');
+ DefaultLoad := Find('defaultload');
+ DefaultGen := Find('defaultgen');
+ DefaultVSource := Find('defaultvsource');
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TSpectrum.Edit: Integer;
-var
- i,
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
- iZeroPoint: Integer; // for error trapping
-
+procedure TSpectrum.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveSpectrumObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActiveSpectrumObj;
-
- with DSS.ActiveSpectrumObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Name + '"', 650);
- 1:
- begin
- NumHarm := Parser.IntValue;
- ReAllocmem(AngleArray, Sizeof(AngleArray^[1]) * NumHarm); // Make a dummy Angle array
- for i := 1 to NumHarm do
- AngleArray^[i] := 0.0;
- end;
- 2:
- begin
- ReAllocmem(HarmArray, Sizeof(HarmArray^[1]) * NumHarm);
- NumHarm := InterpretDblArray(Param, NumHarm, HarmArray);
- end;
- 3:
- begin
- ReAllocmem(puMagArray, Sizeof(puMagArray^[1]) * NumHarm);
- NumHarm := InterpretDblArray(Param, NumHarm, puMagArray);
- for i := 1 to NumHarm do
- puMagArray^[i] := puMagArray^[i] * 0.01; // convert to per unit
- end;
- 4:
- begin
- ReAllocmem(AngleArray, Sizeof(AngleArray^[1]) * NumHarm);
- NumHarm := InterpretDblArray(Param, NumHarm, AngleArray);
- end;
- 5:
- DoCSVFile(AdjustInputFilePath(Param));
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveSpectrumObj, Parampointer - NumPropsThisClass)
- end;
-
+ NumProperties := NumPropsThisClass;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end; {WHILE}
+ // strings
+ PropertyType[ord(TProp.csvfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.csvfile)] := ptruint(@obj.csvfile);
+ PropertyFlags[ord(TProp.csvfile)] := [TPropertyFlag.IsFilename];
- if (HarmArray <> NIL) then // Check this after HarmArray is allocated 2/20/2018
- begin
- if HarmArrayHasaZero(iZeroPoint) then
+ // integer properties
+ PropertyType[ord(TProp.NumHarm)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.NumHarm)] := ptruint(@obj.NumHarm);
- DoSimpleMsg(Format('Error: Zero frequency detected in Spectrum.%s, point %d. Not allowed', [Name, iZeroPoint]), 65001)
+ // double arrays
+ PropertyType[ord(TProp.harmonic)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.harmonic)] := ptruint(@obj.HarmArray);
+ PropertyOffset2[ord(TProp.harmonic)] := ptruint(@obj.NumHarm);
- else
- if (HarmArray <> NIL) and (puMagArray <> NIL) and (AngleArray <> NIL) then
- SetMultArray;
+ PropertyType[ord(TProp.angle)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.angle)] := ptruint(@obj.AngleArray);
+ PropertyOffset2[ord(TProp.angle)] := ptruint(@obj.NumHarm);
- end
+ PropertyType[ord(TProp.pctmag)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.pctmag)] := ptruint(@obj.puMagArray);
+ PropertyOffset2[ord(TProp.pctmag)] := ptruint(@obj.NumHarm);
+ PropertyScale[ord(TProp.pctmag)] := 0.01;
- end; {WITH}
+ ActiveProperty := NumPropsThisClass;
+ inherited;
+end;
+function TSpectrum.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
+begin
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TSpectrum.MakeLike(const LineName: String): Integer;
+procedure TSpectrumObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- OtherSpectrum: TSpectrumObj;
i: Integer;
begin
- Result := 0;
- {See if we can find this line code in the present collection}
- OtherSpectrum := Find(LineName);
- if OtherSpectrum <> NIL then
- with DSS.ActiveSpectrumObj do
+ case Idx of
+ ord(TProp.NumHarm):
begin
-
- NumHarm := OtherSpectrum.NumHarm;
-
- ReallocMem(HarmArray, Sizeof(HarmArray^[1]) * NumHarm);
- ReallocMem(puMagArray, Sizeof(puMagArray^[1]) * NumHarm);
- ReallocMem(AngleArray, Sizeof(AngleArray^[1]) * NumHarm);
-
+ if (HarmArray <> NIL) then // leave it NIL since there is a validation that uses that in Edit
+ ReAllocmem(HarmArray, Sizeof(Double) * NumHarm);
+ //if (HarmArray <> NIL) then
+ ReAllocmem(AngleArray, Sizeof(Double) * NumHarm); // Make a dummy Angle array
for i := 1 to NumHarm do
- begin
- HarmArray^[i] := OtherSpectrum.HarmArray^[i];
- puMagArray^[i] := OtherSpectrum.puMagArray^[i];
- AngleArray^[i] := OtherSpectrum.AngleArray^[i];
- end;
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherSpectrum.PropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Spectrum MakeLike: "' + LineName + '" Not Found.', 651);
-
-
+ AngleArray^[i] := 0.0; //TODO: remove -- left for backwards compatiblity, but this is kinda buggy
+ end;
+ ord(TProp.csvfile):
+ DoCSVFile(self, csvfile);
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-
-function TSpectrum.Get_Code: String; // Returns active line code string
+function TSpectrum.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
var
- SpectrumObj: TSpectrumObj;
-
+ iZeroPoint: Integer; // for error trapping
begin
+ with TObj(ptr) do
+ begin
+ if (HarmArray <> NIL) then // Check this after HarmArray is allocated 2/20/2018
+ begin
+ if HarmArrayHasaZero(iZeroPoint) then
+ DoSimpleMsg('Error: Zero frequency detected in %s, point %d. Not allowed', [FullName, iZeroPoint], 65001)
- SpectrumObj := ElementList.Active;
- Result := SpectrumObj.Name;
-
+ else
+ if (HarmArray <> NIL) and (puMagArray <> NIL) and (AngleArray <> NIL) then
+ SetMultArray;
+ end;
+ Exclude(Flags, Flg.EditionActive);
+ end;
+ Result := True;
end;
-procedure TSpectrum.Set_Code(const Value: String); // sets the active Spectrum
+procedure TSpectrumObj.MakeLike(OtherPtr: Pointer);
var
- SpectrumObj: TSpectrumObj;
+ Other: TObj;
+ i: Integer;
begin
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ NumHarm := Other.NumHarm;
- DSS.ActiveSpectrumObj := NIL;
- SpectrumObj := ElementList.First;
- while SpectrumObj <> NIL do
- begin
-
- if CompareText(SpectrumObj.Name, Value) = 0 then
- begin
- DSS.ActiveSpectrumObj := SpectrumObj;
- Exit;
- end;
+ ReallocMem(HarmArray, Sizeof(HarmArray^[1]) * NumHarm);
+ ReallocMem(puMagArray, Sizeof(puMagArray^[1]) * NumHarm);
+ ReallocMem(AngleArray, Sizeof(AngleArray^[1]) * NumHarm);
- SpectrumObj := ElementList.Next;
+ for i := 1 to NumHarm do
+ begin
+ HarmArray^[i] := Other.HarmArray^[i];
+ puMagArray^[i] := Other.puMagArray^[i];
+ AngleArray^[i] := Other.AngleArray^[i];
end;
-
- DoSimpleMsg('Spectrum: "' + Value + '" not Found.', 652);
-
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TSpectrum Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TSpectrumObj.Create(ParClass: TDSSClass; const SpectrumName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(SpectrumName);
+ Name := AnsiLowerCase(SpectrumName);
DSSObjType := ParClass.DSSClassType;
-
NumHarm := 0;
HarmArray := NIL;
puMagArray := NIL;
AngleArray := NIL;
MultArray := NIL;
-
-
- InitPropertyValues(0);
+ csvfile := '';
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TSpectrumObj.Destroy;
begin
Reallocmem(HarmArray, 0);
@@ -348,31 +249,28 @@ destructor TSpectrumObj.Destroy;
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TSpectrum.DoCSVFile(const FileName: String);
-
+procedure DoCSVFile(Obj: TObj; const FileName: String);
var
- F: TBufferedFileStream = nil;
+ F: TStream = nil;
i: Integer;
s: String;
begin
- try
- F := TBufferedFileStream.Create(FileName, fmOpenRead);
- except
- DoSimpleMsg('Error Opening CSV File: "' + FileName, 653);
- FreeAndNil(F);
- Exit;
- end;
-
- try
+ with Obj do
+ begin
+ try
+ F := DSS.GetROFileStream(FileName);
+ except
+ DoSimpleMsg('Error Opening CSV File: "%s"', [FileName], 653);
+ FreeAndNil(F);
+ Exit;
+ end;
- with DSS.ActiveSpectrumObj do
- begin
+ try
ReAllocmem(HarmArray, Sizeof(HarmArray^[1]) * NumHarm);
ReAllocmem(puMagArray, Sizeof(puMagArray^[1]) * NumHarm);
ReAllocmem(AngleArray, Sizeof(AngleArray^[1]) * NumHarm);
i := 0;
- while ((F.Position + 1) <> F.Size) and (i < NumHarm) do
+ while ((F.Position + 1) < F.Size) and (i < NumHarm) do
begin
Inc(i);
FSReadln(F, S); // Use Auxparser, which allows for formats
@@ -388,59 +286,27 @@ procedure TSpectrum.DoCSVFile(const FileName: String);
end;
end;
F.Free();
- if i <> NumHarm then
- NumHarm := i; // reset number of points
- end;
-
- except
- On E: Exception do
- begin
- DoSimpleMsg('Error Processing CSV File: "' + FileName + '. ' + E.Message, 654);
- F.Free();
- Exit;
+ NumHarm := i; // reset number of points
+ except
+ On E: Exception do
+ begin
+ DoSimpleMsg('Error Processing CSV File: "%s". %s', [FileName, E.Message], 654);
+ F.Free();
+ Exit;
+ end;
end;
end;
-
end;
-
-procedure TSpectrumObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
+procedure TSpectrumObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
- i, j: Integer;
-
+ i: Integer;
begin
inherited DumpProperties(F, Complete);
with ParentClass do
for i := 1 to NumProperties do
- begin
- case i of
- 2:
- begin
- FSWrite(F, '~ ', PropertyName^[i], '=(');
- for j := 1 to NumHarm do
- FSWrite(F, Format('%-g, ', [HarmArray^[j]]));
- FSWriteln(F, ')');
- end;
- 3:
- begin
- FSWrite(F, '~ ', PropertyName^[i], '=(');
- for j := 1 to NumHarm do
- FSWrite(F, Format('%-g, ', [puMagArray^[j] * 100.0]));
- FSWriteln(F, ')');
- end;
- 4:
- begin
- FSWrite(F, '~ ', PropertyName^[i], '=(');
- for j := 1 to NumHarm do
- FSWrite(F, Format('%-g, ', [AngleArray^[j]]));
- FSWriteln(F, ')');
- end;
- else
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
- end;
+ FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
if Complete then
begin
@@ -456,14 +322,10 @@ procedure TSpectrumObj.DumpProperties(F: TFileStream; Complete: Boolean);
end;
end;
-
function TSpectrumObj.GetMult(const h: Double): Complex;
-
var
i: Integer;
-
begin
-
{Search List for harmonic (nearest 0.01 harmonic) and return multiplier}
for i := 1 to NumHarm do
begin
@@ -478,41 +340,6 @@ function TSpectrumObj.GetMult(const h: Double): Complex;
Result := cZERO;
end;
-function TSpectrumObj.GetPropertyValue(Index: Integer): String;
-var
- i: Integer;
-begin
- case Index of
- 2..4:
- Result := '(';
- else
- Result := '';
- end;
-
- case Index of
- 1:
- Result := IntToStr(NumHarm);
- 2:
- for i := 1 to NumHarm do
- Result := Result + Format('%-g, ', [HarmArray^[i]]);
- 3:
- for i := 1 to NumHarm do
- Result := Result + Format('%-g, ', [puMagArray^[i] * 100.0]);
- 4:
- for i := 1 to NumHarm do
- Result := Result + Format('%-g, ', [AngleArray^[i]]);
- else
- Result := inherited GetPropertyValue(index);
- end;
-
- case Index of
- 2..4:
- Result := Result + ')';
- else
- end;
-
-end;
-
function TSpectrumObj.HarmArrayHasaZero(var ZeroPoint: Integer): Boolean;
var
i: Integer;
@@ -528,29 +355,13 @@ function TSpectrumObj.HarmArrayHasaZero(var ZeroPoint: Integer): Boolean;
end;
end;
-procedure TSpectrumObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := '0';
- PropertyValue[2] := '';
- PropertyValue[3] := '';
- PropertyValue[4] := '';
- PropertyValue[5] := '';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
procedure TSpectrumObj.SetMultArray;
-
{Rotate all phase angles so that the fundamental is at zero}
-
var
i: Integer;
FundAngle: Double;
begin
-
try
FundAngle := 0.0;
@@ -568,11 +379,10 @@ procedure TSpectrumObj.SetMultArray;
MultArray^[i] := pdegtocomplex(puMagArray^[i], (AngleArray^[i] - HarmArray^[i] * FundAngle));
except
- DoSimpleMsg('Exception while computing Spectrum.' + Name + '. Check Definition. Aborting', 655);
+ DoSimpleMsg('Exception while computing %s. Check Definition. Aborting', [FullName], 655);
if DSS.In_Redirect then
DSS.Redirect_Abort := TRUE;
end;
-
end;
-end.
+end.
\ No newline at end of file
diff --git a/src/General/TCC_Curve.pas b/src/General/TCC_Curve.pas
index 67831f10a..4454ad6c6 100644
--- a/src/General/TCC_Curve.pas
+++ b/src/General/TCC_Curve.pas
@@ -7,14 +7,10 @@
----------------------------------------------------------
}
-{ Created 8-25-00 }
-
-{
- Nominally, a time-current curve, but also used for volt-time curves.
-
- Collections of time points. Return values can be interpolated either
- Log-Log as traditional TCC or as over- or under-voltage definite time.
-}
+// Nominally, a time-current curve, but also used for volt-time curves.
+//
+// Collections of time points. Return values can be interpolated either
+// Log-Log as traditional TCC or as over- or under-voltage definite time.
interface
@@ -27,28 +23,23 @@ interface
Arraydef;
type
+{$SCOPEDENUMS ON}
+ TTCC_CurveProp = (
+ INVALID = 0,
+ npts = 1, // Number of points to expect
+ C_array = 2, // vector of multiplier values
+ T_array = 3 // vector of time values , Sec
+ );
+{$SCOPEDENUMS OFF}
TTCC_Curve = class(TDSSClass)
- PRIVATE
-
- function Get_Code: String; // Returns active TCC_Curve string
- procedure Set_Code(const Value: String); // sets the active TCC_Curve
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const ShapeName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
-
- // Set this property to point ActiveTCC_CurveObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
-
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TTCC_CurveObj = class(TDSSObject)
@@ -60,11 +51,11 @@ TTCC_CurveObj = class(TDSSObject)
t_values, // Time values (hr) if Interval > 0.0 Else nil
c_values: pDoubleArray;
-
PUBLIC
-
constructor Create(ParClass: TDSSClass; const TCC_CurveName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
function GetTCCTime(const C_Value: Double): Double; // Return operating time for a particular time value
function GetUVTime(const V_Value: Double): Double; // Return operating time for undervoltage relay
@@ -72,98 +63,82 @@ TTCC_CurveObj = class(TDSSObject)
function Value(i: Integer): Double; // get C_Value by index
function Time(i: Integer): Double; // get time value (sec) corresponding to point index
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
property NumPoints: Integer READ Npts;
-
end;
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Sysutils,
- Ucomplex,
+ UComplex, DSSUcomplex,
MathUtil,
Utilities,
DSSHelper,
DSSObjectHelper,
TypInfo;
+type
+ TObj = TTCC_CurveObj;
+ TProp = TTCC_CurveProp;
const
- NumPropsThisClass = 3;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TTCC_Curve.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TTCC_Curve.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'TCC_Curve';
- DSSClassType := DSS_OBJECT;
-
- ActiveElement := 0;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
-
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
+ inherited Create(dssContext, DSS_OBJECT, 'TCC_Curve');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TTCC_Curve.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
procedure TTCC_Curve.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
- // Define Property names
- PropertyName[1] := 'npts'; // Number of points to expect
- PropertyName[2] := 'C_array'; // vector of multiplier values
- PropertyName[3] := 'T_array'; // vextor of time values , Sec
+ // double arrays
+ PropertyType[ord(TProp.C_array)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.C_array)] := ptruint(@obj.C_Values);
+ PropertyOffset2[ord(TProp.C_array)] := ptruint(@obj.Npts);
- // define Property help values
+ PropertyType[ord(TProp.T_array)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.T_array)] := ptruint(@obj.T_values);
+ PropertyOffset2[ord(TProp.T_array)] := ptruint(@obj.Npts);
- PropertyHelp[1] := 'Number of points to expect in time-current arrays.'; // Number of points to expect
- PropertyHelp[2] := 'Array of current (or voltage) values corresponding to time values (see help on T_Array).'; // vector of multiplier values
- PropertyHelp[3] := 'Array of time values in sec. Typical array syntax: ' + CRLF +
- 't_array = (1, 2, 3, 4, ...)' + CRLF + CRLF +
- 'Can also substitute a file designation: ' + CRLF +
- 't_array = (file=filename)' + CRLF + CRLF +
- 'The specified file has one value per line.';
+ // integer
+ PropertyType[ord(TProp.npts)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.npts)] := ptruint(@obj.Npts);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TTCC_Curve.NewObject(const ObjName: String): Integer;
+function TTCC_Curve.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // create a new object of this class and add to list
- DSS.ActiveDSSObject := TTCC_CurveObj.Create(Self, ObjName);
- Result := AddObjectToList(DSS.ActiveDSSObject);
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure CalcLogPoints(const X, LogX: PDoubleArray; N: Integer);
-
var
i: Integer;
-
begin
for i := 1 to N do
if X^[i] > 0.0 then
@@ -172,151 +147,50 @@ procedure CalcLogPoints(const X, LogX: PDoubleArray; N: Integer);
LogX^[i] := Ln(0.001);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TTCC_Curve.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
-begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveTCC_CurveObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActiveTCC_CurveObj;
-
- with DSS.ActiveTCC_CurveObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 420);
- 1:
- Npts := Parser.Intvalue;
- 2:
- Npts := InterpretDblArray(Param, Npts, C_Values); // Parser.ParseAsVector(Npts, Multipliers);
- 3:
- Npts := InterpretDblArray(Param, Npts, T_values); // Parser.ParseAsVector(Npts, Hours);
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveTCC_CurveObj, ParamPointer - NumPropsThisClass)
- end;
-
- case ParamPointer of
- 1:
- begin // Reallocate arrays to corresponde to Npts
- ReAllocmem(C_Values, Sizeof(C_Values^[1]) * Npts);
- ReAllocmem(LogC, Sizeof(LogC^[1]) * Npts);
- ReAllocmem(T_values, Sizeof(T_values^[1]) * Npts);
- ReAllocmem(LogT, Sizeof(LogT^[1]) * Npts);
- end;
- 2:
- CalcLogPoints(C_Values, LogC, Npts);
- 3:
- CalcLogPoints(T_Values, LogT, Npts);
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end; {WHILE}
- end; {WITH}
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TTCC_Curve.MakeLike(const ShapeName: String): Integer;
-var
- OtherTCC_Curve: TTCC_CurveObj;
- i: Integer;
+procedure TTCC_CurveObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- Result := 0;
- {See if we can find this line code in the present collection}
- OtherTCC_Curve := Find(ShapeName);
- if OtherTCC_Curve <> NIL then
- with DSS.ActiveTCC_CurveObj do
- begin
- Npts := OtherTCC_Curve.Npts;
+ case Idx of
+ 1:
+ begin // Reallocate arrays to corresponde to Npts
ReAllocmem(C_Values, Sizeof(C_Values^[1]) * Npts);
ReAllocmem(LogC, Sizeof(LogC^[1]) * Npts);
ReAllocmem(T_values, Sizeof(T_values^[1]) * Npts);
ReAllocmem(LogT, Sizeof(LogT^[1]) * Npts);
- for i := 1 to Npts do
- C_Values^[i] := OtherTCC_Curve.C_Values^[i];
- for i := 1 to Npts do
- T_values^[i] := OtherTCC_Curve.T_values^[i];
- for i := 1 to Npts do
- LogC^[i] := OtherTCC_Curve.LogC^[i];
- for i := 1 to Npts do
- LogT^[i] := OtherTCC_Curve.LogT^[i];
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherTCC_Curve.PropertyValue[i];
- end
- else
- DoSimpleMsg('Error in TCC_Curve MakeLike: "' + ShapeName + '" Not Found.', 421);
-
-
-end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TTCC_Curve.Get_Code: String; // Returns active line code string
-var
- TCC_CurveObj: TTCC_CurveObj;
-
-begin
-
- TCC_CurveObj := ElementList.Active;
- Result := TCC_CurveObj.Name;
-
+ end;
+ 2:
+ CalcLogPoints(C_Values, LogC, Npts);
+ 3:
+ CalcLogPoints(T_Values, LogT, Npts);
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTCC_Curve.Set_Code(const Value: String); // sets the active TCC_Curve
-
+procedure TTCC_CurveObj.MakeLike(OtherPtr: Pointer);
var
- TCC_CurveObj: TTCC_CurveObj;
-
+ Other: TObj;
+ i: Integer;
begin
-
- DSS.ActiveTCC_CurveObj := NIL;
- TCC_CurveObj := ElementList.First;
- while TCC_CurveObj <> NIL do
- begin
-
- if CompareText(TCC_CurveObj.Name, Value) = 0 then
- begin
- DSS.ActiveTCC_CurveObj := TCC_CurveObj;
- Exit;
- end;
-
- TCC_CurveObj := ElementList.Next;
- end;
-
- DoSimpleMsg('TCC_Curve: "' + Value + '" not Found.', 422);
-
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ Npts := Other.Npts;
+ ReAllocmem(C_Values, Sizeof(C_Values^[1]) * Npts);
+ ReAllocmem(LogC, Sizeof(LogC^[1]) * Npts);
+ ReAllocmem(T_values, Sizeof(T_values^[1]) * Npts);
+ ReAllocmem(LogT, Sizeof(LogT^[1]) * Npts);
+ for i := 1 to Npts do
+ C_Values^[i] := Other.C_Values^[i];
+ for i := 1 to Npts do
+ T_values^[i] := Other.T_values^[i];
+ for i := 1 to Npts do
+ LogC^[i] := Other.LogC^[i];
+ for i := 1 to Npts do
+ LogT^[i] := Other.LogT^[i];
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TTCC_Curve Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TTCC_CurveObj.Create(ParClass: TDSSClass; const TCC_CurveName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(TCC_CurveName);
+ Name := AnsiLowerCase(TCC_CurveName);
DSSObjType := ParClass.DSSClassType;
LastValueAccessed := 1;
@@ -325,15 +199,10 @@ constructor TTCC_CurveObj.Create(ParClass: TDSSClass; const TCC_CurveName: Strin
T_Values := NIL;
LogC := NIL;
LogT := NIL;
-
- InitPropertyValues(0);
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TTCC_CurveObj.Destroy;
begin
-
ReallocMem(T_Values, 0);
ReallocMem(C_Values, 0);
ReallocMem(LogC, 0);
@@ -341,22 +210,17 @@ destructor TTCC_CurveObj.Destroy;
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function TTCC_CurveObj.GetTCCtime(const C_Value: Double): Double;
-
// This function returns the operation time for the value given.
// If the value is less than the first entry, return = -1 for No operation.
// Log-Log interpolation is used.
-
var
i: Integer;
Logtest: Double;
-
begin
-
Result := -1.0; // default return value
- {If current is less than first point, just leave}
+ // If current is less than first point, just leave
if C_Value < C_Values^[1] then
Exit;
@@ -366,15 +230,13 @@ function TTCC_CurveObj.GetTCCtime(const C_Value: Double): Double;
Result := T_Values^[1]
else
begin
-
- { Start with previous value accessed under the assumption that most
- of the time, this function will be called sequentially}
+ // Start with previous value accessed under the assumption that most
+ // of the time, this function will be called sequentially
if C_Values^[LastValueAccessed] > C_Value then
LastValueAccessed := 1; // Start over from beginning
for i := LastValueAccessed + 1 to Npts do
begin
-
if C_Values^[i] = C_Value then
begin
Result := T_Values^[i]; // direct hit!
@@ -397,11 +259,10 @@ function TTCC_CurveObj.GetTCCtime(const C_Value: Double): Double;
end;
end;
- // If we fall through the loop, just use last value
+ // If we fall through the loop, just use last value
LastValueAccessed := Npts - 1;
Result := T_Values^[Npts];
end;
-
end;
function TTCC_CurveObj.GetOVTime(const V_Value: Double): Double;
@@ -426,14 +287,10 @@ function TTCC_CurveObj.GetOVTime(const V_Value: Double): Double;
end;
Result := T_Values^[i - 1];
end;
-
end;
-
end;
-
function TTCC_CurveObj.GetUVTime(const V_Value: Double): Double;
-
// Under-voltage, definite time relay
var
i: Integer;
@@ -456,13 +313,10 @@ function TTCC_CurveObj.GetUVTime(const V_Value: Double): Double;
Result := T_Values^[i + 1];
end;
end;
-
end;
-
function TTCC_CurveObj.Value(i: Integer): Double;
begin
-
if (i <= Npts) and (i > 0) then
begin
Result := C_Values^[i];
@@ -470,12 +324,10 @@ function TTCC_CurveObj.Value(i: Integer): Double;
end
else
Result := 0.0;
-
end;
function TTCC_CurveObj.Time(i: Integer): Double;
begin
-
if (i <= Npts) and (i > 0) then
begin
Result := T_Values^[i];
@@ -483,47 +335,6 @@ function TTCC_CurveObj.Time(i: Integer): Double;
end
else
Result := 0.0;
-
-end;
-
-
-procedure TTCC_CurveObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-end;
-
-function TTCC_CurveObj.GetPropertyValue(Index: Integer): String;
-begin
- Result := '';
-
- case Index of
- 2:
- Result := GetDSSArray_Real(Npts, C_Values);
- 3:
- Result := GetDSSArray_Real(Npts, T_Values);
- else
- Result := inherited GetPropertyValue(index);
- end;
-end;
-
-procedure TTCC_CurveObj.InitPropertyValues(ArrayOffset: Integer);
-begin
- PropertyValue[1] := '0'; // Number of points to expect
- PropertyValue[2] := ''; // vector of multiplier values
- PropertyValue[3] := ''; // vextor of sec values
-
- inherited InitPropertyValues(NumPropsThisClass);
-
end;
-end.
+end.
\ No newline at end of file
diff --git a/src/General/TSData.pas b/src/General/TSData.pas
index 1ca0f0635..706559295 100644
--- a/src/General/TSData.pas
+++ b/src/General/TSData.pas
@@ -17,54 +17,43 @@ interface
ConductorData;
type
+{$SCOPEDENUMS ON}
+ TTSDataProp = (
+ INVALID = 0,
+ DiaShield = 1,
+ TapeLayer = 2,
+ TapeLap = 3
+ );
+{$SCOPEDENUMS OFF}
TTSData = class(TCableData)
- PRIVATE
- function Get_Code: String;
- procedure Set_Code(const Value: String);
PROTECTED
- procedure DefineProperties;
- function MakeLike(const TSName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
- // Set this property to point ActiveTSDataObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TTSDataObj = class(TCableDataObj)
-{$IFDEF DSS_CAPI}
- PUBLIC
-{$ELSE}
- PRIVATE
-{$ENDIF}
- FDiaShield: Double;
- FTapeLayer: Double;
- FTapeLap: Double;
PUBLIC
+ DiaShield: Double;
+ TapeLayer: Double;
+ TapeLap: Double;
constructor Create(ParClass: TDSSClass; const TSDataName: String);
destructor Destroy; OVERRIDE;
-
- property DiaShield: Double READ FDiaShield;
- property TapeLayer: Double READ FTapeLayer;
- property TapeLap: Double READ FTapeLap;
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
end;
implementation
uses
- ParserDel,
DSSGlobals,
DSSClassDefs,
Sysutils,
- Ucomplex,
+ UComplex, DSSUcomplex,
Arraydef,
LineUnits,
Utilities,
@@ -72,20 +61,20 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TTSDataObj;
+ TProp = TTSDataProp;
const
- NumPropsThisClass = 3;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-constructor TTSData.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TTSData.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'TSData';
- DSSClassType := DSS_OBJECT;
- ActiveElement := 0;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, DSS_OBJECT, 'TSData');
end;
destructor TTSData.Destroy;
@@ -94,147 +83,69 @@ destructor TTSData.Destroy;
end;
procedure TTSData.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
NumProperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- PropertyName[1] := 'DiaShield';
- PropertyName[2] := 'TapeLayer';
- PropertyName[3] := 'TapeLap';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
- PropertyHelp[1] := 'Diameter over tape shield; same units as radius; no default.';
- PropertyHelp[2] := 'Tape shield thickness; same units as radius; no default.';
- PropertyHelp[3] := 'Tape Lap in percent; default 20.0';
+ // double properties (default type)
+ PropertyOffset[ActiveProperty + ord(TProp.DiaShield)] := ptruint(@obj.DiaShield);
+ PropertyOffset[ActiveProperty + ord(TProp.TapeLayer)] := ptruint(@obj.TapeLayer);
+ PropertyOffset[ActiveProperty + ord(TProp.TapeLap)] := ptruint(@obj.TapeLap);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
+ inherited DefineProperties;
end;
-function TTSData.NewObject(const ObjName: String): Integer;
-begin
- with ActiveCircuit do
- begin
- DSS.ActiveDSSObject := TTSDataObj.Create(Self, ObjName);
- Result := AddObjectToList(DSS.ActiveDSSObject);
- end;
-end;
-
-function TTSData.Edit: Integer;
+function TTSData.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
+ Obj: TObj;
begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveConductorDataObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActiveConductorDataObj;
- with TTSDataObj(DSS.ActiveConductorDataObj) do
- begin
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 101);
- 1:
- FDiaShield := Parser.DblValue;
- 2:
- FTapeLayer := Parser.DblValue;
- 3:
- FTapeLap := Parser.DblValue;
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveConductorDataObj, ParamPointer - NumPropsThisClass)
- end;
-
- {Check for critical errors}
- case ParamPointer of
- 1:
- if (FDiaShield <= 0.0) then
- DoSimpleMsg('Error: Diameter over shield must be positive for TapeShieldData ' + Name, 999);
- 2:
- if (FTapeLayer <= 0.0) then
- DoSimpleMsg('Error: Tape shield thickness must be positive for TapeShieldData ' + Name, 999);
- 3:
- if ((FTapeLap < 0.0) or (FTapeLap > 100.0)) then
- DoSimpleMsg('Error: Tap lap must range from 0 to 100 for TapeShieldData ' + Name, 999);
- end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-function TTSData.MakeLike(const TSName: String): Integer;
-var
- OtherData: TTSDataObj;
- i: Integer;
-begin
- Result := 0;
- OtherData := Find(TSName);
- if OtherData <> NIL then
- with TTSDataObj(DSS.ActiveConductorDataObj) do
- begin
- FDiaShield := OtherData.FDiaShield;
- FTapeLayer := OtherData.FTapeLayer;
- FTapeLap := OtherData.FTapeLap;
- ClassMakeLike(OtherData);
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherData.PropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in TapeShield MakeLike: "' + TSName + '" Not Found.', 102);
-end;
-
-function TTSData.Get_Code: String; // Returns active line code string
+procedure TTSDataObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- Result := TTSDataObj(ElementList.Active).Name;
+ // Check for critical errors
+ case Idx of
+ ord(TProp.DiaShield):
+ if (DiaShield <= 0.0) then
+ DoSimpleMsg('Error: Diameter over shield must be positive for TapeShieldData %s', [Name], 999);
+ ord(TProp.TapeLayer):
+ if (TapeLayer <= 0.0) then
+ DoSimpleMsg('Error: Tape shield thickness must be positive for TapeShieldData %s', [Name], 999);
+ ord(TProp.TapeLap):
+ if ((TapeLap < 0.0) or (TapeLap > 100.0)) then
+ DoSimpleMsg('Error: Tap lap must range from 0 to 100 for TapeShieldData %s', [Name], 999);
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-procedure TTSData.Set_Code(const Value: String); // sets the active TSData
+procedure TTSDataObj.MakeLike(OtherPtr: Pointer);
var
- TSDataObj: TTSDataObj;
+ Other: TObj;
begin
- DSS.ActiveConductorDataObj := NIL;
- TSDataObj := ElementList.First;
- while TSDataObj <> NIL do
- begin
- if CompareText(TSDataObj.Name, Value) = 0 then
- begin
- DSS.ActiveConductorDataObj := TSDataObj;
- Exit;
- end;
- TSDataObj := ElementList.Next;
- end;
- DoSimpleMsg('TSData: "' + Value + '" not Found.', 103);
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ DiaShield := Other.DiaShield;
+ TapeLayer := Other.TapeLayer;
+ TapeLap := Other.TapeLap;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TTSData Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TTSDataObj.Create(ParClass: TDSSClass; const TSDataName: String);
begin
inherited Create(ParClass, TSDataName);
- Name := LowerCase(TSDataName);
+ Name := AnsiLowerCase(TSDataName);
DSSObjType := ParClass.DSSClassType;
- FDiaShield := -1.0;
- FTapeLayer := -1.0;
- FTapeLap := 20.0;
- InitPropertyValues(0);
+ DiaShield := -1.0;
+ TapeLayer := -1.0;
+ TapeLap := 20.0;
end;
destructor TTSDataObj.Destroy;
@@ -242,34 +153,4 @@ destructor TTSDataObj.Destroy;
inherited destroy;
end;
-procedure TTSDataObj.DumpProperties(F: TFileStream; Complete: Boolean);
-var
- i: Integer;
-begin
- inherited DumpProperties(F, Complete);
- with ParentClass do
- begin
- for i := 1 to NumProperties do
- begin
- FSWrite(F, '~ ' + PropertyName^[i] + '=');
- case i of
- 1:
- FSWriteln(F, Format('%.6g', [FDiaShield]));
- 2:
- FSWriteln(F, Format('%.6g', [FTapeLayer]));
- 3:
- FSWriteln(F, Format('%.2g', [FTapeLap]));
- end;
- end;
- end;
-end;
-
-procedure TTSDataObj.InitPropertyValues(ArrayOffset: Integer);
-begin
- PropertyValue[1] := '-1';
- PropertyValue[2] := '-1';
- PropertyValue[3] := '20.0';
- inherited InitPropertyValues(ArrayOffset + NumPropsThisClass);
-end;
-
-end.
+end.
\ No newline at end of file
diff --git a/src/General/TSLineConstants.pas b/src/General/TSLineConstants.pas
index 2b3e84f3c..a304eb20a 100644
--- a/src/General/TSLineConstants.pas
+++ b/src/General/TSLineConstants.pas
@@ -11,7 +11,7 @@ interface
uses
Arraydef,
Ucmatrix,
- Ucomplex,
+ UComplex, DSSUcomplex,
LineUnits,
LineConstants,
CableConstants;
@@ -119,7 +119,7 @@ procedure TTSLineConstants.Calc(f: Double; EarthModel: Integer);
FYCMatrix.Clear;
// add concentric neutrals to the end of conductor list; they are always reduced
- N := FNumConds + FNumPhases;
+ N := FNumConds + FNPhases;
Zmat := TCMatrix.CreateMatrix(N);
{For less than 1 kHz use GMR to better match published data}
@@ -136,24 +136,24 @@ procedure TTSLineConstants.Calc(f: Double; EarthModel: Integer);
if PowerFreq then
begin // for less than 1 kHz, use published GMR
Zi.im := 0.0;
- Zspacing := CmulReal(Lfactor, ln(1.0 / FGMR^[i])); // use GMR
+ Zspacing := Lfactor * ln(1.0 / FGMR^[i]) // use GMR
end
else
begin
- Zspacing := CmulReal(Lfactor, ln(1.0 / Fradius^[i]));
+ Zspacing := Lfactor * ln(1.0 / Fradius^[i]);
end;
- Zmat.SetElement(i, i, Cadd(Zi, Cadd(Zspacing, Get_Ze(i, i, EarthModel))));
+ Zmat.SetElement(i, i, Zi + Zspacing + Get_Ze(i, i, EarthModel));
end;
// TS self impedances
- for i := 1 to FNumPhases do
+ for i := 1 to FNPhases do
begin
ResTS := 0.3183 * RhoTS / (FDiaShield^[i] * FTapeLayer^[i] * sqrt(50.0 / (100.0 - FTapeLap^[i])));
GmrTS := 0.5 * (FDiaShield^[i] - FTapeLayer^[i]); // per Kersting, to center of TS
- Zspacing := CMulReal(Lfactor, ln(1.0 / GmrTS));
+ Zspacing := Lfactor * ln(1.0 / GmrTS);
Zi := cmplx(ResTS, 0.0);
idxi := i + FNumConds;
- Zmat.SetElement(idxi, idxi, Cadd(Zi, Cadd(Zspacing, Get_Ze(i, i, EarthModel))));
+ Zmat.SetElement(idxi, idxi, Zi + Zspacing + Get_Ze(i, i, EarthModel));
end;
// Mutual Impedances - between TS cores and bare neutrals
@@ -162,19 +162,19 @@ procedure TTSLineConstants.Calc(f: Double; EarthModel: Integer);
for j := 1 to i - 1 do
begin
Dij := sqrt(sqr(Fx^[i] - Fx^[j]) + sqr(Fy^[i] - Fy^[j]));
- Zmat.SetElemSym(i, j, Cadd(Cmulreal(Lfactor, ln(1.0 / Dij)), Get_Ze(i, j, EarthModel)));
+ Zmat.SetElemSym(i, j, Lfactor * ln(1.0 / Dij) + Get_Ze(i, j, EarthModel));
end;
end;
// Mutual Impedances - TS to other TS, cores, and bare neutrals
- for i := 1 to FNumPhases do
+ for i := 1 to FNPhases do
begin
idxi := i + FNumConds;
for j := 1 to i - 1 do
begin // TS to other TS
idxj := j + FNumConds;
Dij := sqrt(sqr(Fx^[i] - Fx^[j]) + sqr(Fy^[i] - Fy^[j]));
- Zmat.SetElemSym(idxi, idxj, Cadd(Cmulreal(Lfactor, ln(1.0 / Dij)), Get_Ze(i, j, EarthModel)));
+ Zmat.SetElemSym(idxi, idxj, Lfactor * ln(1.0 / Dij) + Get_Ze(i, j, EarthModel));
end;
for j := 1 to FNumConds do
begin // CN to cores and bare neutrals
@@ -188,7 +188,7 @@ procedure TTSLineConstants.Calc(f: Double; EarthModel: Integer);
begin // TS to another phase or bare neutral
Dij := sqrt(sqr(Fx^[i] - Fx^[j]) + sqr(Fy^[i] - Fy^[j]));
end;
- Zmat.SetElemSym(idxi, idxj, Cadd(Cmulreal(Lfactor, ln(1.0 / Dij)), Get_Ze(i, j, EarthModel)));
+ Zmat.SetElemSym(idxi, idxj, Lfactor * ln(1.0 / Dij) + Get_Ze(i, j, EarthModel));
end;
end;
@@ -204,7 +204,7 @@ procedure TTSLineConstants.Calc(f: Double; EarthModel: Integer);
// for shielded cables, build the capacitance matrix directly
// assumes the insulation may lie between semicon layers
- for i := 1 to FNumPhases do
+ for i := 1 to FNPhases do
begin
Yfactor := twopi * e0 * FEpsR^[i] * Fw; // includes frequency so C==>Y
RadOut := 0.5 * FDiaIns^[i];
@@ -236,6 +236,4 @@ destructor TTSLineConstants.Destroy;
inherited;
end;
-initialization
-
end.
diff --git a/src/General/TempShape.pas b/src/General/TempShape.pas
index 2adbbec1c..b159172e6 100644
--- a/src/General/TempShape.pas
+++ b/src/General/TempShape.pas
@@ -7,46 +7,37 @@
----------------------------------------------------------
}
-{ 2-13-2011 Converted from Loadshape.
- Temperature shapes (Tshapes) would generally be defined to correspond to loadshapes
-
-}
-
interface
-{The Tshape object is a general DSS object used by all circuits
- as a reference for obtaining yearly, daily, and other Temperature shapes.
-
- The values are set by the normal New and Edit PROCEDUREs for any DSS object.
-
- The values may be retrieved by setting the Code Property in the Tshape Class.
- This sets the active Tshape object to be the one referenced by the Code Property;
-
- Then the values of that code can be retrieved via the public variables. Or you
- can pick up the ActiveTShapeObj object and save the direct reference to the object.
-
- Tshapes default to fixed interval data (like Loadshapes). If the Interval is specified to be 0.0,
- then both time and temperature data are expected. If the Interval is greater than 0.0,
- the user specifies only the Temperatures. The Hour command is ignored and the files are
- assumed to contain only the temperature data.
-
- The Interval may also be specified in seconds (sinterval) or minutes (minterval).
-
- The user may place the data in CSV or binary files as well as passing through the
- command interface. Obviously, for large amounts of data such as 8760 load curves, the
- command interface is cumbersome. CSV files are text separated by commas, one interval to a line.
- There are two binary formats permitted: 1) a file of Singles; 2) a file of Doubles.
-
- For fixed interval data, only the Temperature values are expected. Therefore, the CSV format would
- contain only one number per line. The two binary formats are packed.
-
- For variable interval data, (hour, Temperature) pairs are expected in both formats.
-
- The Mean and Std Deviation are automatically computed upon demand when new series of points is entered.
-
-
-
- }
+// The Tshape object is a general DSS object used by all circuits
+// as a reference for obtaining yearly, daily, and other Temperature shapes.
+//
+// The values are set by the normal New and Edit PROCEDUREs for any DSS object.
+//
+// The values may be retrieved by setting the Code Property in the Tshape Class.
+// This sets the active Tshape object to be the one referenced by the Code Property;
+//
+// Then the values of that code can be retrieved via the public variables. Or you
+// can pick up the ActiveTShapeObj object and save the direct reference to the object.
+//
+// Tshapes default to fixed interval data (like Loadshapes). If the Interval is specified to be 0.0,
+// then both time and temperature data are expected. If the Interval is greater than 0.0,
+// the user specifies only the Temperatures. The Hour command is ignored and the files are
+// assumed to contain only the temperature data.
+//
+// The Interval may also be specified in seconds (sinterval) or minutes (minterval).
+//
+// The user may place the data in CSV or binary files as well as passing through the
+// command interface. Obviously, for large amounts of data such as 8760 load curves, the
+// command interface is cumbersome. CSV files are text separated by commas, one interval to a line.
+// There are two binary formats permitted: 1) a file of Singles; 2) a file of Doubles.
+//
+// For fixed interval data, only the Temperature values are expected. Therefore, the CSV format would
+// contain only one number per line. The two binary formats are packed.
+//
+// For variable interval data, (hour, Temperature) pairs are expected in both formats.
+//
+// The Mean and Std Deviation are automatically computed upon demand when new series of points is entered.
uses
Classes,
@@ -56,45 +47,45 @@ interface
Arraydef;
type
+{$SCOPEDENUMS ON}
+ TTShapeProp = (
+ INVALID = 0,
+ npts = 1, // Number of points to expect
+ interval = 2, // default = 1.0;
+ temp = 3, // vector of temperature values
+ hour = 4, // vector of hour values
+ mean = 5, // set the mean temp (otherwise computed)
+ stddev = 6, // set the std dev of the temp (otherwise computed)
+ csvfile = 7, // Switch input to a csvfile
+ sngfile = 8, // switch input to a binary file of singles
+ dblfile = 9, // switch input to a binary file of singles
+ sinterval = 10, // Interval in seconds
+ minterval = 11, // Interval in minutes
+ action = 12 // Interval in minutes
+ );
+{$SCOPEDENUMS OFF}
TTShape = class(TDSSClass)
- PRIVATE
-
- function Get_Code: String; // Returns active TShape string
- procedure Set_Code(const Value: String); // sets the active TShape
-
- procedure DoCSVFile(const FileName: String);
- procedure DoSngFile(const FileName: String);
- procedure DoDblFile(const FileName: String);
PROTECTED
- procedure DefineProperties;
- function MakeLike(const ShapeName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
function Find(const ObjName: String; const ChangeActive: Boolean=True): Pointer; OVERRIDE; // Find an obj of this class by name
-
- // Set this property to point ActiveTShapeObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
-
end;
TTShapeObj = class(TDSSObject)
PRIVATE
LastValueAccessed,
FNumPoints: Integer; // Number of points in curve
- ArrayPropertyIndex: Integer;
FStdDevCalculated: Boolean;
FMean,
FStdDev: Double;
function Get_Interval: Double;
- procedure Set_NumPoints(const Value: Integer);
procedure SaveToDblFile;
procedure SaveToSngFile;
procedure CalcMeanandStdDev;
@@ -104,35 +95,30 @@ TTShapeObj = class(TDSSObject)
procedure Set_StdDev(const Value: Double); // Normalize the curve presently in memory
PUBLIC
-
Interval: Double; //=0.0 then random interval (hr)
Hours, // Time values (hr) if Interval > 0.0 Else nil
TValues: pDoubleArray; // Temperatures
+ csvfile, dblfile, sngfile: String;
constructor Create(ParClass: TDSSClass; const TShapeName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
function GetTemperature(hr: Double): Double; // Get Temperatures at specified time, hr
function Temperature(i: Integer): Double; // get Temperatures by index
function Hour(i: Integer): Double; // get hour corresponding to point index
-
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
- property NumPoints: Integer READ FNumPoints WRITE Set_NumPoints;
+ property NumPoints: Integer READ FNumPoints;
property PresentInterval: Double READ Get_Interval;
property Mean: Double READ Get_Mean WRITE Set_Mean;
property StdDev: Double READ Get_StdDev WRITE Set_StdDev;
-
end;
-
implementation
uses
- ParserDel,
+ BufStream,
DSSClassDefs,
DSSGlobals,
Sysutils,
@@ -140,447 +126,196 @@ implementation
Utilities,
Math,
DSSPointerList,
- BufStream,
DSSHelper,
DSSObjectHelper,
TypInfo;
+type
+ TObj = TTShapeObj;
+ TProp = TTShapeProp;
+{$PUSH}
+{$Z4} // keep enums as int32 values
+ TTShapeAction = (
+ DblSave = 0,
+ SngSave = 1
+ );
+{$POP}
const
- NumPropsThisClass = 12;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
+ ActionEnum: TDSSEnum;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TTShape.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TTShape.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'TShape';
- DSSClassType := DSS_OBJECT;
-
- ActiveElement := 0;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ ActionEnum := TDSSEnum.Create('TShape: Action', True, 1, 1,
+ ['DblSave', 'SngSave'],
+ [ord(TTShapeAction.DblSave), ord(TTShapeAction.SngSave)]);
+ end;
+ inherited Create(dssContext, DSS_OBJECT, 'TShape');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TTShape.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTShape.DefineProperties;
-begin
-
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
- PropertyName[1] := 'npts'; // Number of points to expect
- PropertyName[2] := 'interval'; // default = 1.0;
- PropertyName[3] := 'temp'; // vector of temperature values
- PropertyName[4] := 'hour'; // vector of hour values
- PropertyName[5] := 'mean'; // set the mean temp (otherwise computed)
- PropertyName[6] := 'stddev'; // set the std dev of the temp (otherwise computed)
- PropertyName[7] := 'csvfile'; // Switch input to a csvfile
- PropertyName[8] := 'sngfile'; // switch input to a binary file of singles
- PropertyName[9] := 'dblfile'; // switch input to a binary file of singles
- PropertyName[10] := 'sinterval'; // Interval in seconds
- PropertyName[11] := 'minterval'; // Interval in minutes
- PropertyName[12] := 'action'; // Interval in minutes
-
- // define Property help values
-
- PropertyHelp[1] := 'Max number of points to expect in temperature shape vectors. This gets reset to the number of Temperature values ' +
- 'found if less than specified.'; // Number of points to expect
- PropertyHelp[2] := 'Time interval for fixed interval data, hrs. Default = 1. ' +
- 'If Interval = 0 then time data (in hours) may be at irregular intervals and time value must be specified using either the Hour property or input files. ' +
- 'Then values are interpolated when Interval=0, but not for fixed interval data. ' + CRLF + CRLF +
- 'See also "sinterval" and "minterval".'; // default = 1.0;
- PropertyHelp[3] := 'Array of temperature values. Units should be compatible with the object using the data. ' +
- 'You can also use the syntax: ' + CRLF +
- 'Temp = (file=filename) !for text file one value per line' + CRLF +
- 'Temp = (dblfile=filename) !for packed file of doubles' + CRLF +
- 'Temp = (sngfile=filename) !for packed file of singles ' + CRLF + CRLF +
- 'Note: this property will reset Npts if the number of values in the files are fewer.'; // vextor of hour values
- PropertyHelp[4] := 'Array of hour values. Only necessary to define this property for variable interval data.' +
- ' If the data are fixed interval, do not use this property. ' +
- 'You can also use the syntax: ' + CRLF +
- 'hour = (file=filename) !for text file one value per line' + CRLF +
- 'hour = (dblfile=filename) !for packed file of doubles' + CRLF +
- 'hour = (sngfile=filename) !for packed file of singles '; // vextor of hour values
- PropertyHelp[5] := 'Mean of the temperature curve values. This is computed on demand the first time a ' +
- 'value is needed. However, you may set it to another value independently. ' +
- 'Used for Monte Carlo load simulations.'; // set the mean (otherwise computed)
- PropertyHelp[6] := 'Standard deviation of the temperatures. This is computed on demand the first time a ' +
- 'value is needed. However, you may set it to another value independently.' +
- 'Is overwritten if you subsequently read in a curve' + CRLF + CRLF +
- 'Used for Monte Carlo load simulations.'; // set the std dev (otherwise computed)
- PropertyHelp[7] := 'Switch input of temperature curve data to a csv file ' +
- 'containing (hour, Temp) points, or simply (Temp) values for fixed time interval data, one per line. ' +
- 'NOTE: This action may reset the number of points to a lower value.'; // Switch input to a csvfile
- PropertyHelp[8] := 'Switch input of temperature curve data to a binary file of singles ' +
- 'containing (hour, Temp) points, or simply (Temp) values for fixed time interval data, packed one after another. ' +
- 'NOTE: This action may reset the number of points to a lower value.'; // switch input to a binary file of singles
- PropertyHelp[9] := 'Switch input of temperature curve data to a binary file of doubles ' +
- 'containing (hour, Temp) points, or simply (Temp) values for fixed time interval data, packed one after another. ' +
- 'NOTE: This action may reset the number of points to a lower value.'; // switch input to a binary file of singles
- PropertyHelp[10] := 'Specify fixed interval in SECONDS. Alternate way to specify Interval property.';
- PropertyHelp[11] := 'Specify fixed interval in MINUTES. Alternate way to specify Interval property.';
- PropertyHelp[12] := '{DblSave | SngSave} After defining temperature curve data... ' +
- 'Setting action=DblSave or SngSave will cause the present "Temp" values to be written to ' +
- 'either a packed file of double or single. The filename is the Tshape name. '; // Action
-
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TTShape.NewObject(const ObjName: String): Integer;
-begin
- // create a new object of this class and add to list
- DSS.ActiveDSSObject := TTShapeObj.Create(Self, ObjName);
- Result := AddObjectToList(DSS.ActiveDSSObject);
-end;
-
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TTShape.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
+procedure DoAction(Obj: TObj; action: TTShapeAction);
begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveTShapeObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActiveTShapeObj;
-
- with DSS.ActiveTShapeObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
-
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 57610);
- 1:
- NumPoints := Parser.Intvalue;
- 2:
- Interval := Parser.DblValue;
- 3:
- begin
- ReAllocmem(TValues, Sizeof(TValues^[1]) * NumPoints);
- // Allow possible Resetting (to a lower value) of num points when specifying temperatures not Hours
- NumPoints := InterpretDblArray(Param, NumPoints, TValues); // Parser.ParseAsVector(Npts, Temperatures);
- end;
- 4:
- begin
- ReAllocmem(Hours, Sizeof(Hours^[1]) * NumPoints);
- NumPoints := InterpretDblArray(Param, NumPoints, Hours); // Parser.ParseAsVector(Npts, Hours);
- end;
- 5:
- Mean := Parser.DblValue;
- 6:
- StdDev := Parser.DblValue;
- 7:
- DoCSVFile(AdjustInputFilePath(Param));
- 8:
- DoSngFile(AdjustInputFilePath(Param));
- 9:
- DoDblFile(AdjustInputFilePath(Param));
- 10:
- Interval := Parser.DblValue / 3600.0; // Convert seconds to hr
- 11:
- Interval := Parser.DblValue / 60.0; // Convert minutes to hr
- 12:
- case lowercase(Param)[1] of
- 'd':
- SaveToDblFile;
- 's':
- SaveToSngFile;
- end;
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveTShapeObj, ParamPointer - NumPropsThisClass)
- end;
-
- case ParamPointer of
- 3, 7, 8, 9:
- begin
- FStdDevCalculated := FALSE; // now calculated on demand
- ArrayPropertyIndex := ParamPointer;
- NumPoints := FNumPoints; // Keep Properties in order for save command
- end;
-
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end; {While}
-
- end; {WITH}
+ case action of
+ TTShapeAction.DblSave:
+ Obj.SaveToDblFile;
+ TTShapeAction.SngSave:
+ Obj.SaveToSngFile;
+ end;
end;
-function TTShape.Find(const ObjName: String; const ChangeActive: Boolean): Pointer;
+function GetMean(obj: TObj): Double;
begin
- if (Length(ObjName) = 0) or (CompareText(ObjName, 'none') = 0) then
- Result := NIL
- else
- Result := inherited Find(ObjName, ChangeActive);
+ Result := obj.Mean;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TTShape.MakeLike(const ShapeName: String): Integer;
-var
- OtherTShape: TTShapeObj;
- i: Integer;
+function GetStdDev(obj: TObj): Double;
begin
- Result := 0;
- {See if we can find this line code in the present collection}
- OtherTShape := Find(ShapeName);
- if OtherTShape <> NIL then
- with DSS.ActiveTShapeObj do
- begin
- NumPoints := OtherTShape.NumPoints;
- Interval := OtherTShape.Interval;
- ReallocMem(TValues, SizeOf(TValues^[1]) * NumPoints);
- for i := 1 to NumPoints do
- TValues^[i] := OtherTShape.TValues^[i];
- if Interval > 0.0 then
- ReallocMem(Hours, 0)
- else
- begin
- ReallocMem(Hours, SizeOf(Hours^[1]) * NumPoints);
- for i := 1 to NumPoints do
- Hours^[i] := OtherTShape.Hours^[i];
- end;
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherTShape.PropertyValue[i];
- end
- else
- DoSimpleMsg('Error in TShape MakeLike: "' + ShapeName + '" Not Found.', 57611);
-
-
+ Result := obj.StdDev;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TTShape.Get_Code: String; // Returns active line code string
-var
- TShapeObj: TTShapeObj;
+procedure TTShape.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
+ Numproperties := NumPropsThisClass;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // doubles
+ PropertyOffset[ord(TProp.interval)] := ptruint(@obj.Interval);
+
+ PropertyOffset[ord(TProp.mean)] := ptruint(@obj.FMean);
+ PropertyReadFunction[ord(TProp.mean)] := @GetMean;
+ PropertyFlags[ord(TProp.mean)] := [TPropertyFlag.ReadByFunction];
+
+ PropertyOffset[ord(TProp.stddev)] := ptruint(@obj.FStdDev);
+ PropertyReadFunction[ord(TProp.stddev)] := @GetStdDev;
+ PropertyFlags[ord(TProp.stddev)] := [TPropertyFlag.ReadByFunction];
+
+ // advanced doubles
+ PropertyOffset[ord(TProp.sinterval)] := ptruint(@obj.Interval);
+ PropertyScale[ord(TProp.sinterval)] := 1 / 3600.0;
+ PropertyFlags[ord(TProp.sinterval)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.sinterval)] := ord(TProp.interval);
+
+ PropertyOffset[ord(TProp.minterval)] := ptruint(@obj.Interval);
+ PropertyScale[ord(TProp.minterval)] := 1 / 60.0;
+ PropertyFlags[ord(TProp.minterval)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.minterval)] := ord(TProp.interval);
+
+ // strings
+ PropertyType[ord(TProp.csvfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.csvfile)] := ptruint(@obj.csvfile);
+ PropertyFlags[ord(TProp.csvfile)] := [TPropertyFlag.IsFilename];
+
+ PropertyType[ord(TProp.dblfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.dblfile)] := ptruint(@obj.dblfile);
+ PropertyFlags[ord(TProp.dblfile)] := [TPropertyFlag.IsFilename];
+
+ PropertyType[ord(TProp.sngfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.sngfile)] := ptruint(@obj.sngfile);
+ PropertyFlags[ord(TProp.sngfile)] := [TPropertyFlag.IsFilename];
+
+ // integer
+ PropertyType[ord(TProp.Npts)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Npts)] := ptruint(@obj.FNumPoints);
+
+ // double arrays
+ PropertyType[ord(TProp.hour)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.hour)] := ptruint(@obj.Hours);
+ PropertyOffset2[ord(TProp.hour)] := ptruint(@obj.FNumPoints);
+
+ PropertyType[ord(TProp.temp)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.temp)] := ptruint(@obj.TValues);
+ PropertyOffset2[ord(TProp.temp)] := ptruint(@obj.FNumPoints);
+
+ // enum action
+ PropertyType[ord(TProp.Action)] := TPropertyType.StringEnumActionProperty;
+ PropertyOffset[ord(TProp.Action)] := ptruint(@DoAction);
+ PropertyOffset2[ord(TProp.Action)] := PtrInt(ActionEnum);
- TShapeObj := ElementList.Active;
- Result := TShapeObj.Name;
-
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTShape.Set_Code(const Value: String); // sets the active TShape
-
+function TTShape.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- TShapeObj: TTShapeObj;
-
+ Obj: TObj;
begin
-
- DSS.ActiveTShapeObj := NIL;
- TShapeObj := ElementList.First;
- while TShapeObj <> NIL do
- begin
-
- if CompareText(TShapeObj.Name, Value) = 0 then
- begin
- DSS.ActiveTShapeObj := TShapeObj;
- Exit;
- end;
-
- TShapeObj := ElementList.Next;
- end;
-
- DoSimpleMsg('TShape: "' + Value + '" not Found.', 57612);
-
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTShape.DoCSVFile(const FileName: String);
-
-var
- F: TBufferedFileStream = nil;
- i: Integer;
- s: String;
-
+procedure TTShapeObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- try
- F := TBufferedFileStream.Create(FileName, fmOpenRead);
- except
- DoSimpleMsg('Error Opening File: "' + FileName, 57613);
- FreeAndNil(F);
- Exit;
+ case Idx of
+ ord(TProp.csvfile):
+ DoCSVFile(DSS, Hours, TValues, FNumPoints, (Interval <> 0.0), csvfile, ParentClass.Name);
+ ord(TProp.sngfile):
+ DoSngFile(DSS, Hours, TValues, FNumPoints, (Interval <> 0.0), sngfile, ParentClass.Name);
+ ord(TProp.dblfile):
+ DoDblFile(DSS, Hours, TValues, FNumPoints, (Interval <> 0.0), dblfile, ParentClass.Name);
end;
-
- try
-
- with DSS.ActiveTShapeObj do
+ case Idx of
+ ord(TProp.npts):
+ // Force as the always first property when saving in a later point
+ PrpSequence[Idx] := -10;
+ 3, 7, 8, 9:
begin
- ReAllocmem(TValues, Sizeof(TValues^[1]) * NumPoints);
- if Interval = 0.0 then
- ReAllocmem(Hours, Sizeof(Hours^[1]) * NumPoints);
- i := 0;
- while ((F.Position + 1) < F.Size) and (i < FNumPoints) do
- begin
- Inc(i);
- FSReadln(F, s); // read entire line and parse with AuxParser
- {AuxParser allows commas or white space}
- with AuxParser do
- begin
- CmdString := s;
- if Interval = 0.0 then
- begin
- NextParam;
- Hours^[i] := DblValue;
- end;
- NextParam;
- TValues^[i] := DblValue;
- end;
- end;
- FreeAndNil(F);
- if i <> FNumPoints then
- NumPoints := i;
- end;
-
- except
- On E: Exception do
- begin
- DoSimpleMsg('Error Processing CSV File: "' + FileName + '. ' + E.Message, 57614);
- FreeAndNil(F);
- Exit;
+ FStdDevCalculated := FALSE; // now calculated on demand
+ PropertySideEffects(ord(TProp.npts));
end;
end;
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTShape.DoSngFile(const FileName: String);
-var
- F: TFileStream = nil;
- Hr,
- M: Single;
- i: Integer;
-
+function TTShape.Find(const ObjName: String; const ChangeActive: Boolean): Pointer;
begin
- try
- F := TFileStream.Create(FileName, fmOpenRead);
- except
- DoSimpleMsg('Error Opening File: "' + FileName, 57615);
- FreeAndNil(F);
- Exit;
- end;
-
- try
- with DSS.ActiveTShapeObj do
- begin
- ReAllocmem(TValues, Sizeof(TValues^[1]) * NumPoints);
- if Interval = 0.0 then
- ReAllocmem(Hours, Sizeof(Hours^[1]) * NumPoints);
- i := 0;
- while ((F.Position + 1) < F.Size) and (i < FNumPoints) do
- begin
- Inc(i);
- if Interval = 0.0 then
- begin
- if F.Read(Hr, SizeOf(Hr)) <> SizeOf(Hr) then
- Break;
- Hours^[i] := Hr;
- end;
- if F.Read(M, SizeOf(M)) <> SizeOf(M) then
- Break;
- TValues^[i] := M;
- end;
- FreeAndNil(F);
- if i <> FNumPoints then
- NumPoints := i;
- end;
- except
- DoSimpleMsg('Error Processing TShape File: "' + FileName, 57616);
- FreeAndNil(F);
- Exit;
- end;
-
+ if (Length(ObjName) = 0) or (CompareText(ObjName, 'none') = 0) then
+ Result := NIL
+ else
+ Result := inherited Find(ObjName, ChangeActive);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTShape.DoDblFile(const FileName: String);
+procedure TTShapeObj.MakeLike(OtherPtr: Pointer);
var
- F: TFileStream = nil;
+ Other: TObj;
i: Integer;
-
begin
- try
- F := TFileStream.Create(FileName, fmOpenRead);
- except
- DoSimpleMsg('Error Opening File: "' + FileName, 57617);
- FreeAndNil(F);
- Exit;
- end;
-
- try
- with DSS.ActiveTShapeObj do
- begin
- ReAllocmem(TValues, Sizeof(TValues^[1]) * NumPoints);
- if Interval = 0.0 then
- ReAllocmem(Hours, Sizeof(Hours^[1]) * NumPoints);
- i := 0;
- while ((F.Position + 1) < F.Size) and (i < FNumPoints) do
- begin
- Inc(i);
- if Interval = 0.0 then
- if F.Read(Hours^[i], SizeOf(Double)) <> SizeOf(Double) then
- Break;
-
- if F.Read(TValues^[i], SizeOf(Double)) <> SizeOf(Double) then
- Break;
- end;
- FreeAndNil(F);
- if i <> FNumPoints then
- NumPoints := i;
- end;
- except
- DoSimpleMsg('Error Processing Tshape File: "' + FileName, 57618);
- FreeAndNil(F);
- Exit;
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNumPoints := Other.NumPoints;
+ Interval := Other.Interval;
+ ReallocMem(TValues, SizeOf(TValues^[1]) * NumPoints);
+ for i := 1 to NumPoints do
+ TValues^[i] := Other.TValues^[i];
+ if Interval > 0.0 then
+ ReallocMem(Hours, 0)
+ else
+ begin
+ ReallocMem(Hours, SizeOf(Hours^[1]) * NumPoints);
+ for i := 1 to NumPoints do
+ Hours^[i] := Other.Hours^[i];
end;
-
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TTShape Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TTShapeObj.Create(ParClass: TDSSClass; const TShapeName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(TShapeName);
+ Name := AnsiLowerCase(TShapeName);
DSSObjType := ParClass.DSSClassType;
LastValueAccessed := 1;
@@ -591,25 +326,20 @@ constructor TTShapeObj.Create(ParClass: TDSSClass; const TShapeName: String);
TValues := NIL;
FStdDevCalculated := FALSE; // calculate on demand
- ArrayPropertyIndex := 0;
-
- InitPropertyValues(0);
-
+ csvfile := '';
+ dblfile := '';
+ sngfile := '';
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TTShapeObj.Destroy;
begin
-
ReallocMem(Hours, 0);
if Assigned(TValues) then
ReallocMem(TValues, 0);
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function TTShapeObj.GetTemperature(hr: Double): Double;
-
// This FUNCTION returns the Temperature for the given hour.
// If no points exist in the curve, the result is 0.0
// If there are fewer points than requested, the curve is simply assumed to repeat
@@ -617,94 +347,80 @@ function TTShapeObj.GetTemperature(hr: Double): Double;
// same day over and over again.
// The value returned is the nearest to the interval requested. Thus if you request
// hour=12.25 and the interval is 1.0, you will get interval 12.
-
var
Index, i: Integer;
-
begin
-
Result := 0.0; // default return value if no points in curve
- if FNumPoints > 0 then // Handle Exceptional cases
- if FNumPoints = 1 then
+ if FNumPoints <= 0 then // Handle Exceptional cases
+ Exit;
+
+ if FNumPoints = 1 then
+ begin
+ Result := TValues^[1];
+ Exit;
+ end;
+
+ if Interval > 0.0 then
+ begin
+ Index := round(hr / Interval);
+ if Index > FNumPoints then
+ Index := Index mod FNumPoints; // Wrap around using remainder
+ if Index = 0 then
+ Index := FNumPoints;
+ Result := TValues^[Index];
+ Exit;
+ end;
+
+ // For random interval
+
+ // Start with previous value accessed under the assumption that most
+ // of the time, this FUNCTION will be called sequentially
+
+ // Normalize Hr to max hour in curve to get wraparound
+ if (Hr > Hours^[FNumPoints]) then
+ begin
+ Hr := Hr - Trunc(Hr / Hours^[FNumPoints]) * Hours^[FNumPoints];
+ end;
+
+ if (Hours^[LastValueAccessed] > Hr) then
+ LastValueAccessed := 1; // Start over from Beginning
+ for i := LastValueAccessed + 1 to FNumPoints do
+ begin
+ if (Abs(Hours^[i] - Hr) < 0.00001) then // If close to an actual point, just use it.
begin
- Result := TValues^[1];
+ Result := TValues^[i];
+ LastValueAccessed := i;
+ Exit;
end
else
+ if (Hours^[i] > Hr) then // Interpolate for temperature
begin
- if Interval > 0.0 then
- begin
- Index := round(hr / Interval);
- if Index > FNumPoints then
- Index := Index mod FNumPoints; // Wrap around using remainder
- if Index = 0 then
- Index := FNumPoints;
- Result := TValues^[Index];
- end
- else
- begin
- // For random interval
-
- { Start with previous value accessed under the assumption that most
- of the time, this FUNCTION will be called sequentially}
-
- {Normalize Hr to max hour in curve to get wraparound}
- if (Hr > Hours^[FNumPoints]) then
- begin
- Hr := Hr - Trunc(Hr / Hours^[FNumPoints]) * Hours^[FNumPoints];
- end;
-
- if (Hours^[LastValueAccessed] > Hr) then
- LastValueAccessed := 1; // Start over from Beginning
- for i := LastValueAccessed + 1 to FNumPoints do
- begin
- if (Abs(Hours^[i] - Hr) < 0.00001) then // If close to an actual point, just use it.
- begin
- Result := TValues^[i];
- LastValueAccessed := i;
- Exit;
- end
- else
- if (Hours^[i] > Hr) then // Interpolate for temperature
- begin
- LastValueAccessed := i - 1;
- Result := TValues^[LastValueAccessed] +
- (Hr - Hours^[LastValueAccessed]) / (Hours^[i] - Hours^[LastValueAccessed]) *
- (TValues^[i] - TValues^[LastValueAccessed]);
- Exit;
- end;
- end;
-
- // If we fall through the loop, just use last value
- LastValueAccessed := FNumPoints - 1;
- Result := TValues^[FNumPoints];
- end;
+ LastValueAccessed := i - 1;
+ Result := TValues^[LastValueAccessed] +
+ (Hr - Hours^[LastValueAccessed]) / (Hours^[i] - Hours^[LastValueAccessed]) *
+ (TValues^[i] - TValues^[LastValueAccessed]);
+ Exit;
end;
-
+ end;
+ // If we fall through the loop, just use last value
+ LastValueAccessed := FNumPoints - 1;
+ Result := TValues^[FNumPoints];
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TTShapeObj.CalcMeanandStdDev;
-
begin
-
if FNumPoints > 0 then
if Interval > 0.0 then
RCDMeanandStdDev(TValues, FNumPoints, FMean, FStdDev)
else
CurveMeanAndStdDev(TValues, Hours, FNumPoints, FMean, FStdDev);
- PropertyValue[5] := Format('%.8g', [FMean]);
- PropertyValue[6] := Format('%.8g', [FStdDev]);
-
FStdDevCalculated := TRUE;
end;
-
function TTShapeObj.Get_Interval: Double;
begin
-
if Interval > 0.0 then
Result := Interval
else
@@ -714,7 +430,6 @@ function TTShapeObj.Get_Interval: Double;
else
Result := 0.0;
end;
-
end;
function TTShapeObj.Get_Mean: Double;
@@ -733,7 +448,6 @@ function TTShapeObj.Get_StdDev: Double;
function TTShapeObj.Temperature(i: Integer): Double;
begin
-
if (i <= FNumPoints) and (i > 0) then
begin
Result := TValues^[i];
@@ -741,12 +455,10 @@ function TTShapeObj.Temperature(i: Integer): Double;
end
else
Result := 0.0;
-
end;
function TTShapeObj.Hour(i: Integer): Double;
begin
-
if Interval = 0 then
begin
if (i <= FNumPoints) and (i > 0) then
@@ -762,151 +474,65 @@ function TTShapeObj.Hour(i: Integer): Double;
Result := Hours^[i] * Interval;
LastValueAccessed := i;
end;
-
-end;
-
-
-procedure TTShapeObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
-
-end;
-
-function TTShapeObj.GetPropertyValue(Index: Integer): String;
-begin
- Result := '';
-
- case Index of
- 2:
- Result := Format('%.8g', [Interval]);
- 3:
- Result := GetDSSArray_Real(FNumPoints, TValues);
- 4:
- if Hours <> NIL then
- Result := GetDSSArray_Real(FNumPoints, Hours);
- 5:
- Result := Format('%.8g', [Mean]);
- 6:
- Result := Format('%.8g', [StdDev]);
- 10:
- Result := Format('%.8g', [Interval * 3600.0]);
- 11:
- Result := Format('%.8g', [Interval * 60.0]);
- else
- Result := inherited GetPropertyValue(index);
- end;
-
-end;
-
-procedure TTShapeObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := '0'; // Number of points to expect
- PropertyValue[2] := '1'; // default = 1.0 hr;
- PropertyValue[3] := ''; // vector of multiplier values
- PropertyValue[4] := ''; // vextor of hour values
- PropertyValue[5] := '0'; // set the mean (otherwise computed)
- PropertyValue[6] := '0'; // set the std dev (otherwise computed)
- PropertyValue[7] := ''; // Switch input to a csvfile
- PropertyValue[8] := ''; // switch input to a binary file of singles
- PropertyValue[9] := ''; // switch input to a binary file of singles
- PropertyValue[10] := '3600'; // seconds
- PropertyValue[11] := '60'; // minutes
- PropertyValue[12] := ''; // action option .
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
end;
procedure TTShapeObj.SaveToDblFile;
-
var
F: TFileStream = nil;
Fname: String;
begin
- if Assigned(TValues) then
+ if not Assigned(TValues) then
begin
- try
- FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s.dbl', [Name]);
- F := TFileStream.Create(FName, fmCreate);
- F.WriteBuffer(TValues^[1], NumPoints * SizeOf(Double));
- DSS.GlobalResult := 'Temp=[dblfile=' + FName + ']';
- finally
- FreeAndNil(F);
- end;
-
- end
- else
- DoSimpleMsg('Tshape.' + Name + ' Temperatures not defined.', 57622);
+ DoSimpleMsg('%s Temperatures not defined.', [FullName], 57622);
+ Exit;
+ end;
+ try
+ FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s.dbl', [Name]);
+ F := TBufferedFileStream.Create(FName, fmCreate);
+ F.WriteBuffer(TValues^[1], NumPoints * SizeOf(Double));
+ DSS.GlobalResult := 'Temp=[dblfile=' + FName + ']';
+ finally
+ FreeAndNil(F);
+ end;
end;
procedure TTShapeObj.SaveToSngFile;
-
var
F: TFileStream = nil;
i: Integer;
Fname: String;
Temp: Single;
-
begin
- if Assigned(TValues) then
+ if not Assigned(TValues) then
begin
- try
- FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s.sng', [Name]);
- F := TFileStream.Create(FName, fmCreate);
- for i := 1 to NumPoints do
- begin
- Temp := TValues^[i];
- F.WriteBuffer(Temp, SizeOf(Temp));
- end;
- DSS.GlobalResult := 'Temp=[sngfile=' + FName + ']';
- finally
- FreeAndNil(F);
+ DoSimpleMsg('%s Temperatures not defined.', [FullName], 57623);
+ Exit;
+ end;
+ try
+ FName := DSS.OutputDirectory {CurrentDSSDir} + Format('%s.sng', [Name]);
+ F := TBufferedFileStream.Create(FName, fmCreate);
+ for i := 1 to NumPoints do
+ begin
+ Temp := TValues^[i];
+ F.WriteBuffer(Temp, SizeOf(Temp));
end;
-
-
- end
- else
- DoSimpleMsg('Tshape.' + Name + ' Temperatures not defined.', 57623);
-
-
+ DSS.GlobalResult := 'Temp=[sngfile=' + FName + ']';
+ finally
+ FreeAndNil(F);
+ end;
end;
-
procedure TTShapeObj.Set_Mean(const Value: Double);
begin
FStdDevCalculated := TRUE;
FMean := Value;
end;
-procedure TTShapeObj.Set_NumPoints(const Value: Integer);
-begin
- PropertyValue[1] := IntToStr(Value); // Update property list variable
-
- // Reset array property values to keep them in propoer order in Save
-
- if ArrayPropertyIndex > 0 then
- PropertyValue[ArrayPropertyIndex] := PropertyValue[ArrayPropertyIndex];
-
- FNumPoints := Value; // Now assign the value
-end;
-
procedure TTShapeObj.Set_StdDev(const Value: Double);
begin
FStdDevCalculated := TRUE;
FStdDev := Value;
end;
+finalization ActionEnum.Free;
end.
diff --git a/src/General/WireData.pas b/src/General/WireData.pas
index 780ace1f9..9d1fd81a1 100644
--- a/src/General/WireData.pas
+++ b/src/General/WireData.pas
@@ -9,8 +9,7 @@
interface
-{Used for overhead line impedances.
-}
+// Used for overhead line impedances.
uses
Classes,
@@ -21,40 +20,28 @@ interface
type
TWireData = class(TConductorData)
- PRIVATE
- function Get_Code: String; // Returns active line code string
- procedure Set_Code(const Value: String); // sets the active WireData
PROTECTED
- procedure DefineProperties;
- function MakeLike(const WireName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
- // Set this property to point ActiveWireDataObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TWireDataObj = class(TConductorDataObj)
PUBLIC
constructor Create(ParClass: TDSSClass; const WireDataName: String);
destructor Destroy; OVERRIDE;
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
end;
implementation
uses
- ParserDel,
DSSGlobals,
DSSClassDefs,
Sysutils,
- Ucomplex,
+ UComplex, DSSUcomplex,
Arraydef,
LineUNits,
Utilities,
@@ -62,20 +49,15 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TWireDataObj;
+
const
NumPropsThisClass = 0; // because they were all moved to ConductorData
-constructor TWireData.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TWireData.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'WireData';
- DSSClassType := DSS_OBJECT;
- ActiveElement := 0;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, DSS_OBJECT, 'WireData');
end;
destructor TWireData.Destroy;
@@ -86,110 +68,28 @@ destructor TWireData.Destroy;
procedure TWireData.DefineProperties;
begin
NumProperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
+ CountPropertiesAndAllocate();
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-end;
-
-function TWireData.NewObject(const ObjName: String): Integer;
-begin
- // create a new object of this class and add to list
- DSS.ActiveDSSObject := TWireDataObj.Create(Self, ObjName);
- Result := AddObjectToList(DSS.ActiveDSSObject);
-end;
-
-function TWireData.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveConductorDataObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActiveConductorDataObj;
- with DSS.ActiveConductorDataObj do
- begin
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 101);
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveConductorDataObj, ParamPointer - NumPropsThisClass)
- end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
- end;
+ inherited DefineProperties;
end;
-function TWireData.MakeLike(const WireName: String): Integer;
+function TWireData.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- OtherWireData: TWireDataObj;
- i: Integer;
+ Obj: TObj;
begin
- Result := 0;
- OtherWireData := Find(WireName);
- if OtherWireData <> NIL then
- with DSS.ActiveConductorDataObj do
- begin
- ClassMakeLike(OtherWireData);
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherWireData.PropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Wire MakeLike: "' + WireName + '" Not Found.', 102);
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-function TWireData.Get_Code: String; // Returns active line code string
-begin
- Result := TWireDataObj(ElementList.Active).Name;
-end;
-
-procedure TWireData.Set_Code(const Value: String); // sets the active WireData
-var
- WireDataObj: TWireDataObj;
-begin
- DSS.ActiveConductorDataObj := NIL;
- WireDataObj := ElementList.First;
- while WireDataObj <> NIL do
- begin
- if CompareText(WireDataObj.Name, Value) = 0 then
- begin
- DSS.ActiveConductorDataObj := WireDataObj;
- Exit;
- end;
- WireDataObj := ElementList.Next;
- end;
- DoSimpleMsg('WireData: "' + Value + '" not Found.', 103);
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TWireData Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TWireDataObj.Create(ParClass: TDSSClass; const WireDataName: String);
begin
inherited Create(ParClass, WireDataName);
- Name := LowerCase(WireDataName);
+ Name := AnsiLowerCase(WireDataName);
DSSObjType := ParClass.DSSClassType;
- InitPropertyValues(0);
end;
destructor TWireDataObj.Destroy;
@@ -197,14 +97,4 @@ destructor TWireDataObj.Destroy;
inherited destroy;
end;
-procedure TWireDataObj.DumpProperties(F: TFileStream; Complete: Boolean);
-begin
- inherited DumpProperties(F, Complete);
-end;
-
-procedure TWireDataObj.InitPropertyValues(ArrayOffset: Integer);
-begin
- inherited InitPropertyValues(ArrayOffset + NumPropsThisClass);
-end;
-
end.
diff --git a/src/General/XYcurve.pas b/src/General/XYcurve.pas
index 1dee90f78..f9babe0c2 100644
--- a/src/General/XYcurve.pas
+++ b/src/General/XYcurve.pas
@@ -7,37 +7,25 @@
----------------------------------------------------------
}
-{ 2-15-2011 Converted from TempShape.
-
- General X-Y Curve Data Support Class
-
-}
-
interface
-{
-
- The XYcurve object is a general DSS object used by all circuit elements
- as a reference for obtaining yearly, daily, and other Temperature shapes.
-
- The values are set by the normal New and Edit PROCEDUREs for any DSS object.
-
- The values may be retrieved by setting the Code Property in the XYCurve Class.
- This sets the active XYCurve object to be the one referenced by the Code Property;
-
- Then the values of that code can be retrieved via the public variables. Or you
- can pick up the ActiveTXYcurveObj object and save the direct reference to the object.
-
- The user may place the curve data in CSV or binary files as well as passing through the
- command interface. Obviously, for large amounts of data such as 8760 load curves, the
- command interface is cumbersome. CSV files are text separated by commas, or white space
- one point to a line.
-
- There are two binary formats permitted: 1) a file of Singles; 2) a file of Doubles.
-
-
-
- }
+// The XYcurve object is a general DSS object used by all circuit elements
+// as a reference for obtaining yearly, daily, and other Temperature shapes.
+//
+// The values are set by the normal New and Edit PROCEDUREs for any DSS object.
+//
+// The values may be retrieved by setting the Code Property in the XYCurve Class.
+// This sets the active XYCurve object to be the one referenced by the Code Property;
+//
+// Then the values of that code can be retrieved via the public variables. Or you
+// can pick up the ActiveTXYcurveObj object and save the direct reference to the object.
+//
+// The user may place the curve data in CSV or binary files as well as passing through the
+// command interface. Obviously, for large amounts of data such as 8760 load curves, the
+// command interface is cumbersome. CSV files are text separated by commas, or white space
+// one point to a line.
+//
+// There are two binary formats permitted: 1) a file of Singles; 2) a file of Doubles.
uses
Classes,
@@ -47,33 +35,36 @@ interface
Arraydef;
type
+{$SCOPEDENUMS ON}
+ TXYcurveProp = (
+ INVALID = 0,
+ npts = 1, // Number of points to expect
+ Points = 2,
+ Yarray = 3, // vector of Y values
+ Xarray = 4, // vector of X values corresponding to Y values
+ csvfile = 5, // Switch input to a csvfile
+ sngfile = 6, // switch input to a binary file of singles
+ dblfile = 7, // switch input to a binary file of singles
+ x = 8,
+ y = 9,
+ Xshift = 10,
+ Yshift = 11,
+ Xscale = 12,
+ Yscale = 13
+ );
+{$SCOPEDENUMS OFF}
+
TCoeff = array[1..2] of Double;
TXYcurve = class(TDSSClass)
- PRIVATE
- TempPointsBuffer: pDoubleArray;
- function Get_Code: String; // Returns active TShape string
- procedure Set_Code(const Value: String); // sets the active TShape
-
- procedure DoCSVFile(const FileName: String);
- procedure DoSngFile(const FileName: String);
- procedure DoDblFile(const FileName: String);
PROTECTED
- procedure DefineProperties;
- function MakeLike(const CurveName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
function Find(const ObjName: String; const ChangeActive: Boolean=True): Pointer; OVERRIDE; // Find an obj of this class by name
-
-
- // Set this property to point ActiveTShapeObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
-
end;
TXYcurveObj = class(TDSSObject)
@@ -81,16 +72,11 @@ TXYcurveObj = class(TDSSObject)
XValues,
YValues: pDoubleArray;
PRIVATE
- LastValueAccessed,
- FNumPoints: Integer; // Number of points in curve
- ArrayPropertyIndex: Integer;
+ LastValueAccessed: Integer;
FX,
FY: Double;
- procedure Set_NumPoints(const Value: Integer);
function InterpolatePoints(i, j: Integer; X: Double; Xarray, Yarray: pDoubleArray): Double;
- // PROCEDURE SaveToDblFile;
- // PROCEDURE SaveToSngFile;
function Get_YValue(i: Integer): Double; // get Y Value by index
function Get_XValue(i: Integer): Double; // get X Value corresponding to point index
procedure Set_XValue(Index: Integer; Value: Double);
@@ -102,498 +88,286 @@ TXYcurveObj = class(TDSSObject)
procedure Set_Y(Value: Double);
PUBLIC
+ FNumPoints: Integer; // Number of points in curve
- // Make these vars available to COM interface
FXshift,
FYshift,
FXscale,
FYscale: Double;
+
+ csvfile, dblfile, sngfile: String;
constructor Create(ParClass: TDSSClass; const XYCurveName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
function GetYValue(X: Double): Double; // Get Y value at specified X Value
function GetXValue(Y: Double): Double; // Get X value at specified Y Value
function GetCoefficients(X: Double): TCoeff;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- procedure SaveWrite(F: TFileStream); OVERRIDE;
-
- property NumPoints: Integer READ FNumPoints WRITE Set_NumPoints;
+ property NumPoints: Integer READ FNumPoints;
property XValue_pt[Index: Integer]: Double READ Get_XValue WRITE Set_XValue;
property YValue_pt[Index: Integer]: Double READ Get_YValue WRITE Set_YValue;
property X: Double READ Get_X WRITE Set_X;
property Y: Double READ Get_Y WRITE Set_Y;
-
end;
-
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Sysutils,
MathUtil,
Utilities,
- BufStream,
Math,
DSSPointerList,
DSSHelper,
DSSObjectHelper,
- TypInfo;
+ TypInfo,
+ CAPI_Types,
+ CAPI_Utils;
+type
+ TObj = TXYcurveObj;
+ TProp = TXYcurveProp;
const
- NumPropsThisClass = 13;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TXYcurve.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TXYcurve.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'XYcurve';
- DSSClassType := DSS_OBJECT;
-
- ActiveElement := 0;
- TempPointsBuffer := NIL; // Has to start off Nil for Reallocmem call
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, DSS_OBJECT, 'XYcurve');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TXYcurve.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TXYcurve.DefineProperties;
-begin
-
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
- PropertyName[1] := 'npts'; // Number of points to expect
- PropertyName[2] := 'Points';
- PropertyName[3] := 'Yarray'; // vector of Y values
- PropertyName[4] := 'Xarray'; // vector of X values corresponding to Y values
- PropertyName[5] := 'csvfile'; // Switch input to a csvfile
- PropertyName[6] := 'sngfile'; // switch input to a binary file of singles
- PropertyName[7] := 'dblfile'; // switch input to a binary file of singles
- PropertyName[8] := 'x';
- PropertyName[9] := 'y';
- PropertyName[10] := 'Xshift';
- PropertyName[11] := 'Yshift';
- PropertyName[12] := 'Xscale';
- PropertyName[13] := 'Yscale';
-
- // define Property help values
-
- PropertyHelp[1] := 'Max number of points to expect in curve. This could get reset to the actual number of points defined ' +
- 'if less than specified.'; // Number of points to expect
- PropertyHelp[2] := 'One way to enter the points in a curve. Enter x and y values as one array ' +
- 'in the order [x1, y1, x2, y2, ...]. For example:' + CRLF + CRLF +
- 'Points=[1,100 2,200 3, 300] ' + CRLF + CRLF +
- 'Values separated by commas or white space. Zero fills arrays if insufficient number of values.';
- PropertyHelp[3] := 'Alternate way to enter Y values. Enter an array of Y values corresponding to the X values. ' +
- 'You can also use the syntax: ' + CRLF +
- 'Yarray = (file=filename) !for text file one value per line' + CRLF +
- 'Yarray = (dblfile=filename) !for packed file of doubles' + CRLF +
- 'Yarray = (sngfile=filename) !for packed file of singles ' + CRLF + CRLF +
- 'Note: this property will reset Npts to a smaller value if the number of values in the files are fewer.'; // vextor of hour values
- PropertyHelp[4] := 'Alternate way to enter X values. Enter an array of X values corresponding to the Y values. ' +
- 'You can also use the syntax: ' + CRLF +
- 'Xarray = (file=filename) !for text file one value per line' + CRLF +
- 'Xarray = (dblfile=filename) !for packed file of doubles' + CRLF +
- 'Xarray = (sngfile=filename) !for packed file of singles ' + CRLF + CRLF +
- 'Note: this property will reset Npts to a smaller value if the number of values in the files are fewer.'; // vextor of hour values
- PropertyHelp[5] := 'Switch input of X-Y curve data to a CSV file ' +
- 'containing X, Y points one per line. ' +
- 'NOTE: This action may reset the number of points to a lower value.'; // Switch input to a csvfile
- PropertyHelp[6] := 'Switch input of X-Y curve data to a binary file of SINGLES ' +
- 'containing X, Y points packed one after another. ' +
- 'NOTE: This action may reset the number of points to a lower value.'; // switch input to a binary file of singles
- PropertyHelp[7] := 'Switch input of X-Y curve data to a binary file of DOUBLES ' +
- 'containing X, Y points packed one after another. ' +
- 'NOTE: This action may reset the number of points to a lower value.'; // switch input to a binary file of singles
- PropertyHelp[8] := 'Enter a value and then retrieve the interpolated Y value from the Y property. On input shifted then scaled to original curve. Scaled then shifted on output.';
- PropertyHelp[9] := 'Enter a value and then retrieve the interpolated X value from the X property. On input shifted then scaled to original curve. Scaled then shifted on output.';
- PropertyHelp[10] := 'Shift X property values (in/out) by this amount of offset. Default = 0. Does not change original definition of arrays.';
- PropertyHelp[11] := 'Shift Y property values (in/out) by this amount of offset. Default = 0. Does not change original definition of arrays.';
- PropertyHelp[12] := 'Scale X property values (in/out) by this factor. Default = 1.0. Does not change original definition of arrays.';
- PropertyHelp[13] := 'Scale Y property values (in/out) by this factor. Default = 1.0. Does not change original definition of arrays.';
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TXYcurve.NewObject(const ObjName: String): Integer;
+procedure SetX(Obj: TObj; Value: Double);
begin
- // create a new object of this class and add to list
- DSS.ActiveDSSObject := TXYcurveObj.Create(Self, ObjName);
- Result := AddObjectToList(DSS.ActiveDSSObject);
+ Obj.X := Value;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TXYcurve.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
- i: Integer;
-
+procedure SetY(Obj: TObj; Value: Double);
begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveXYcurveObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActiveXYcurveObj;
-
- with DSS.ActiveXYcurveObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
-
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 610);
- 1:
- NumPoints := Parser.Intvalue;
- 2:
- begin
- ReAllocmem(TempPointsBuffer, Sizeof(TempPointsBuffer^[1]) * FNumPoints * 2);
- // Allow possible Resetting (to a lower value) of num points when specifying temperatures not Hours
- NumPoints := InterpretDblArray(Param, (FNumPoints * 2), TempPointsBuffer) div 2; // Parser.ParseAsVector(Npts, Temperatures);
- ReAllocmem(YValues, Sizeof(YValues^[1]) * FNumPoints);
- ReAllocmem(XValues, Sizeof(XValues^[1]) * FNumPoints);
- for i := 1 to FNumPoints do
- begin
- XValues^[i] := TempPointsBuffer^[2 * i - 1];
- YValues^[i] := TempPointsBuffer^[2 * i];
- end;
- X := Xvalues^[1];
- Y := Yvalues^[1];
- ReAllocmem(TempPointsBuffer, 0); // Throw away temp array
- end;
- 3:
- begin
- ReAllocmem(YValues, Sizeof(YValues^[1]) * NumPoints);
- NumPoints := InterpretDblArray(Param, NumPoints, YValues);
- Y := Yvalues^[1];
- end;
- 4:
- begin
- ReAllocmem(XValues, Sizeof(XValues^[1]) * NumPoints);
- NumPoints := InterpretDblArray(Param, NumPoints, XValues);
- X := Xvalues^[1];
- end;
- 5:
- DoCSVFile(AdjustInputFilePath(Param)); // file of x,y points, one to a line
- 6:
- DoSngFile(AdjustInputFilePath(Param));
- 7:
- DoDblFile(AdjustInputFilePath(Param));
- 8:
- X := Parser.dblvalue;
- 9:
- Y := Parser.dblvalue;
- 10:
- FXshift := Parser.dblvalue;
- 11:
- FYshift := Parser.dblvalue;
- 12:
- FXscale := Parser.dblvalue;
- 13:
- FYscale := Parser.dblvalue;
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveXYcurveObj, ParamPointer - NumPropsThisClass)
- end;
-
- case ParamPointer of
- 5..7:
- begin
- X := Xvalues^[1];
- Y := Yvalues^[1];
- end;
- end;
-
- case ParamPointer of
- 2..7:
- begin
- ArrayPropertyIndex := ParamPointer;
- NumPoints := FNumPoints; // Keep Properties in order for save command
- LastValueAccessed := 1;
- end;
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end; {While}
-
- end; {WITH}
+ Obj.Y := Value;
end;
-function TXYcurve.Find(const ObjName: String; const ChangeActive: Boolean): Pointer;
+function GetX(Obj: TObj): Double;
begin
- if (Length(ObjName) = 0) or (CompareText(ObjName, 'none') = 0) then
- Result := NIL
- else
- Result := inherited Find(ObjName, ChangeActive);
+ Result := Obj.X;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TXYcurve.MakeLike(const CurveName: String): Integer;
-var
- OtherXYCurve: TXYcurveObj;
- i: Integer;
+function GetY(Obj: TObj): Double;
begin
- Result := 0;
- {See if we can find this curve in the present collection}
- OtherXYCurve := Find(CurveName);
- if OtherXYCurve <> NIL then
- with DSS.ActiveXYcurveObj do
- begin
- NumPoints := OtherXYCurve.NumPoints;
- ReAllocmem(XValues, Sizeof(XValues^[1]) * NumPoints);
- ReAllocmem(YValues, Sizeof(YValues^[1]) * NumPoints);
- for i := 1 to NumPoints do
- XValues^[i] := OtherXYCurve.XValues^[i];
- for i := 1 to NumPoints do
- YValues^[i] := OtherXYCurve.YValues^[i];
-
- FXshift := OtherXYCurve.FXshift;
- FYshift := OtherXYCurve.FYshift;
- FXscale := OtherXYCurve.FXscale;
- FYscale := OtherXYCurve.FYscale;
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherXYCurve.PropertyValue[i];
- end
- else
- DoSimpleMsg('Error in XYCurve MakeLike: "' + CurveName + '" Not Found.', 611);
-
-
+ Result := Obj.Y;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TXYcurve.Get_Code: String; // Returns active line code string
-var
- XYCurveObj: TXYcurveObj;
-
+function Get2xNumPoints(Obj: TObj): Integer;
begin
-
- XYCurveObj := ElementList.Active;
- Result := XYCurveObj.Name;
-
+ Result := Obj.FNumPoints * 2;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TXYcurve.Set_Code(const Value: String); // sets the active TShape
-
+procedure SetPoints(obj: TObj; Values: PDouble; ValueCount: Integer);
var
- XYCurveObj: TXYcurveObj;
-
+ i: Integer;
begin
-
- DSS.ActiveXYcurveObj := NIL;
- XYCurveObj := ElementList.First;
- while XYCurveObj <> NIL do
+ with obj do
begin
-
- if CompareText(XYCurveObj.Name, Value) = 0 then
+ // Allow possible Resetting (to a lower value) of num points when specifying temperatures not Hours
+ FNumPoints := ValueCount div 2;
+ ReAllocmem(YValues, Sizeof(Double) * FNumPoints);
+ ReAllocmem(XValues, Sizeof(Double) * FNumPoints);
+ for i := 1 to FNumPoints do
begin
- DSS.ActiveXYcurveObj := XYCurveObj;
- Exit;
+ XValues[i] := Values^;
+ Inc(Values);
+ YValues[i] := Values^;
+ Inc(Values);
end;
-
- XYCurveObj := ElementList.Next;
+ X := Xvalues[1];
+ Y := Yvalues[1];
end;
-
- DoSimpleMsg('XYCurve: "' + Value + '" not Found.', 612);
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TXYcurve.DoCSVFile(const FileName: String);
-
+procedure GetPoints(obj: TObj; var ResultPtr: PDouble; ResultCount: PAPISize);
var
- F: TBufferedFileStream = nil;
i: Integer;
- s: String;
-
+ Result: PDoubleArray0;
begin
- try
- F := TBufferedFileStream.Create(FileName, fmOpenRead);
- except
- DoSimpleMsg('Error Opening File: "' + FileName, 613);
- FreeAndNil(F);
- Exit;
- end;
-
- try
-
- with DSS.ActiveXYcurveObj do
+ with Obj do
+ if (XValues <> NIL) and (YValues <> NIL) then
begin
- ReAllocmem(XValues, Sizeof(XValues^[1]) * NumPoints);
- ReAllocmem(YValues, Sizeof(YValues^[1]) * NumPoints);
- i := 0;
- while ((F.Position + 1) < F.Size) and (i < FNumPoints) do
+ Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, FNumPoints * 2);
+ for i := 1 to FNumPoints do
begin
- Inc(i);
- FSReadln(F, s); // read entire line and parse with AuxParser
- {AuxParser allows commas or white space}
- with AuxParser do
- begin
- CmdString := s;
- NextParam;
- XValues^[i] := DblValue;
- NextParam;
- YValues^[i] := DblValue;
- end;
+ Result[2 * (i - 1)] := XValues^[i];
+ Result[2 * (i - 1) + 1] := YValues^[i];
end;
- FreeAndNil(F);
- if i <> FNumPoints then
- NumPoints := i;
- end;
-
- except
- On E: Exception do
- begin
- DoSimpleMsg('Error Processing XYCurve CSV File: "' + FileName + '. ' + E.Message, 614);
- FreeAndNil(F);
Exit;
end;
- end;
+ Result := DSS_RecreateArray_PDouble(ResultPtr, ResultCount, 2);
+ Result[0] := 0;
+ Result[1] := 0;
+end;
+procedure TXYcurve.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
+begin
+ Numproperties := NumPropsThisClass;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // integer properties
+ PropertyType[ord(TProp.Npts)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Npts)] := ptruint(@obj.FNumPoints);
+
+ // double arrays
+ PropertyType[ord(TProp.Xarray)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.Xarray)] := ptruint(@obj.XValues);
+ PropertyOffset2[ord(TProp.Xarray)] := ptruint(@obj.FNumPoints);
+
+ PropertyType[ord(TProp.Yarray)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.Yarray)] := ptruint(@obj.YValues);
+ PropertyOffset2[ord(TProp.Yarray)] := ptruint(@obj.FNumPoints);
+
+ // strings
+ PropertyType[ord(TProp.csvfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.csvfile)] := ptruint(@obj.csvfile);
+ PropertyFlags[ord(TProp.csvfile)] := [TPropertyFlag.IsFilename];
+
+ PropertyType[ord(TProp.dblfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.dblfile)] := ptruint(@obj.dblfile);
+ PropertyFlags[ord(TProp.dblfile)] := [TPropertyFlag.IsFilename];
+
+ PropertyType[ord(TProp.sngfile)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.sngfile)] := ptruint(@obj.sngfile);
+ PropertyFlags[ord(TProp.sngfile)] := [TPropertyFlag.IsFilename];
+
+ // doubles
+ PropertyOffset[ord(TProp.Xshift)] := ptruint(@obj.FXshift);
+ PropertyOffset[ord(TProp.Yshift)] := ptruint(@obj.FYshift);
+ PropertyOffset[ord(TProp.Xscale)] := ptruint(@obj.FXscale);
+ PropertyOffset[ord(TProp.Yscale)] := ptruint(@obj.FYscale);
+
+ // doubles with setter and getters
+ PropertyType[ord(TProp.X)] := TPropertyType.DoubleProperty;
+ PropertyOffset[ord(TProp.X)] := 1; // dummy
+ PropertyWriteFunction[ord(TProp.X)] := @SetX;
+ PropertyReadFunction[ord(TProp.X)] := @GetX;
+ PropertyFlags[ord(TProp.X)] := [TPropertyFlag.WriteByFunction, TPropertyFlag.ReadByFunction, TPropertyFlag.Util];
+
+ PropertyType[ord(TProp.Y)] := TPropertyType.DoubleProperty;
+ PropertyOffset[ord(TProp.Y)] := 1; // dummy
+ PropertyWriteFunction[ord(TProp.Y)] := @SetY;
+ PropertyReadFunction[ord(TProp.Y)] := @GetY;
+ PropertyFlags[ord(TProp.Y)] := [TPropertyFlag.WriteByFunction, TPropertyFlag.ReadByFunction, TPropertyFlag.Util];
+
+ // ...and just mark Points as custom
+ PropertyType[ord(TProp.Points)] := TPropertyType.DoubleDArrayProperty;
+ PropertyOffset[ord(TProp.Points)] := 1; // dummy
+ PropertyWriteFunction[ord(TProp.Points)] := @SetPoints;
+ PropertyReadFunction[ord(TProp.Points)] := @GetPoints;
+ PropertyOffset3[ord(TProp.Points)] := ptruint(@Get2xNumPoints);
+ PropertyFlags[ord(TProp.Points)] := [TPropertyFlag.WriteByFunction, TPropertyFlag.ReadByFunction, TPropertyFlag.SizeIsFunction, TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.Points)] := ord(TProp.Xarray);
+
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TXYcurve.DoSngFile(const FileName: String);
+function TXYcurve.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- F: TFileStream = nil;
- sX,
- sY: Single;
- i: Integer;
+ Obj: TObj;
+begin
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
+end;
+procedure TXYcurveObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- try
- F := TFileStream.Create(FileName, fmOpenRead);
- except
- DoSimpleMsg('Error Opening File: "' + FileName, 615);
- FreeAndNil(F);
- Exit;
+ case Idx of
+ ord(TProp.csvfile):
+ DoCSVFile(DSS, Xvalues, Yvalues, FNumPoints, False, csvfile, ParentClass.Name); // file of x,y points, one to a line
+ ord(TProp.sngfile):
+ DoSngFile(DSS, Xvalues, Yvalues, FNumPoints, False, sngfile, ParentClass.Name);
+ ord(TProp.dblfile):
+ DoDblFile(DSS, Xvalues, Yvalues, FNumPoints, False, dblfile, ParentClass.Name);
end;
- try
- with DSS.ActiveXYcurveObj do
+ case Idx of
+ ord(TProp.npts):
begin
- ReAllocmem(XValues, Sizeof(XValues^[1]) * NumPoints);
- ReAllocmem(YValues, Sizeof(YValues^[1]) * NumPoints);
- i := 0;
- while ((F.Position + 1) < F.Size) and (i < FNumPoints) do
- begin
- Inc(i);
-
- if F.Read(sX, SizeOf(sX)) <> SizeOf(sX) then
- Break;
-
- XValues^[i] := sX;
-
- if F.Read(sY, SizeOf(sY)) <> SizeOf(sY) then
- Break;
+ // Force as the always first property when saving in a later point
+ PrpSequence[Idx] := -10;
+ ReAllocmem(YValues, Sizeof(YValues^[1]) * FNumPoints);
+ ReAllocmem(XValues, Sizeof(XValues^[1]) * FNumPoints);
+ end;
+ ord(TProp.Yarray):
+ Y := Yvalues^[1];
+ ord(TProp.Xarray):
+ X := Xvalues^[1];
+ ord(TProp.csvfile), ord(TProp.sngfile), ord(TProp.dblfile):
+ begin
+ X := Xvalues^[1];
+ Y := Yvalues^[1];
+ end;
+ end;
- YValues^[i] := sY;
- end;
- FreeAndNil(F);
- if i <> FNumPoints then
- NumPoints := i;
+ case Idx of
+ 2..7:
+ begin
+ LastValueAccessed := 1;
end;
- except
- DoSimpleMsg('Error Processing binary (single) XYCurve File: "' + FileName, 616);
- FreeAndNil(F);
- Exit;
end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
+function TXYcurve.Find(const ObjName: String; const ChangeActive: Boolean): Pointer;
+begin
+ if (Length(ObjName) = 0) or (CompareText(ObjName, 'none') = 0) then
+ Result := NIL
+ else
+ Result := inherited Find(ObjName, ChangeActive);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TXYcurve.DoDblFile(const FileName: String);
+procedure TXYcurveObj.MakeLike(OtherPtr: Pointer);
var
- F: TFileStream = nil;
+ Other: TObj;
i: Integer;
-
begin
-
- try
- F := TFileStream.Create(FileName, fmOpenRead);
- except
- DoSimpleMsg('Error Opening File: "' + FileName, 617);
- FreeAndNil(F);
- Exit;
- end;
-
- try
- with DSS.ActiveXYcurveObj do
- begin
- ReAllocmem(XValues, Sizeof(XValues^[1]) * NumPoints);
- ReAllocmem(YValues, Sizeof(YValues^[1]) * NumPoints);
- i := 0;
- while ((F.Position + 1) < F.Size) and (i < FNumPoints) do
- begin
- Inc(i);
- if F.Read(XValues^[i], SizeOf(Double)) <> SizeOf(Double) then
- Break;
-
- if F.Read(YValues^[i], SizeOf(Double)) <> SizeOf(Double) then
- Break;
- end;
- FreeAndNil(F);
- if i <> FNumPoints then
- NumPoints := i;
- end;
- except
- DoSimpleMsg('Error Processing binary (double) XYCurve File: "' + FileName, 618);
- FreeAndNil(F);
- Exit;
- end;
-
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNumPoints := Other.NumPoints;
+ ReAllocmem(XValues, Sizeof(XValues^[1]) * NumPoints);
+ ReAllocmem(YValues, Sizeof(YValues^[1]) * NumPoints);
+ for i := 1 to NumPoints do
+ XValues^[i] := Other.XValues^[i];
+ for i := 1 to NumPoints do
+ YValues^[i] := Other.YValues^[i];
+
+ FXshift := Other.FXshift;
+ FYshift := Other.FYshift;
+ FXscale := Other.FXscale;
+ FYscale := Other.FYscale;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TTShape Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TXYcurveObj.Create(ParClass: TDSSClass; const XYCurveName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(XYCurveName);
+ Name := AnsiLowerCase(XYCurveName);
DSSObjType := ParClass.DSSClassType;
LastValueAccessed := 1;
@@ -609,16 +383,13 @@ constructor TXYcurveObj.Create(ParClass: TDSSClass; const XYCurveName: String);
FXscale := 1.0;
FYscale := 1.0;
- ArrayPropertyIndex := 0;
-
- InitPropertyValues(0);
-
+ csvfile := '';
+ dblfile := '';
+ sngfile := '';
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TXYcurveObj.Destroy;
begin
-
if Assigned(XValues) then
ReallocMem(XValues, 0);
if Assigned(YValues) then
@@ -626,136 +397,120 @@ destructor TXYcurveObj.Destroy;
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function TXYcurveObj.GetYValue(X: Double): Double;
-
// This function returns the interpolated Y value for the given X.
// If no points exist in the curve, the result is 0.0
// If Xvalue is outside the range of defined X values,
// the curve is extrapolated from the Ends.
-
var
i: Integer;
-
begin
-
Result := 0.0; // default return value if no points in curve
- if FNumPoints > 0 then // Handle Exceptional cases
- if FNumPoints = 1 then
- Result := YValues^[1]
- else
- begin
-
- { Start with previous value accessed under the assumption that most
- of the time, the values won't change much}
+ if FNumPoints <= 0 then // Handle Exceptional cases
+ Exit;
- if (XValues^[LastValueAccessed] > X) then
- LastValueAccessed := 1; // Start over from Beginning
+ if FNumPoints = 1 then
+ begin
+ Result := YValues^[1];
+ Exit;
+ end;
- // if off the curve for the first point, extrapolate from the first two points
- if (LastValueAccessed = 1) and (XValues[1] > X) then
- begin
- Result := InterpolatePoints(1, 2, X, XValues, YValues);
- Exit;
- end;
+ // Start with previous value accessed under the assumption that most
+ // of the time, the values won't change much
+ if (XValues^[LastValueAccessed] > X) then
+ LastValueAccessed := 1; // Start over from Beginning
- // In the middle of the arrays
- for i := LastValueAccessed + 1 to FNumPoints do
- begin
- if (Abs(XValues^[i] - X) < 0.00001) then // If close to an actual point, just use it.
- begin
- Result := YValues^[i];
- LastValueAccessed := i;
- Exit;
- end
- else
- if (XValues^[i] > X) then
- // INTERPOLATE between two values
- begin
- LastValueAccessed := i - 1;
- Result := InterpolatePoints(i, LastValueAccessed, X, XValues, YValues);
- Exit;
- end;
- end;
+ // if off the curve for the first point, extrapolate from the first two points
+ if (LastValueAccessed = 1) and (XValues[1] > X) then
+ begin
+ Result := InterpolatePoints(1, 2, X, XValues, YValues);
+ Exit;
+ end;
- // If we fall through the loop, Extrapolate from last two points
- LastValueAccessed := FNumPoints - 1;
- Result := InterpolatePoints(FNumPoints, LastValueAccessed, X, XValues, YValues);
+ // In the middle of the arrays
+ for i := LastValueAccessed + 1 to FNumPoints do
+ begin
+ if (Abs(XValues^[i] - X) < 0.00001) then // If close to an actual point, just use it.
+ begin
+ Result := YValues^[i];
+ LastValueAccessed := i;
+ Exit;
+ end
+ else
+ if (XValues^[i] > X) then
+ // INTERPOLATE between two values
+ begin
+ LastValueAccessed := i - 1;
+ Result := InterpolatePoints(i, LastValueAccessed, X, XValues, YValues);
+ Exit;
end;
+ end;
+
+ // If we fall through the loop, Extrapolate from last two points
+ LastValueAccessed := FNumPoints - 1;
+ Result := InterpolatePoints(FNumPoints, LastValueAccessed, X, XValues, YValues);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function TXYcurveObj.GetCoefficients(X: Double): TCoeff;
-
// This function returns the coefficients of the line interpolated line for the given X (a*X + b).
// If no points exist in the curve (or just a single point), the result is (a = 0, b = 0)
// If Xvalue is outside the range of defined X values,
// the curve is extrapolated from the Ends (a = 0, b = extrapolated value)
-
var
i: Integer;
// coef: pDoubleArray;
coef: TCoeff;
begin
-
- // default return value if no points in curve
+ // default return value if no points in curve
coef[1] := 0.0;
coef[2] := 0.0;
Result := coef;
- if FNumPoints > 0 then // Handle Exceptional cases
- if FNumPoints = 1 then
- Result := coef
- else
- begin
-
- { Start with previous value accessed under the assumption that most
- of the time, the values won't change much}
+ if FNumPoints <= 0 then // Handle Exceptional cases
+ Exit;
- if (XValues^[LastValueAccessed] > X) then
- LastValueAccessed := 1; // Start over from Beginning
+ if FNumPoints = 1 then
+ begin
+ Result := coef;
+ Exit;
+ end;
- // if off the curve for the first point, extrapolate from the first two points
- if (LastValueAccessed = 1) and (XValues[1] > X) then
- begin
+ // Start with previous value accessed under the assumption that most
+ // of the time, the values won't change much
+ if (XValues^[LastValueAccessed] > X) then
+ LastValueAccessed := 1; // Start over from Beginning
+ // if off the curve for the first point, extrapolate from the first two points
+ if (LastValueAccessed = 1) and (XValues[1] > X) then
+ begin
// Assume the same coefficients determined by the first two points. Necessary to keep
// consistency with TXYcurveObj.GetYValue function.
- coef[1] := (YValues^[2] - YValues^[1]) / (XValues^[2] - XValues^[1]);
- coef[2] := YValues^[2] - coef[1] * XValues^[2];
-
- Result := coef;
- Exit;
- end;
-
- // In the middle of the arrays
- for i := LastValueAccessed + 1 to FNumPoints do
- begin
- if (XValues^[i] > X) then
- // INTERPOLATE between two values
- begin
- LastValueAccessed := i - 1;
-
- coef[1] := (YValues^[i] - YValues^[i - 1]) / (XValues^[i] - XValues^[i - 1]);
-
- coef[2] := YValues^[i] - coef[1] * XValues^[i];
-
- Result := coef;
- Exit;
- end;
- end;
+ coef[1] := (YValues^[2] - YValues^[1]) / (XValues^[2] - XValues^[1]);
+ coef[2] := YValues^[2] - coef[1] * XValues^[2];
+ Result := coef;
+ Exit;
+ end;
- // Assume the same coefficients determined by the last two points. Necessary to keep
- // consistency with TXYcurveObj.GetYValue function.
- coef[1] := (YValues^[FNumPoints] - YValues^[FNumPoints - 1]) / (XValues^[FNumPoints] - XValues^[FNumPoints - 1]);
- coef[2] := YValues^[FNumPoints] - coef[1] * XValues^[FNumPoints];
+ // In the middle of the arrays
+ for i := LastValueAccessed + 1 to FNumPoints do
+ if (XValues^[i] > X) then
+ // INTERPOLATE between two values
+ begin
+ LastValueAccessed := i - 1;
+ coef[1] := (YValues^[i] - YValues^[i - 1]) / (XValues^[i] - XValues^[i - 1]);
+ coef[2] := YValues^[i] - coef[1] * XValues^[i];
Result := coef;
-
+ Exit;
end;
-end;
+ // Assume the same coefficients determined by the last two points. Necessary to keep
+ // consistency with TXYcurveObj.GetYValue function.
+ coef[1] := (YValues^[FNumPoints] - YValues^[FNumPoints - 1]) / (XValues^[FNumPoints] - XValues^[FNumPoints - 1]);
+ coef[2] := YValues^[FNumPoints] - coef[1] * XValues^[FNumPoints];
+ Result := coef;
+end;
function TXYcurveObj.Get_Y: Double;
begin
@@ -764,7 +519,6 @@ function TXYcurveObj.Get_Y: Double;
function TXYcurveObj.Get_YValue(i: Integer): Double;
begin
-
if (i <= FNumPoints) and (i > 0) then
begin
Result := YValues^[i];
@@ -772,7 +526,6 @@ function TXYcurveObj.Get_YValue(i: Integer): Double;
end
else
Result := 0.0;
-
end;
function TXYcurveObj.Get_X: Double;
@@ -782,7 +535,6 @@ function TXYcurveObj.Get_X: Double;
function TXYcurveObj.Get_XValue(i: Integer): Double;
begin
-
if (i <= FNumPoints) and (i > 0) then
begin
Result := XValues^[i];
@@ -790,83 +542,6 @@ function TXYcurveObj.Get_XValue(i: Integer): Double;
end
else
Result := 0.0;
-
-end;
-
-
-procedure TXYcurveObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- case i of
- 3, 4:
- FSWriteln(F, '~ ' + PropertyName^[i] + '=(' + PropertyValue[i] + ')');
- else
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
- end;
-
-
-end;
-
-function TXYcurveObj.GetPropertyValue(Index: Integer): String;
-var
- i: Integer;
-begin
- case Index of
- 2..4:
- Result := '[';
- else
- Result := '';
- end;
-
- case Index of
- 2:
- if (XValues <> NIL) and (YValues <> NIL) then
- for i := 1 to FNumPoints do
- Result := Result + Format('%.8g, %.8g ', [XValues^[i], YValues^[i]])
- else
- Result := '0, 0';
- 3:
- if (YValues <> NIL) then
- for i := 1 to FNumPoints do
- Result := Result + Format('%-g, ', [YValues^[i]])
- else
- Result := '0';
- 4:
- if (XValues <> NIL) then
- for i := 1 to FNumPoints do
- Result := Result + Format('%-g, ', [XValues^[i]])
- else
- Result := '0';
- 8:
- Result := Format('%.8g', [Get_X]);
- 9:
- Result := Format('%.8g', [Get_Y]);
- 10:
- Result := Format('%.8g', [FXshift]);
- 11:
- Result := Format('%.8g', [FYshift]);
- 12:
- Result := Format('%.8g', [FXscale]);
- 13:
- Result := Format('%.8g', [FYscale]);
- else
- Result := inherited GetPropertyValue(index);
- end;
-
- case Index of
- 2..4:
- Result := Result + ']';
- else
- end;
end;
function TXYcurveObj.GetXValue(Y: Double): Double;
@@ -876,119 +551,52 @@ function TXYcurveObj.GetXValue(Y: Double): Double;
// the curve is extrapolated from the Ends.
// TEMc: change to relax assumption that Y values are increasing monotonically
// if Y is not monotonic (increasing or decreasing) then X is not unique
-
var
i: Integer;
-
begin
-
Result := 0.0; // default return value if no points in curve
- if FNumPoints > 0 then
- if FNumPoints = 1 then
- Result := XValues^[1]
- else
- begin
- for i := 2 to FNumPoints do
- begin
- if ((Y >= YValues^[i - 1]) and (Y <= YValues^[i])) then
- begin
- Result := InterpolatePoints(i - 1, i, Y, YValues, XValues);
- Exit;
- end;
- if ((Y <= YValues^[i - 1]) and (Y >= YValues^[i])) then
- begin
- Result := InterpolatePoints(i - 1, i, Y, YValues, XValues);
- Exit;
- end;
- end;
- // Y is out of range, need to determine which end to extrapolate from
- if YValues^[1] <= YValues^[FNumPoints] then
- begin // increasing Y values
- if Y <= YValues^[1] then
- begin
- Result := InterpolatePoints(1, 2, Y, YValues, XValues);
- end
- else
- begin
- Result := InterpolatePoints(FNumPoints - 1, FNumPoints, Y, YValues, XValues);
- end
- end
- else
- begin // decreasing Y values
- if Y >= YValues^[1] then
- begin
- Result := InterpolatePoints(1, 2, Y, YValues, XValues);
- end
- else
- begin
- Result := InterpolatePoints(FNumPoints - 1, FNumPoints, Y, YValues, XValues);
- end
- end;
- end;
- {
- IF FNumPoints>0 Then // Handle Exceptional cases
- IF FNumPoints=1 Then Result := XValues^[1]
- ELSE
- Begin
-
- IF (YValues^[LastValueAccessed] > Y) Then LastValueAccessed := 1; // Start over from Beginning
-
- // if off the curve for the first point, extrapolate from the first two points
- IF (LastValueAccessed = 1) AND (YValues[1] > Y) Then Begin
- Result := InterpolatePoints(1, 2, Y, YValues, XValues);
- Exit;
- End;
-
- FOR i := LastValueAccessed+1 TO FNumPoints do
- Begin
- IF (Abs(YValues^[i]-Y) < 0.00001) Then // If close to an actual point, just use it.
- Begin
- Result := XValues^[i];
- LastValueAccessed := i;
- Exit;
- End
- ELSE IF (YValues^[i] > Y) Then
-// INTERPOLATE
- Begin
- LastValueAccessed := i-1;
- Result := InterpolatePoints(i, LastValueAccessed, Y, YValues, XValues);
- Exit ;
- End;
- End;
-
- // If we fall through the loop, Extrapolate from last two points
- LastValueAccessed := FNumPoints-1;
- Result := InterpolatePoints(FNumPoints, LastValueAccessed, Y, YValues, XValues);
- End;
- }
-end;
+ if FNumPoints <= 0 then
+ Exit;
-procedure TXYcurveObj.InitPropertyValues(ArrayOffset: Integer);
-begin
+ if FNumPoints = 1 then
+ begin
+ Result := XValues^[1];
+ Exit;
+ end;
- PropertyValue[1] := '0'; // Number of points to expect
- PropertyValue[2] := '';
- PropertyValue[3] := '';
- PropertyValue[4] := '';
- PropertyValue[5] := '';
- PropertyValue[6] := '';
- PropertyValue[7] := '';
- PropertyValue[8] := '';
- PropertyValue[9] := '';
- PropertyValue[10] := '0';
- PropertyValue[11] := '0';
- PropertyValue[12] := '1';
- PropertyValue[13] := '1';
-
- inherited InitPropertyValues(NumPropsThisClass);
+ for i := 2 to FNumPoints do
+ begin
+ if ((Y >= YValues^[i - 1]) and (Y <= YValues^[i])) then
+ begin
+ Result := InterpolatePoints(i - 1, i, Y, YValues, XValues);
+ Exit;
+ end;
+ if ((Y <= YValues^[i - 1]) and (Y >= YValues^[i])) then
+ begin
+ Result := InterpolatePoints(i - 1, i, Y, YValues, XValues);
+ Exit;
+ end;
+ end;
+ // Y is out of range, need to determine which end to extrapolate from
+ if YValues^[1] <= YValues^[FNumPoints] then
+ begin // increasing Y values
+ if Y <= YValues^[1] then
+ Result := InterpolatePoints(1, 2, Y, YValues, XValues)
+ else
+ Result := InterpolatePoints(FNumPoints - 1, FNumPoints, Y, YValues, XValues);
+ end
+ else
+ begin // decreasing Y values
+ if Y >= YValues^[1] then
+ Result := InterpolatePoints(1, 2, Y, YValues, XValues)
+ else
+ Result := InterpolatePoints(FNumPoints - 1, FNumPoints, Y, YValues, XValues);
+ end;
end;
-
-function TXYcurveObj.InterpolatePoints(i, j: Integer; X: Double; Xarray,
- Yarray: pDoubleArray): Double;
-
+function TXYcurveObj.InterpolatePoints(i, j: Integer; X: Double; Xarray, Yarray: pDoubleArray): Double;
var
Den: Double;
begin
@@ -999,7 +607,6 @@ function TXYcurveObj.InterpolatePoints(i, j: Integer; X: Double; Xarray,
Result := Yarray^[i]; // Y is undefined, return ith value
end;
-
procedure TXYcurveObj.Set_X(Value: Double);
begin
FX := (Value - FXshift) / FXscale;
@@ -1024,50 +631,4 @@ procedure TXYCurveObj.Set_YValue(Index: Integer; Value: Double);
YValues^[Index] := Value;
end;
-procedure TXYcurveObj.SaveWrite(F: TFileStream);
-
-{Override standard SaveWrite}
-{Transformer structure not conducive to standard means of saving}
-var
- iprop: Integer;
-begin
- {Write only properties that were explicitly set in the final order they were actually set}
-
- {Write Npts out first so that arrays get allocated properly}
- FSWrite(F, Format(' Npts=%d', [NumPoints]));
- iProp := GetNextPropertySet(0); // Works on ActiveDSSObject
- while iProp > 0 do
- begin
- with ParentClass do
- {Trap npts= and write out array properties instead}
- case RevPropertyIdxMap[iProp] of
- 1:
-{Ignore Npts};
-
- else
- FSWrite(F, Format(' %s=%s', [PropertyName^[RevPropertyIdxMap[iProp]], CheckForBlanks(PropertyValue[iProp])]));
- end;
- iProp := GetNextPropertySet(iProp);
- end;
-
-end;
-
-procedure TXYcurveObj.Set_NumPoints(const Value: Integer);
-begin
- PropertyValue[1] := IntToStr(Value); // Update property list variable
-
- // Reset array property values to keep them in propoer order in Save
-
- if ArrayPropertyIndex > 0 then
- PropertyValue[ArrayPropertyIndex] := PropertyValue[ArrayPropertyIndex];
-
- FNumPoints := Value; // Now assign the value
-
- // reallocate the curve memory
- ReAllocmem(YValues, Sizeof(YValues^[1]) * FNumPoints);
- ReAllocmem(XValues, Sizeof(XValues^[1]) * FNumPoints);
-
-end;
-
-
-end.
+end.
\ No newline at end of file
diff --git a/src/General/XfmrCode.pas b/src/General/XfmrCode.pas
index fe68038f2..4e37fdb03 100644
--- a/src/General/XfmrCode.pas
+++ b/src/General/XfmrCode.pas
@@ -19,26 +19,64 @@ interface
Transformer;
type
- WdgParmChoice = (Conn, kV, kVA, R, Tap);
+{$SCOPEDENUMS ON}
+ TXfmrCodeProp = (
+ INVALID = 0,
+
+ phases = 1,
+ windings = 2,
+
+ // Winding Definition
+ wdg = 3,
+ conn = 4,
+ kV = 5, // FOR 2-and 3- always kVLL ELSE actual winding KV
+ kVA = 6,
+ tap = 7,
+ pctR = 8,
+ Rneut = 9,
+ Xneut = 10,
+
+ // General Data
+ conns = 11,
+ kVs = 12,
+ kVAs = 13,
+ taps = 14,
+ Xhl = 15,
+ Xht = 16,
+ Xlt = 17,
+ Xscarray = 18, // x12 13 14... 23 24.. 34 ..
+ thermal = 19,
+ n = 20,
+ m = 21,
+ flrise = 22,
+ hsrise = 23,
+ pctloadloss = 24,
+ pctnoloadloss = 25,
+ normhkVA = 26,
+ emerghkVA = 27,
+ MaxTap = 28,
+ MinTap = 29,
+ NumTaps = 30,
+ pctimag = 31,
+ ppm_antifloat = 32,
+ pctRs = 33,
+ X12 = 34,
+ X13 = 35,
+ X23 = 36,
+ RdcOhms = 37,
+ Seasons = 38,
+ Ratings = 39
+ );
+{$SCOPEDENUMS OFF}
TXfmrCode = class(TDSSClass)
- PRIVATE
- function Get_Code: String;
- procedure Set_Code(const Value: String);
- procedure SetActiveWinding(w: Integer);
- procedure InterpretWindings(const S: String; which: WdgParmChoice);
PROTECTED
- procedure DefineProperties;
- function MakeLike(const Name: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
- // Set this property to point ActiveXfmrCodeObj to the right value
- property Code: String READ Get_Code WRITE Set_Code;
-
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TXfmrCodeObj = class(TDSSObject)
@@ -52,9 +90,9 @@ TXfmrCodeObj = class(TDSSObject)
VABase: Double; // FOR impedances
NormMaxHKVA: Double;
EmergMaxHKVA: Double;
- ThermalTimeConst: Double; {hr}
+ ThermalTimeConst: Double; // hr
n_thermal: Double;
- m_thermal: Double; {Exponents}
+ m_thermal: Double; // Exponents
FLrise: Double;
HSrise: Double;
pctLoadLoss: Double;
@@ -63,7 +101,7 @@ TXfmrCodeObj = class(TDSSObject)
pctImag: Double;
Winding: pWindingArray;
- NumkVARatings: Integer;
+ NumkVARatings: Integer; //TODO: remove, redundant as kVARatings already contains it
kVARatings: Array Of Double;
procedure SetNumWindings(N: Integer);
@@ -71,430 +109,299 @@ TXfmrCodeObj = class(TDSSObject)
constructor Create(ParClass: TDSSClass; const XfmrCodeName: String);
destructor Destroy; OVERRIDE;
-
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
end;
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Sysutils,
- Ucomplex,
+ UComplex, DSSUcomplex,
Utilities,
DSSHelper,
DSSObjectHelper,
TypInfo;
+type
+ TObj = TXfmrCodeObj;
+ TProp = TXfmrCodeProp;
const
- NumPropsThisClass = 39;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
constructor TXfmrCode.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'XfmrCode';
- DSSClassType := DSS_OBJECT;
- ActiveElement := 0;
-
- DefineProperties;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, DSS_OBJECT, 'XfmrCode');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TXfmrCode.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TXfmrCode.DefineProperties;
+
+function XscSize(obj: TObj): Integer;
begin
+ with obj do
+ Result := (NumWindings - 1) * NumWindings div 2;
+end;
+procedure TXfmrCode.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
+begin
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- PropertyName[1] := 'phases';
- PropertyName[2] := 'windings';
-
- // Winding Definition
- PropertyName[3] := 'wdg';
- PropertyName[4] := 'conn';
- PropertyName[5] := 'kV'; // FOR 2-and 3- always kVLL ELSE actual winding KV
- PropertyName[6] := 'kVA';
- PropertyName[7] := 'tap';
- PropertyName[8] := '%R';
- PropertyName[9] := 'Rneut';
- PropertyName[10] := 'Xneut';
-
- // General Data
- PropertyName[11] := 'conns';
- PropertyName[12] := 'kVs';
- PropertyName[13] := 'kVAs';
- PropertyName[14] := 'taps';
- PropertyName[15] := 'Xhl';
- PropertyName[16] := 'Xht';
- PropertyName[17] := 'Xlt';
- PropertyName[18] := 'Xscarray'; // x12 13 14... 23 24.. 34 ..
- PropertyName[19] := 'thermal';
- PropertyName[20] := 'n';
- PropertyName[21] := 'm';
- PropertyName[22] := 'flrise';
- PropertyName[23] := 'hsrise';
- PropertyName[24] := '%loadloss';
- PropertyName[25] := '%noloadloss';
- PropertyName[26] := 'normhkVA';
- PropertyName[27] := 'emerghkVA';
- PropertyName[28] := 'MaxTap';
- PropertyName[29] := 'MinTap';
- PropertyName[30] := 'NumTaps';
- PropertyName[31] := '%imag';
- PropertyName[32] := 'ppm_antifloat';
- PropertyName[33] := '%Rs';
- PropertyName[34] := 'X12';
- PropertyName[35] := 'X13';
- PropertyName[36] := 'X23';
- PropertyName[37] := 'RdcOhms';
- PropertyName[38] := 'Seasons';
- PropertyName[39] := 'Ratings';
-
- // define Property help values
- PropertyHelp[1] := 'Number of phases this transformer. Default is 3.';
- PropertyHelp[2] := 'Number of windings, this transformers. (Also is the number of terminals) ' +
- 'Default is 2. This property triggers memory allocation for the Transformer and will cause other properties to revert to default values.';
- // Winding Definition
- PropertyHelp[3] := 'Set this = to the number of the winding you wish to define. Then set ' +
- 'the values for this winding. Repeat for each winding. Alternatively, use ' +
- 'the array collections (buses, kvas, etc.) to define the windings. Note: ' +
- 'reactances are BETWEEN pairs of windings; they are not the property of a single winding.';
- PropertyHelp[4] := 'Connection of this winding. Default is "wye" with the neutral solidly grounded.';
- PropertyHelp[5] := 'For 2-or 3-phase, enter phase-phase kV rating. Otherwise, kV rating of the actual winding';
- PropertyHelp[6] := 'Base kVA rating of the winding. Side effect: forces change of max normal and emerg kva ratings.' +
- 'If 2-winding transformer, forces other winding to same value. ' +
- 'When winding 1 is defined, all other windings are defaulted to the same rating ' +
- 'and the first two winding resistances are defaulted to the %loadloss value.';
- PropertyHelp[7] := 'Per unit tap that this winding is normally on.';
- PropertyHelp[8] := 'Percent resistance this winding. (half of total for a 2-winding).';
- PropertyHelp[9] := 'Default = -1. Neutral resistance of wye (star)-connected winding in actual ohms.' +
- 'If entered as a negative value, the neutral is assumed to be open, or floating.';
- PropertyHelp[10] := 'Neutral reactance of wye(star)-connected winding in actual ohms. May be + or -.';
-
- // General Data
- PropertyHelp[11] := 'Use this to specify all the Winding connections at once using an array. Example:' + CRLF + CRLF +
- 'New Transformer.T1 buses="Hibus, lowbus" ' +
- '~ conns=(delta, wye)';
- PropertyHelp[12] := 'Use this to specify the kV ratings of all windings at once using an array. Example:' + CRLF + CRLF +
- 'New Transformer.T1 buses="Hibus, lowbus" ' + CRLF +
- '~ conns=(delta, wye)' + CRLF +
- '~ kvs=(115, 12.47)' + CRLF + CRLF +
- 'See kV= property for voltage rules.';
- PropertyHelp[13] := 'Use this to specify the kVA ratings of all windings at once using an array.';
- PropertyHelp[14] := 'Use this to specify the normal p.u. tap of all windings at once using an array.';
- PropertyHelp[15] := 'Use this to specify the percent reactance, H-L (winding 1 to winding 2). Use ' +
- 'for 2- or 3-winding transformers. On the kva base of winding 1.';
- PropertyHelp[16] := 'Use this to specify the percent reactance, H-T (winding 1 to winding 3). Use ' +
- 'for 3-winding transformers only. On the kVA base of winding 1.';
- PropertyHelp[17] := 'Use this to specify the percent reactance, L-T (winding 2 to winding 3). Use ' +
- 'for 3-winding transformers only. On the kVA base of winding 1.';
- PropertyHelp[18] := 'Use this to specify the percent reactance between all pairs of windings as an array. ' +
- 'All values are on the kVA base of winding 1. The order of the values is as follows:' + CRLF + CRLF +
- '(x12 13 14... 23 24.. 34 ..) ' + CRLF + CRLF +
- 'There will be n(n-1)/2 values, where n=number of windings.';
- PropertyHelp[19] := 'Thermal time constant of the transformer in hours. Typically about 2.';
- PropertyHelp[20] := 'n Exponent for thermal properties in IEEE C57. Typically 0.8.';
- PropertyHelp[21] := 'm Exponent for thermal properties in IEEE C57. Typically 0.9 - 1.0';
- PropertyHelp[22] := 'Temperature rise, deg C, for full load. Default is 65.';
- PropertyHelp[23] := 'Hot spot temperature rise, deg C. Default is 15.';
- PropertyHelp[24] := 'Percent load loss at full load. The %R of the High and Low windings (1 and 2) are adjusted to agree at rated kVA loading.';
- PropertyHelp[25] := 'Percent no load losses at rated excitatation voltage. Default is 0. Converts to a resistance in parallel with the magnetizing impedance in each winding.';
- PropertyHelp[26] := 'Normal maximum kVA rating of H winding (winding 1). Usually 100% - 110% of' +
- 'maximum nameplate rating, depending on load shape. Defaults to 110% of kVA rating of Winding 1.';
- PropertyHelp[27] := 'Emergency (contingency) kVA rating of H winding (winding 1). Usually 140% - 150% of' +
- 'maximum nameplate rating, depending on load shape. Defaults to 150% of kVA rating of Winding 1.';
- PropertyHelp[28] := 'Max per unit tap for the active winding. Default is 1.10';
- PropertyHelp[29] := 'Min per unit tap for the active winding. Default is 0.90';
- PropertyHelp[30] := 'Total number of taps between min and max tap. Default is 32.';
- PropertyHelp[31] := 'Percent magnetizing current. Default=0.0. Magnetizing branch is in parallel with windings in each phase. Also, see "ppm_antifloat".';
- PropertyHelp[32] := 'Default=1 ppm. Parts per million of transformer winding VA rating connected to ground to protect against accidentally floating a winding without a reference. ' +
- 'If positive then the effect is adding a very large reactance to ground. If negative, then a capacitor.';
- PropertyHelp[33] := 'Use this property to specify all the winding %resistances using an array. Example:' + CRLF + CRLF +
- 'New Transformer.T1 buses="Hibus, lowbus" ' +
- '~ %Rs=(0.2 0.3)';
- PropertyHelp[34] := 'Alternative to XHL for specifying the percent reactance from winding 1 to winding 2. Use ' +
- 'for 2- or 3-winding transformers. Percent on the kVA base of winding 1. ';
- PropertyHelp[35] := 'Alternative to XHT for specifying the percent reactance from winding 1 to winding 3. Use ' +
- 'for 3-winding transformers only. Percent on the kVA base of winding 1. ';
- PropertyHelp[36] := 'Alternative to XLT for specifying the percent reactance from winding 2 to winding 3.Use ' +
- 'for 3-winding transformers only. Percent on the kVA base of winding 1. ';
- PropertyHelp[37] := 'Winding dc resistance in OHMS. Useful for GIC analysis. From transformer test report. ' +
- 'Defaults to 85% of %R property';
- PropertyHelp[38] := 'Defines the number of ratings to be defined for the transfomer, to be used only when defining seasonal ratings using the "Ratings" property.';
- PropertyHelp[39] := 'An array of ratings to be used when the seasonal ratings flag is True. It can be used to insert' +
- CRLF + 'multiple ratings to change during a QSTS simulation to evaluate different ratings in transformers.';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ PropertyStructArrayOffset := ptruint(@obj.Winding);
+ PropertyStructArrayStep := SizeOf(TWinding);
+ PropertyStructArrayIndexOffset := ptruint(@obj.ActiveWinding);
+ PropertyStructArrayCountOffset := ptruint(@obj.NumWindings);
+
+ // double arrays
+ PropertyType[ord(TProp.Ratings)] := TPropertyType.DoubleDArrayProperty;
+ PropertyOffset[ord(TProp.Ratings)] := ptruint(@obj.kVARatings);
+ PropertyOffset2[ord(TProp.Ratings)] := ptruint(@obj.NumkVARatings);
+
+ PropertyType[ord(TProp.Xscarray)] := TPropertyType.DoubleVArrayProperty;
+ PropertyOffset[ord(TProp.Xscarray)] := ptruint(@obj.Xsc);
+ PropertyOffset3[ord(TProp.Xscarray)] := ptruint(@XscSize);
+ PropertyFlags[ord(TProp.Xscarray)] := [TPropertyFlag.SizeIsFunction];
+ PropertyScale[ord(TProp.Xscarray)] := 0.01;
+
+ // integer properties
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.Seasons)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNphases);
+ PropertyOffset[ord(TProp.Seasons)] := ptruint(@obj.NumkVARatings);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ PropertyType[ord(TProp.windings)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.windings)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.windings)] := [TPropertyFlag.GreaterThanOne];
+
+ PropertyType[ord(TProp.wdg)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.wdg)] := ptruint(@obj.ActiveWinding);
+ PropertyFlags[ord(TProp.wdg)] := [TPropertyFlag.IntegerStructIndex];
+
+ // double-on-struct array properties
+ PropertyType[ord(TProp.kV)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.kV)] := ptruint(@TWinding(nil^).kVLL);
+
+ PropertyType[ord(TProp.kVA)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.kVA)] := ptruint(@TWinding(nil^).kVA);
+
+ PropertyType[ord(TProp.tap)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.tap)] := ptruint(@TWinding(nil^).puTap);
+
+ PropertyType[ord(TProp.Rneut)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.Rneut)] := ptruint(@TWinding(nil^).Rneut);
+
+ PropertyType[ord(TProp.Xneut)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.Xneut)] := ptruint(@TWinding(nil^).Xneut);
+
+ PropertyType[ord(TProp.MaxTap)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.MaxTap)] := ptruint(@TWinding(nil^).MaxTap);
+
+ PropertyType[ord(TProp.MinTap)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.MinTap)] := ptruint(@TWinding(nil^).MinTap);
+
+ PropertyType[ord(TProp.RdcOhms)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.RdcOhms)] := ptruint(@TWinding(nil^).RdcOhms);
+
+ PropertyType[ord(TProp.pctR)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.pctR)] := ptruint(@TWinding(nil^).Rpu);
+ PropertyScale[ord(TProp.pctR)] := 0.01;
+
+ // integer on struct array
+ PropertyType[ord(TProp.NumTaps)] := TPropertyType.IntegerOnStructArrayProperty;
+ PropertyOffset[ord(TProp.NumTaps)] := ptruint(@TWinding(nil^).NumTaps);
+
+ // enum on array of structs
+ PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumOnStructArrayProperty;
+ PropertyOffset[ord(TProp.conn)] := ptruint(@TWinding(nil^).Connection);
+ PropertyOffset2[ord(TProp.conn)] := PtrInt(DSS.ConnectionEnum);
+
+ // array of enums on array of structs
+ PropertyType[ord(TProp.conns)] := TPropertyType.MappedStringEnumArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.conns)] := ptruint(@TWinding(nil^).Connection);
+ PropertyOffset2[ord(TProp.conns)] := PtrInt(DSS.ConnectionEnum);
+ PropertyFlags[ord(TProp.conns)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.conns)] := ord(TProp.conn);
+
+ // double percent properties
+ PropertyScale[ord(TProp.XHL)] := 0.01;
+ PropertyScale[ord(TProp.XHT)] := 0.01;
+ PropertyScale[ord(TProp.XLT)] := 0.01;
+ PropertyScale[ord(TProp.X12)] := 0.01;
+ PropertyScale[ord(TProp.X13)] := 0.01;
+ PropertyScale[ord(TProp.X23)] := 0.01;
+ PropertyOffset[ord(TProp.XHL)] := ptruint(@obj.XHL);
+ PropertyOffset[ord(TProp.XHT)] := ptruint(@obj.XHT);
+ PropertyOffset[ord(TProp.XLT)] := ptruint(@obj.XLT);
+ PropertyOffset[ord(TProp.X12)] := ptruint(@obj.XHL);
+ PropertyOffset[ord(TProp.X13)] := ptruint(@obj.XHT);
+ PropertyOffset[ord(TProp.X23)] := ptruint(@obj.XLT);
+ PropertyFlags[ord(TProp.X12)] := [TPropertyFlag.Redundant];
+ PropertyFlags[ord(TProp.X13)] := [TPropertyFlag.Redundant];
+ PropertyFlags[ord(TProp.X23)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.X12)] := ord(TProp.XHL);
+ PropertyRedundantWith[ord(TProp.X13)] := ord(TProp.XHT);
+ PropertyRedundantWith[ord(TProp.X23)] := ord(TProp.XLT);
+
+ // scaled double
+ PropertyOffset[ord(TProp.ppm_antifloat)] := ptruint(@obj.ppm_FloatFactor);
+ PropertyScale[ord(TProp.ppm_antifloat)] := 1.0e-6;
+
+ // double properties
+ PropertyOffset[ord(TProp.thermal)] := ptruint(@obj.ThermalTimeConst);
+ PropertyOffset[ord(TProp.n)] := ptruint(@obj.n_thermal);
+ PropertyOffset[ord(TProp.m)] := ptruint(@obj.m_thermal);
+ PropertyOffset[ord(TProp.flrise)] := ptruint(@obj.FLrise);
+ PropertyOffset[ord(TProp.hsrise)] := ptruint(@obj.HSRise);
+ PropertyFlags[ord(TProp.thermal)] := [TPropertyFlag.Unused];
+ PropertyFlags[ord(TProp.n)] := [TPropertyFlag.Unused];
+ PropertyFlags[ord(TProp.m)] := [TPropertyFlag.Unused];
+ PropertyFlags[ord(TProp.flrise)] := [TPropertyFlag.Unused];
+ PropertyFlags[ord(TProp.hsrise)] := [TPropertyFlag.Unused];
+
+ PropertyOffset[ord(TProp.pctloadloss)] := ptruint(@obj.pctLoadLoss);
+ PropertyOffset[ord(TProp.pctnoloadloss)] := ptruint(@obj.pctNoLoadLoss);
+ PropertyOffset[ord(TProp.normhkVA)] := ptruint(@obj.NormMaxHkVA);
+ PropertyOffset[ord(TProp.emerghkVA)] := ptruint(@obj.EmergMaxHkVA);
+ PropertyOffset[ord(TProp.pctimag)] := ptruint(@obj.pctImag);
+
+ // double arrays via struct array
+ PropertyType[ord(TProp.pctRs)] := TPropertyType.DoubleArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.pctRs)] := ptruint(@TWinding(nil^).Rpu);
+ PropertyOffset2[ord(TProp.pctRs)] := ptruint(@obj.NumWindings);
+ PropertyScale[ord(TProp.pctRs)] := 0.01;
+ PropertyFlags[ord(TProp.pctRs)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.pctRs)] := ord(TProp.pctR);
+
+ PropertyType[ord(TProp.kVs)] := TPropertyType.DoubleArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.kVs)] := ptruint(@TWinding(nil^).kVLL);
+ PropertyOffset2[ord(TProp.kVs)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.kVs)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.kVs)] := ord(TProp.kV);
+
+ PropertyType[ord(TProp.kVAs)] := TPropertyType.DoubleArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.kVAs)] := ptruint(@TWinding(nil^).kVA);
+ PropertyOffset2[ord(TProp.kVAs)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.kVAs)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.kVAs)] := ord(TProp.kVA);
+
+ PropertyType[ord(TProp.taps)] := TPropertyType.DoubleArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.taps)] := ptruint(@TWinding(nil^).puTap);
+ PropertyOffset2[ord(TProp.taps)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.taps)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.taps)] := ord(TProp.tap);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
-end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TXfmrCode.NewObject(const ObjName: String): Integer;
-begin
- // create a new object of this class and add to list
- DSS.ActiveDSSObject := TXfmrCodeObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
+ inherited DefineProperties;
end;
-procedure TXfmrCodeObj.SetNumWindings(N: Integer);
+function TXfmrCode.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- i: Integer;
- OldWdgSize: Integer;
- NewWdgSize: Integer;
+ Obj: TObj;
begin
- if N > 1 then
- begin
- for i := 1 to NumWindings do
- Winding^[i].Free; // Free old winding objects
- OldWdgSize := (NumWindings - 1) * NumWindings div 2;
- NumWindings := N;
- MaxWindings := N;
- NewWdgSize := (NumWindings - 1) * NumWindings div 2;
- Reallocmem(Winding, Sizeof(Winding^[1]) * MaxWindings); // Reallocate collector array
- for i := 1 to MaxWindings do
- Winding^[i] := TWinding.Create;
- ReAllocmem(XSC, SizeOF(XSC^[1]) * NewWdgSize);
- for i := OldWdgSize + 1 to NewWdgSize do
- begin
- XSC^[i] := 0.30; // default to something
- end
- end
- else
- DoSimpleMsg('Invalid number of windings: (' + IntToStr(N) + ') for Transformer ' +
- DSS.ActiveTransfObj.Name, 111);
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ DSS.ActiveDSSObject := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-procedure TXfmrCode.SetActiveWinding(w: Integer);
+procedure TXfmrCodeObj.SetNumWindings(N: Integer);
+var
+ prev: Integer;
begin
- with DSS.ActiveXfmrCodeObj do
- if (w > 0) and (w <= NumWindings) then
- ActiveWinding := w
- else
- DoSimpleMsg('Wdg parameter invalid for "' + DSS.ActiveXfmrCodeObj.Name + '"', 112);
+ prev := NumWindings;
+ NumWindings := N;
+ PropertySideEffects(ord(TProp.windings), prev);
end;
-procedure TXfmrCode.InterpretWindings(const S: String; which: WdgParmChoice);
+procedure TXfmrCodeObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- Str: String;
i: Integer;
+ OldXSCSize, NewXSCSize: Integer;
begin
- AuxParser.CmdString := S;
- with DSS.ActiveXfmrCodeObj do
- begin
- for i := 1 to Numwindings do
+ case Idx of
+ // default all winding kvas to first winding so latter Donot have to be specified
+ ord(TProp.windings):
+ begin
+ // Reallocate stuff if bigger
+ OldXSCSize := (previousIntVal - 1) * previousIntVal div 2;
+ MaxWindings := NumWindings;
+ NewXSCSize := (NumWindings - 1) * NumWindings div 2;
+ Reallocmem(Winding, Sizeof(TWinding) * MaxWindings); // Reallocate collector array
+ for i := 1 to MaxWindings do
+ Winding[i].Init();
+ ReAllocmem(XSC, SizeOF(XSC^[1]) * NewXSCSize);
+ for i := OldXSCSize + 1 to NewXSCSize do
+ begin
+ XSC^[i] := 0.30; // default to something
+ end
+ end;
+ ord(TProp.kVA):
+ if (ActiveWinding = 1) then
+ begin
+ for i := 2 to NumWindings do
+ Winding^[i].kVA := Winding^[1].kVA;
+ NormMaxHkVA := 1.1 * Winding^[1].kVA; // Defaults for new winding rating.
+ EmergMaxHkVA := 1.5 * Winding^[1].kVA;
+ end
+ else
+ if NumWindings = 2 then
+ begin
+ Winding^[1].kVA := Winding^[2].kVA; // For 2-winding, force both kVAs to be same
+ end;
+ // Update LoadLosskW if winding %r changed. Using only windings 1 and 2
+ ord(TProp.pctR):
+ pctLoadLoss := (Winding^[1].Rpu + Winding^[2].Rpu) * 100.0;
+ ord(TProp.kVAs):
begin
- ActiveWinding := i;
- AuxParser.NextParam; // ignore any parameter name not expecting any
- Str := AuxParser.StrValue;
- if Length(Str) > 0 then
- case which of
- Conn:
- Winding^[ActiveWinding].Connection := InterpretConnection(Str);
- kV:
- Winding^[ActiveWinding].kvll := AuxParser.Dblvalue;
- kVA:
- Winding^[ActiveWinding].kva := AuxParser.Dblvalue;
- R:
- Winding^[ActiveWinding].Rpu := 0.01 * AuxParser.Dblvalue;
- Tap:
- Winding^[ActiveWinding].puTap := AuxParser.Dblvalue;
- end;
+ NormMaxHkVA := 1.1 * Winding^[1].kVA; // Defaults for new winding rating.
+ EmergMaxHkVA := 1.5 * Winding^[1].kVA;
+ end;
+ 15..17:
+ Include(Flags, Flg.NeedsRecalc);
+ 24:
+ begin // Assume load loss is split evenly between windings 1 and 2
+ Winding^[1].Rpu := pctLoadLoss / 2.0 / 100.0;
+ Winding^[2].Rpu := Winding^[1].Rpu;
end;
+ 33:
+ pctLoadLoss := (Winding^[1].Rpu + Winding^[2].Rpu) * 100.0; // Keep this up to date
+ 34..36:
+ Include(Flags, Flg.NeedsRecalc);
+ 37:
+ Winding^[ActiveWinding].RdcSpecified := TRUE;
+ 38:
+ SetLength(kVARatings, NumkVARatings);
end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TXfmrCode.Edit: Integer;
+function TXfmrCode.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
var
- ParamPointer,
i: Integer;
- ParamName: String; {For parsing property names}
- Param: String;
- UpdateXsc: Boolean;
-
begin
- DSS.ActiveXfmrCodeObj := ElementList.Active;
- DSS.ActiveDSSObject := DSS.ActiveXfmrCodeObj;
- UpdateXsc := FALSE;
-
- with DSS.ActiveXfmrCodeObj do
+ with TObj(ptr) do
begin
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "XfmrCode.' + Name + '"', 110);
- 1:
- FNphases := Parser.IntValue;
- 2:
- SetNumWindings(Parser.IntValue); // Reallocate stuff if bigger
- 3:
- SetActiveWinding(Parser.IntValue);
- 4:
- Winding^[ActiveWinding].Connection := InterpretConnection(Param);
- 5:
- Winding^[ActiveWinding].kvll := parser.Dblvalue;
- 6:
- Winding^[ActiveWinding].kVA := parser.Dblvalue;
- 7:
- Winding^[ActiveWinding].puTap := parser.Dblvalue;
- 8:
- Winding^[ActiveWinding].Rpu := parser.Dblvalue * 0.01; // %R
- 9:
- Winding^[ActiveWinding].Rneut := parser.Dblvalue;
- 10:
- Winding^[ActiveWinding].Xneut := parser.Dblvalue;
- 11:
- InterpretWindings(Param, Conn);
- 12:
- InterpretWindings(Param, kV);
- 13:
- InterpretWindings(Param, kVA);
- 14:
- InterpretWindings(Param, Tap);
- 15:
- XHL := parser.Dblvalue * 0.01;
- 16:
- XHT := parser.Dblvalue * 0.01;
- 17:
- XLT := parser.Dblvalue * 0.01;
- 18:
- Parser.ParseAsVector(((NumWindings - 1) * NumWindings div 2), Xsc);
- 19:
- ThermalTimeConst := Parser.DblValue;
- 20:
- n_thermal := Parser.DblValue;
- 21:
- m_thermal := Parser.DblValue;
- 22:
- FLrise := Parser.DblValue;
- 23:
- HSRise := Parser.DblValue;
- 24:
- pctLoadLoss := Parser.DblValue;
- 25:
- pctNoLoadLoss := Parser.DblValue;
- 26:
- NormMaxHkVA := Parser.Dblvalue;
- 27:
- EmergMaxHkVA := Parser.Dblvalue;
- 28:
- Winding^[ActiveWinding].MaxTap := Parser.DblValue;
- 29:
- Winding^[ActiveWinding].MinTap := Parser.DblValue;
- 30:
- Winding^[ActiveWinding].NumTaps := Parser.IntValue;
- 31:
- pctImag := Parser.DblValue;
- 32:
- ppm_FloatFactor := Parser.DblValue * 1.0e-6;
- 33:
- InterpretWindings(Param, R);
- 34:
- XHL := parser.Dblvalue * 0.01;
- 35:
- XHT := parser.Dblvalue * 0.01;
- 36:
- XLT := parser.Dblvalue * 0.01;
- 37:
- Winding^[ActiveWinding].RdcOhms := Parser.DblValue;
- 38:
- begin
- NumkVARatings := Parser.IntValue;
- SetLength(kVARatings, NumkVARatings);
- end;
- 39:
- begin
- SetLength(kVARatings, NumkVARatings);
- Param := Parser.StrValue;
- NumkVARatings := InterpretDblArray(Param, NumkVARatings, Pointer(kVARatings));
- end
- else
- ClassEdit(DSS.ActiveXfmrCodeObj, ParamPointer - NumPropsThisClass)
- end;
-
- {Take care of properties that require some additional work,}
- case ParamPointer of
- // default all winding kvas to first winding so latter Donot have to be specified
- 6:
- if (ActiveWinding = 1) then
- begin
- for i := 2 to NumWindings do
- Winding^[i].kVA := Winding^[1].kVA;
- NormMaxHkVA := 1.1 * Winding^[1].kVA; // Defaults for new winding rating.
- EmergMaxHkVA := 1.5 * Winding^[1].kVA;
- end
- else
- if NumWindings = 2 then
- begin
- Winding^[1].kVA := Winding^[2].kVA; // For 2-winding, force both kVAs to be same
- end;
- // Update LoadLosskW if winding %r changed. Using only windings 1 and 2
- 8:
- pctLoadLoss := (Winding^[1].Rpu + Winding^[2].Rpu) * 100.0;
- 13:
- begin
- NormMaxHkVA := 1.1 * Winding^[1].kVA; // Defaults for new winding rating.
- EmergMaxHkVA := 1.5 * Winding^[1].kVA;
- end;
- 15..17:
- UpdateXsc := TRUE;
- 18:
- for i := 1 to ((NumWindings - 1) * NumWindings div 2) do
- Xsc^[i] := Xsc^[i] * 0.01; // Convert to per unit
-
- 24:
- begin // Assume load loss is split evenly between windings 1 and 2
- Winding^[1].Rpu := pctLoadLoss / 2.0 / 100.0;
- Winding^[2].Rpu := Winding^[1].Rpu;
- end;
- 33:
- pctLoadLoss := (Winding^[1].Rpu + Winding^[2].Rpu) * 100.0; // Keep this up to date
- 34..36:
- UpdateXsc := TRUE;
- 37:
- Winding^[ActiveWinding].RdcSpecified := TRUE;
-
- else
- end;
-
- {Advance to next property on input line}
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
- if UpdateXsc then
+ if Flg.NeedsRecalc in Flags then
begin
+ Exclude(Flags, Flg.NeedsRecalc);
if NumWindings <= 3 then
- for i := 1 to (NumWindings * (NumWindings - 1) div 2) do
+ for i := 1 to (NumWindings - 1) * NumWindings div 2 do
case i of
1:
XSC^[1] := XHL;
@@ -502,120 +409,61 @@ function TXfmrCode.Edit: Integer;
XSC^[2] := XHT;
3:
XSC^[3] := XLT;
- else
end;
end;
+ Exclude(Flags, Flg.EditionActive);
end;
-
- Result := 0;
+ Result := True;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TXfmrCode.MakeLike(const Name: String): Integer;
+procedure TXfmrCodeObj.MakeLike(OtherPtr: Pointer);
var
- Other: TXfmrCodeObj;
+ Other: TObj;
i: Integer;
begin
- Result := 0;
- {See if we can find this ode in the present collection}
- Other := Find(Name);
- if Other <> NIL then
- with DSS.ActiveXfmrCodeObj do
- begin
- FNphases := Other.FNphases;
- SetNumWindings(Other.NumWindings);
- for i := 1 to NumWindings do
- with Winding^[i] do
- begin
- Connection := Other.Winding^[i].Connection;
- kvll := Other.Winding^[i].kvll;
- Vbase := Other.Winding^[i].Vbase;
- kva := Other.Winding^[i].kva;
- puTAP := Other.Winding^[i].puTAP;
- Rpu := Other.Winding^[i].Rpu;
- RdcOhms := Other.Winding^[i].RdcOhms;
- RdcSpecified := Other.Winding^[i].RdcSpecified;
- RNeut := Other.Winding^[i].RNeut;
- Xneut := Other.Winding^[i].Xneut;
- TapIncrement := Other.Winding^[i].TapIncrement;
- MinTap := Other.Winding^[i].MinTap;
- MaxTap := Other.Winding^[i].MaxTap;
- NumTaps := Other.Winding^[i].NumTaps;
- end;
- XHL := Other.XHL;
- XHT := Other.XHT;
- XLT := Other.XLT;
- for i := 1 to (NumWindings * (NumWindings - 1) div 2) do
- XSc^[i] := Other.XSC^[i];
- ThermalTimeConst := Other.ThermalTimeConst;
- n_thermal := Other.n_thermal;
- m_thermal := Other.m_thermal;
- FLrise := Other.FLrise;
- HSrise := Other.HSrise;
- pctLoadLoss := Other.pctLoadLoss;
- pctNoLoadLoss := Other.pctNoLoadLoss;
- NormMaxHkVA := Other.NormMaxHkVA;
- EmergMaxHkVA := Other.EmergMaxHkVA;
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := Other.PropertyValue[i];
-
- NumkVARatings := Other.NumkVARatings;
- SetLength(kVARatings, NumkVARatings);
- for i := 0 to High(kVARatings) do
- kVARatings[i] := Other.kVARatings[i];
-
- Result := 1;
- end
- else
- DoSimpleMsg('Error in XfmrCode MakeLike: "' + Name + '" Not Found.', 102);
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TXfmrCode.Get_Code: String; // Returns active line code string
-begin
- Result := TXfmrCodeObj(ElementList.Active).Name;
-end;
-
-procedure TXfmrCode.Set_Code(const Value: String); // sets the active XfmrCode
-var
- XfmrCodeObj: TXfmrCodeObj;
-begin
- DSS.ActiveXfmrCodeObj := NIL;
- XfmrCodeObj := ElementList.First;
- while XfmrCodeObj <> NIL do
- begin
- if CompareText(XfmrCodeObj.Name, Value) = 0 then
- begin
- DSS.ActiveXfmrCodeObj := XfmrCodeObj;
- Exit;
- end;
- XfmrCodeObj := ElementList.Next;
- end;
- DoSimpleMsg('XfmrCode: "' + Value + '" not Found.', 103);
+ Other := TObj(OtherPtr);
+ FNphases := Other.FNphases;
+ SetNumWindings(Other.NumWindings);
+ for i := 1 to NumWindings do
+ Winding^[i] := Other.Winding^[i];
+
+ XHL := Other.XHL;
+ XHT := Other.XHT;
+ XLT := Other.XLT;
+ for i := 1 to (NumWindings * (NumWindings - 1) div 2) do
+ XSc^[i] := Other.XSC^[i];
+ ThermalTimeConst := Other.ThermalTimeConst;
+ n_thermal := Other.n_thermal;
+ m_thermal := Other.m_thermal;
+ FLrise := Other.FLrise;
+ HSrise := Other.HSrise;
+ pctLoadLoss := Other.pctLoadLoss;
+ pctNoLoadLoss := Other.pctNoLoadLoss;
+ NormMaxHkVA := Other.NormMaxHkVA;
+ EmergMaxHkVA := Other.EmergMaxHkVA;
+
+ NumkVARatings := Other.NumkVARatings;
+ SetLength(kVARatings, NumkVARatings);
+ for i := 0 to High(kVARatings) do
+ kVARatings[i] := Other.kVARatings[i];
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TXfmrCode Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TXfmrCodeObj.Create(ParClass: TDSSClass; const XfmrCodeName: String);
var
i: Integer;
begin
inherited Create(ParClass);
- Name := LowerCase(XfmrCodeName);
+ Name := AnsiLowerCase(XfmrCodeName);
DSSObjType := ParClass.DSSClassType;
- // default values and sizes
+ // default values and sizes
FNPhases := 3;
NumWindings := 2;
MaxWindings := 2;
ActiveWinding := 1;
- Winding := Allocmem(Sizeof(Winding^[1]) * MaxWindings);
+ Winding := Allocmem(Sizeof(TWinding) * MaxWindings);
for i := 1 to MaxWindings do
- Winding^[i] := TWinding.Create;
+ Winding[i].Init();
XHL := 0.07;
XHT := 0.35;
XLT := 0.30;
@@ -630,7 +478,7 @@ constructor TXfmrCodeObj.Create(ParClass: TDSSClass; const XfmrCodeName: String)
EmergMaxHkVA := 1.5 * Winding^[1].kVA;
pctLoadLoss := 2.0 * Winding^[1].Rpu * 100.0; // assume two windings
ppm_FloatFactor := 0.000001;
- {Compute antifloat added for each winding }
+ // Compute antifloat added for each winding
for i := 1 to NumWindings do
Winding^[i].ComputeAntiFloatAdder(ppm_FloatFactor, VABase / FNPhases);
pctNoLoadLoss := 0.0;
@@ -639,17 +487,10 @@ constructor TXfmrCodeObj.Create(ParClass: TDSSClass; const XfmrCodeName: String)
NumkVARatings := 1;
SetLength(kVARatings, NumkVARatings);
kVARatings[0] := 600;
-
- InitPropertyValues(0);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TXfmrCodeObj.Destroy;
-var
- i: Integer;
begin
- for i := 1 to NumWindings do
- Winding^[i].Free;
Reallocmem(Winding, 0);
Reallocmem(XSC, 0);
inherited destroy;
@@ -659,53 +500,36 @@ procedure TXfmrCodeObj.PullFromTransformer(obj: TTransfObj);
var
i: Integer;
begin
- SetNumWindings(obj.NumberOfWindings);
+ SetNumWindings(obj.NumWindings);
FNPhases := obj.NPhases;
- XHL := obj.XhlVal;
- XHT := obj.XhtVal;
- XLT := obj.XltVal;
- VABase := obj.baseVA;
- NormMaxHKVA := obj.NormalHkVA;
- EmergMaxHKVA := obj.EmergHkVA;
- ThermalTimeConst := obj.thTau;
- n_thermal := obj.thN;
- m_thermal := obj.thM;
- FLrise := obj.thFLrise;
- HSrise := obj.thHSrise;
- pctLoadLoss := obj.loadLossPct;
- pctNoLoadLoss := obj.noLoadLossPct;
- ppm_FloatFactor := obj.ppmFloatFac;
- pctImag := obj.imagPct;
+ XHL := obj.Xhl;
+ XHT := obj.Xht;
+ XLT := obj.Xlt;
+ VABase := obj.VABase;
+ NormMaxHKVA := obj.NormMaxHKVA;
+ EmergMaxHKVA := obj.EmergMaxHKVA;
+ ThermalTimeConst := obj.ThermalTimeConst;
+ n_thermal := obj.n_thermal;
+ m_thermal := obj.m_thermal;
+ FLrise := obj.FLrise;
+ HSrise := obj.HSrise;
+ pctLoadLoss := obj.pctLoadLoss;
+ pctNoLoadLoss := obj.pctNoLoadLoss;
+ ppm_FloatFactor := obj.ppm_FloatFactor;
+ pctImag := obj.pctImag;
for i := 1 to (NumWindings - 1) * NumWindings div 2 do
XSC[i] := obj.XscVal[i];
for i := 1 to NumWindings do
- begin
- Winding^[i].Connection := obj.WdgConnection[i];
- Winding^[i].kvll := obj.BasekVLL[i];
- Winding^[i].VBase := obj.BaseVoltage[i];
- Winding^[i].kva := obj.WdgKVA[i];
- Winding^[i].puTap := obj.PresentTap[i];
- Winding^[i].Rpu := obj.WdgResistance[i];
- Winding^[i].Rneut := obj.WdgRneutral[i];
- Winding^[i].Xneut := obj.WdgXneutral[i];
- Winding^[i].Y_PPM := obj.WdgYPPM[i];
- Winding^[i].TapIncrement := obj.TapIncrement[i];
- Winding^[i].MinTap := obj.MinTap[i];
- Winding^[i].MaxTap := obj.MaxTap[i];
- Winding^[i].NumTaps := obj.NumTaps[i];
- end;
+ Winding[i] := obj.Winding[i];
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TXfmrCodeObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
+procedure TXfmrCodeObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i: Integer;
-
begin
inherited DumpProperties(F, Complete);
- {Basic Property Dump}
+ // Basic Property Dump
FSWriteln(F, '~ NumWindings=' + IntToStr(NumWindings));
FSWriteln(F, '~ phases=' + IntToStr(Fnphases));
@@ -764,146 +588,4 @@ procedure TXfmrCodeObj.DumpProperties(F: TFileStream; Complete: Boolean);
end;
end;
-function TXfmrCodeObj.GetPropertyValue(Index: Integer): String;
-
-{ gets the property for the active winding ; Set the active winding before calling}
-
-var
- i, k: Integer;
- TempStr: String;
-begin
- case Index of
- 11..14, 18, 33:
- Result := '[';
- else
- Result := '';
- end;
-
- case Index of
- 3:
- Result := IntToStr(ActiveWinding); // return active winding
- 4:
- case Winding^[ActiveWinding].Connection of
- 0:
- Result := 'wye ';
- 1:
- Result := 'delta ';
- else
- end;
- 5:
- Result := Format('%.7g', [Winding^[ActiveWinding].kvll]);
- 6:
- Result := Format('%.7g', [Winding^[ActiveWinding].kva]);
- 7:
- Result := Format('%.7g', [Winding^[ActiveWinding].puTap]);
- 8:
- Result := Format('%.7g', [Winding^[ActiveWinding].Rpu * 100.0]); // %R
- 9:
- Result := Format('%.7g', [Winding^[ActiveWinding].Rneut]);
- 10:
- Result := Format('%.7g', [Winding^[ActiveWinding].Xneut]);
-
- 11:
- for i := 1 to NumWindings do
- case Winding^[i].Connection of
- 0:
- Result := Result + 'wye, ';
- 1:
- Result := Result + 'delta, ';
- else
- end;
- 12:
- for i := 1 to NumWindings do
- Result := Result + Format('%.7g, ', [Winding^[i].kvll]);
- 13:
- for i := 1 to NumWindings do
- Result := Result + Format('%.7g, ', [Winding^[i].kVA]);
- 14:
- for i := 1 to NumWindings do
- Result := Result + Format('%.7g, ', [Winding^[i].puTap]);
- 18:
- for i := 1 to (NumWindings - 1) * NumWindings div 2 do
- Result := Result + Format('%-g, ', [Xsc^[i] * 100.0]);
- 24:
- Result := Format('%.7g', [pctLoadLoss]);
- 25:
- Result := Format('%.7g', [pctNoLoadLoss]);
- 28:
- Result := Format('%.7g', [Winding^[ActiveWinding].MaxTap]);
- 29:
- Result := Format('%.7g', [Winding^[ActiveWinding].MinTap]);
- 30:
- Result := Format('%-d', [Winding^[ActiveWinding].NumTaps]);
- 33:
- for i := 1 to NumWindings do
- Result := Result + Format('%.7g, ', [Winding^[i].rpu * 100.0]);
- 37:
- Result := Format('%.7g', [Winding^[ActiveWinding].RdcOhms]);
- 38:
- Result := inttostr(NumkVARatings);
- 39:
- begin
- TempStr := '[';
- for k := 1 to NumkVARatings do
- TempStr := TempStr + floattoStrf(kVARatings[k - 1], ffGeneral, 8, 4) + ',';
- TempStr := TempStr + ']';
- Result := TempStr;
- end;
- else
- Result := inherited GetPropertyValue(index);
- end;
-
- case Index of
- 11..14, 18, 33:
- Result := Result + ']';
- else
- end;
-end;
-
-procedure TXfmrCodeObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := '3'; //'phases';
- PropertyValue[2] := '2'; //'windings';
- PropertyValue[3] := '1'; //'wdg';
- PropertyValue[4] := 'wye'; // 'conn';
- PropertyValue[5] := '12.47'; // IF 2or 3-phase: phase-phase ELSE actual winding
- PropertyValue[6] := '1000';
- PropertyValue[7] := '1.0';
- PropertyValue[8] := '0.2';
- PropertyValue[9] := '-1';
- PropertyValue[10] := '0';
- PropertyValue[11] := '';
- PropertyValue[12] := ''; // IF 1-phase: actual winding rating; ELSE phase-phase
- PropertyValue[13] := ''; // IF 1-phase: actual winding rating; ELSE phase-phase
- PropertyValue[14] := '';
- PropertyValue[15] := '7';
- PropertyValue[16] := '35';
- PropertyValue[17] := '30';
- PropertyValue[18] := ''; // x12 13 14... 23 24.. 34 ..
- PropertyValue[19] := '2';
- PropertyValue[20] := '.8';
- PropertyValue[21] := '.8';
- PropertyValue[22] := '65';
- PropertyValue[23] := '15';
- PropertyValue[24] := '0';
- PropertyValue[25] := '0';
- PropertyValue[26] := '';
- PropertyValue[27] := '';
- PropertyValue[28] := '1.10';
- PropertyValue[29] := '0.90';
- PropertyValue[30] := '32';
- PropertyValue[31] := '0';
- PropertyValue[32] := '1';
- PropertyValue[33] := '';
- PropertyValue[34] := '7';
- PropertyValue[35] := '35';
- PropertyValue[36] := '30';
- PropertyValue[37] := '0.1';
- PropertyValue[38] := '1';
- PropertyValue[39] := '[600]';
-
- inherited InitPropertyValues(NumPropsThisClass);
-end;
-
-end.
+end.
\ No newline at end of file
diff --git a/src/Meters/EnergyMeter.pas b/src/Meters/EnergyMeter.pas
index 9cb6cc443..6182a4cda 100644
--- a/src/Meters/EnergyMeter.pas
+++ b/src/Meters/EnergyMeter.pas
@@ -6,76 +6,38 @@
All rights reserved.
----------------------------------------------------------
}
-{
- This class of device accumulates the energy of the voltage and current in the
- terminal of the device to which it is connected.
-
- It is an intelligent energy meter capable of measuring losses of all
- devices within its "zone".
-
- The Zone is determined automatically after a circuit change. The Zone starts on the
- opposite side of the branch on which the meter is located and continues in the same
- direction through the network until
- a) an open point is encountered
- b) an open terminal or switch is encountered
- c) another energy meter is encountered
- d) a branch that is already included in a zone is encountered
-
- It keeps track of kwh, kvarh, UE, EEN, Losses, etc., having registers FOR each
- of these quantities.
-
- In EEN/UE calculations, line overload takes precedence.
-
- If the Max Zone kW limits are specified, then these replace the line overload UE/EEN numbers.
- These limits were added so that the user can override line limits in cases
- such as networks where it is difficult to judge the UE from the individual
- line limits.
-
- Only the maximum |kVA| overload is accumulated, not all. Loads downline from
- an overload are marked WITH a factor representing the degree of overload. This
- is used to compute EEN/UE FOR loads.
-
- FOR low voltages, the full kW FOR loads below the emergency min voltage are counted.
- The EEN is proportioned based on how low the voltage is.
-
- Emergency min voltage must be less than normal min voltage.
-
-}
-
-{ CHANGE LOG
-
-8-3-99 Added Option property
- Revised EEN/UE computation to do either total or excess
-8-4-99 Save always rewrites file now and returns file name.
-
-11-11-99 Fixed bug in Take sample to use the maxvalue of the overload_EEN
-
-1-4-99 Modified tree checking to avoid picking up the same load more than once
- Fixed bugs in sampling load EEN/UE
- Modified Overload UE; added kwnormal, kwemerg properties for whole zone
-
-1-28-00 Changed to derived from Meter Element
-2-2-00 Trapezoidal Integration option
-4-14-00 Added load allocation algorithm
-4-17-00 Removed shunt capacitors from meter zones
-5-3-00 Corrected Zone kW, kvar accumulation to be actual power not target power
-5-29-00 Fixed problem with Nphases not being set right for 1-phase devices.
-6-15-01 Added Zonelist and LocalOnly options
-7/6/01 Added Voltage Only option for Load UE calcs.
-7/19/01 Added Totalizer Function for meterclass
-7/24/01 Added Generator registers and code for adding generators to zone lists.
- Changed to use zone loads and gens even if local only. If you only want the local
- measurements, specify a null zone manually.
-8/2/01 Fixed hole in Local only options.
-4/29/03 Added ReduceZone Function
-2/7/07 Fixed overload formulas
-9/18/08 Added load loss and no load loss registers and aux registers
-11/8/08 Revamped TakeSample to fix bugs with Demand Interval reporting
-8/8/13 Added initial reliability calcs
-3/27/2018 Corrected SAIDI calcs
-}
-
-{$WARN UNIT_PLATFORM OFF}
+// This class of device accumulates the energy of the voltage and current in the
+// terminal of the device to which it is connected.
+//
+// It is an intelligent energy meter capable of measuring losses of all
+// devices within its "zone".
+//
+// The Zone is determined automatically after a circuit change. The Zone starts on the
+// opposite side of the branch on which the meter is located and continues in the same
+// direction through the network until
+// a) an open point is encountered
+// b) an open terminal or switch is encountered
+// c) another energy meter is encountered
+// d) a branch that is already included in a zone is encountered
+//
+// It keeps track of kwh, kvarh, UE, EEN, Losses, etc., having registers FOR each
+// of these quantities.
+//
+// In EEN/UE calculations, line overload takes precedence.
+//
+// If the Max Zone kW limits are specified, then these replace the line overload UE/EEN numbers.
+// These limits were added so that the user can override line limits in cases
+// such as networks where it is difficult to judge the UE from the individual
+// line limits.
+//
+// Only the maximum |kVA| overload is accumulated, not all. Loads downline from
+// an overload are marked WITH a factor representing the degree of overload. This
+// is used to compute EEN/UE FOR loads.
+//
+// FOR low voltages, the full kW FOR loads below the emergency min voltage are counted.
+// The EEN is proportioned based on how low the voltage is.
+//
+// Emergency min voltage must be less than normal min voltage.
interface
@@ -88,7 +50,7 @@ interface
arrayDef,
DSSPointerList,
CktTree,
- ucomplex,
+ UComplex, DSSUcomplex,
Load,
Generator,
XYCurve,
@@ -98,7 +60,8 @@ interface
const
NumEMVbase = 7;
NumEMRegisters = 32 + 5 * NumEMVbase; // Total Number of energy meter registers
- {Fixed Registers}
+
+ // Fixed Registers
Reg_kWh = 1;
Reg_kvarh = 2;
Reg_MaxkW = 3;
@@ -134,6 +97,36 @@ interface
Reg_VBaseStart = 32; // anchor for the voltage base loss registers
type
+{$SCOPEDENUMS ON}
+ TEnergyMeterProp = (
+ INVALID = 0,
+ element = 1,
+ terminal = 2,
+ action = 3,
+ option = 4,
+ kVAnormal = 5,
+ kVAemerg = 6,
+ peakcurrent = 7,
+ Zonelist = 8,
+ LocalOnly = 9,
+ Mask = 10,
+ Losses = 11,
+ LineLosses = 12,
+ XfmrLosses = 13,
+ SeqLosses = 14,
+ __3phaseLosses = 15,
+ VbaseLosses = 16, // segregate losses by voltage base
+ PhaseVoltageReport = 17, // Compute Avg phase voltages in zone
+ Int_Rate = 18,
+ Int_Duration = 19,
+ SAIFI = 20, // Read only
+ SAIFIkW = 21, // Read only
+ SAIDI = 22, // Read only
+ CAIDI = 23, // Read only
+ CustInterrupts = 24 // Read only
+ );
+{$SCOPEDENUMS OFF}
+
TRegisterArray = array[1..NumEMregisters] of Double;
// --------- Feeder Section Definition -----------
@@ -153,6 +146,8 @@ TFeederSection = record
FeederSectionArray = array[0..100] of TFeederSection; // Dummy dimension
// --------- Feeder Section Definition -----------
+ TEnergyMeter = class;
+
TSystemMeter = class(Tobject)
PRIVATE
kWh, dkWh,
@@ -184,9 +179,8 @@ TSystemMeter = class(Tobject)
procedure Reset;
procedure Save;
- constructor Create(dssContext: TDSSContext);
+ constructor Create(EnergyMeterClass: TEnergyMeter);
destructor Destroy; OVERRIDE;
-
end;
TEnergyMeter = class(TMeterClass) // derive strait from base class
@@ -194,7 +188,6 @@ TEnergyMeter = class(TMeterClass) // derive strait from base class
FSaveDemandInterval: Boolean;
FDI_Verbose: Boolean;
- procedure ProcessOptions(const Opts: String);
procedure Set_SaveDemandInterval(const Value: Boolean);
function Get_SaveDemandInterval: Boolean;
procedure CreateMeterTotals;
@@ -205,7 +198,6 @@ TEnergyMeter = class(TMeterClass) // derive strait from base class
procedure OpenVoltageReportFile;
procedure WriteOverloadReport;
procedure WriteVoltageReport;
- procedure InterpretRegisterMaskArray(var Mask: TRegisterArray);
procedure Set_DI_Verbose(const Value: Boolean);
function Get_DI_Verbose: Boolean;
@@ -213,51 +205,46 @@ TEnergyMeter = class(TMeterClass) // derive strait from base class
// Moved from global unit vars
Delta_Hrs: Double;
- // adjacency lists for PC and PD elements at each bus, built for faster searches
+ // adjacency lists for PC and PD elements at each bus, built for faster searches
BusAdjPC: TAdjArray; // also includes shunt PD elements
BusAdjPD: TAdjArray;
- {*******************************************************************************
- * Nomenclature: *
- * OV_ Overloads *
- * VR_ Voltage report *
- * DI_ Demand interval *
- * SI_ System Demand interval *
- * TDI_ DI Totals *
- * FM_ Meter Totals *
- * SM_ System Mater *
- * EMT_ Energy Meter Totals *
- * PHV_ Phase Voltage Report *
- * These prefixes are applied to the variables of each file mapped into *
- * Memory using the MemoryMap_Lib *
- ********************************************************************************
- }
+ // ********************************************************************************
+ // * Nomenclature: *
+ // * OV_ Overloads *
+ // * VR_ Voltage report *
+ // * DI_ Demand interval *
+ // * SI_ System Demand interval *
+ // * TDI_ DI Totals *
+ // * FM_ Meter Totals *
+ // * SM_ System Mater *
+ // * EMT_ Energy Meter Totals *
+ // * PHV_ Phase Voltage Report *
+ // * These prefixes are applied to the variables of each file mapped into *
+ // * Memory using the MemoryMap_Lib *
+ // ********************************************************************************
+
PUBLIC
OV_MHandle: TBytesStream; // a. Handle to the file in memory
VR_MHandle: TBytesStream;
OV_Append: Boolean;
VR_Append: Boolean;
+ SDI_Append: Boolean;
+ TDI_Append: Boolean;
+ SM_Append: Boolean;
+ EMT_Append: Boolean;
+ FM_Append: Boolean;
+
PROTECTED
- DI_MHandle: TBytesStream;
SDI_MHandle: TBytesStream;
TDI_MHandle: TBytesStream;
SM_MHandle: TBytesStream;
EMT_MHandle: TBytesStream;
- PHV_MHandle: TBytesStream;
FM_MHandle: TBytesStream;
- //*********** Flags for appending Files*****************************************
- DI_Append: Boolean;
- SDI_Append: Boolean;
- TDI_Append: Boolean;
- SM_Append: Boolean;
- EMT_Append: Boolean;
- PHV_Append: Boolean;
- FM_Append: Boolean;
- procedure DefineProperties;
- function MakeLike(const EnergyMeterName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
procedure SetHasMeterFlag;
PUBLIC
@@ -276,8 +263,9 @@ TEnergyMeter = class(TMeterClass) // derive strait from base class
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function BeginEdit(ptr: Pointer; SetActive_: Boolean=True): Pointer; override;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
procedure ResetMeterZonesAll;
procedure ResetAll; OVERRIDE; // Reset all meters in active circuit to zero
@@ -299,25 +287,23 @@ TEnergyMeterObj = class(TMeterElement)
ExcessFlag: Boolean;
ZoneIsRadial: Boolean;
VoltageUEOnly: Boolean;
- LocalOnly: Boolean;
-
- FLosses: Boolean;
- FLineLosses: Boolean;
- FXfmrLosses: Boolean;
- FSeqLosses: Boolean;
- F3PhaseLosses: Boolean;
- FVBaseLosses: Boolean;
- FPhaseVoltageReport: Boolean;
-
- DefinedZoneList: pStringArray;
- DefinedZoneListSize: Integer;
-
- {Limits on the entire load in the zone for networks where UE cannot be determined
- by the individual branches}
+ LocalOnly,
+ FLosses,
+ FLineLosses,
+ FXfmrLosses,
+ FSeqLosses,
+ F3PhaseLosses,
+ FVBaseLosses,
+ FPhaseVoltageReport: LongBool;
+
+ DefinedZoneList: TStringList;
+
+ // Limits on the entire load in the zone for networks where UE cannot be determined
+ // by the individual branches
MaxZonekVA_Norm: Double;
MaxZonekVA_Emerg: Double;
- {Voltage bases in the Meter Zone}
+ // Voltage bases in the Meter Zone
VBaseTotalLosses: pDoubleArray; // allocated array
VBaseLineLosses: pDoubleArray;
VBaseLoadLosses: pDoubleArray;
@@ -327,14 +313,14 @@ TEnergyMeterObj = class(TMeterElement)
VBaseCount: Integer;
MaxVBaseCount: Integer;
- { Arrays for phase voltage report }
+ // Arrays for phase voltage report
VphaseMax: pDoubleArray;
VPhaseMin: pDoubleArray;
VPhaseAccum: pDoubleArray;
VPhaseAccumCount: pIntegerArray;
VPhaseReportFileIsOpen: Boolean;
- {Demand Interval File variables}
+ // Demand Interval File variables
This_Meter_DIFileIsOpen: Boolean;
@@ -359,6 +345,24 @@ TEnergyMeterObj = class(TMeterElement)
procedure AppendDemandIntervalFile;
PUBLIC
+ // ********************************************************************************
+ // * Nomenclature: *
+ // * OV_ Overloads *
+ // * VR_ Voltage report *
+ // * DI_ Demand interval *
+ // * SI_ System Demand interval *
+ // * TDI_ DI Totals *
+ // * FM_ Meter Totals *
+ // * SM_ System Mater *
+ // * EMT_ Energy Meter Totals *
+ // * PHV_ Phase Voltage Report *
+ // * These prefixes are applied to the variables of each file mapped into *
+ // * Memory using the MemoryMap_Lib *
+ // ********************************************************************************
+
+ DI_MHandle: TBytesStream;
+ PHV_MHandle: TBytesStream;
+
RegisterNames: array[1..NumEMregisters] of String;
BranchList: TCktTree; // Pointers to all circuit elements in meter's zone
@@ -385,46 +389,16 @@ TEnergyMeterObj = class(TMeterElement)
FeederSections: pFeederSections;
ZonePCE: Array of string;
- {*******************************************************************************
- * Nomenclature: *
- * OV_ Overloads *
- * VR_ Voltage report *
- * DI_ Demand interval *
- * SI_ System Demand interval *
- * TDI_ DI Totals *
- * FM_ Meter Totals *
- * SM_ System Mater *
- * EMT_ Energy Meter Totals *
- * PHV_ Phase Voltage Report *
- * These prefixes are applied to the variables of each file mapped into *
- * Memory using the MemoryMap_Lib *
- ********************************************************************************
- }
- OV_MHandle: TBytesStream; // a. Handle to the file in memory
- VR_MHandle: TBytesStream;
- DI_MHandle: TBytesStream;
- SDI_MHandle: TBytesStream;
- TDI_MHandle: TBytesStream;
- SM_MHandle: TBytesStream;
- EMT_MHandle: TBytesStream;
- PHV_MHandle: TBytesStream;
- FM_MHandle: TBytesStream;
-
- //*********** Flags for appending Files*****************************************
- OV_Append: Boolean;
- VR_Append: Boolean;
+ //*********** Flags for appending Files*****************************************
DI_Append: Boolean;
- SDI_Append: Boolean;
- TDI_Append: Boolean;
- SM_Append: Boolean;
- EMT_Append: Boolean;
PHV_Append: Boolean;
- FM_Append: Boolean;
-
+
constructor Create(ParClass: TDSSClass; const EnergyMeterName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model, reset nphases
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model, reset nphases
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
procedure GetCurrents(Curr: pComplexArray); OVERRIDE; //Get present value of terminal Curr
@@ -443,16 +417,14 @@ TEnergyMeterObj = class(TMeterElement)
procedure CalcReliabilityIndices(AssumeRestoration: Boolean);
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
end;
implementation
uses
- ParserDel,
+ BufStream,
DSSClassDefs,
DSSGlobals,
Bus,
@@ -466,40 +438,51 @@ implementation
Line,
LineUnits,
ReduceAlgs,
-{$IFNDEF FPC}
- Windows,
-{$ENDIF}
Math,
MemoryMap_Lib,
DSSHelper,
DSSObjectHelper,
TypInfo;
- // {$DEFINE DEBUG}
-{$UNDEF DEBUG}
-
+type
+ TObj = TEnergyMeterObj;
+ TProp = TEnergyMeterProp;
+{$PUSH}
+{$Z4} // keep enums as int32 values
+ TEnergyMeterAction = (
+ Allocate = 0,
+ Clear = 1,
+ Reduce = 2,
+ Save = 3,
+ Take = 4,
+ ZoneDump = 5
+ );
+{$POP}
const
- NumPropsThisClass = 24;
-
-
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
+ ActionEnum: TDSSEnum;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function jiIndex(i, j: Integer): Integer; inline;
begin
Result := (j - 1) * 3 + i;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TEnergyMeter.Create(dssContext: TDSSContext); // Creates superstructure FOR all EnergyMeter objects
+constructor TEnergyMeter.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'EnergyMeter';
- DSSClassType := DSSClassType + ENERGY_METER;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ ActionEnum := TDSSEnum.Create('EnergyMeter: Action', True, 1, 2,
+ ['Allocate', 'Clear', 'Reduce', 'Save', 'Take', 'ZoneDump'],
+ [0, 1, 2, 3, 4, 5]);
+ end;
- ActiveElement := 0;
+ inherited Create(dssContext, ENERGY_METER, 'EnergyMeter');
- {Initialice demand interval options to off}
+ // Initialize demand interval options to off
FSaveDemandInterval := FALSE;
FDI_Verbose := FALSE;
@@ -507,40 +490,27 @@ constructor TEnergyMeter.Create(dssContext: TDSSContext); // Creates superstruc
OverLoadFileIsOpen := FALSE;
VoltageFileIsOpen := FALSE;
-
Do_VoltageExceptionReport := FALSE;
DI_Dir := '';
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
-
-
- SystemMeter := TSystemMeter.Create(DSS);
+ SystemMeter := TSystemMeter.Create(self);
OV_MHandle := NIL;
VR_MHandle := NIL;
- DI_MHandle := NIL;
SDI_MHandle := NIL;
TDI_MHandle := NIL;
SM_MHandle := NIL;
EMT_MHandle := NIL;
- PHV_MHandle := NIL;
FM_MHandle := NIL;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TEnergyMeter.Destroy;
-
begin
SystemMeter.Free;
if OV_MHandle <> NIL then
OV_MHandle.Free;
if VR_MHandle <> NIL then
VR_MHandle.Free;
- if DI_MHandle <> NIL then
- DI_MHandle.Free;
if SDI_MHandle <> NIL then
SDI_MHandle.Free;
if TDI_MHandle <> NIL then
@@ -549,343 +519,270 @@ destructor TEnergyMeter.Destroy;
SM_MHandle.Free;
if EMT_MHandle <> NIL then
EMT_MHandle.Free;
- if PHV_MHandle <> NIL then
- PHV_MHandle.Free;
if FM_MHandle <> NIL then
FM_MHandle.Free;
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
+procedure DoAction(Obj: TObj; action: TEnergyMeterAction);
+begin
+ case action of
+ TEnergyMeterAction.Allocate:
+ Obj.AllocateLoad;
+ TEnergyMeterAction.Clear:
+ Obj.ResetRegisters;
+ TEnergyMeterAction.Reduce:
+ Obj.ReduceZone;
+ TEnergyMeterAction.Save:
+ Obj.SaveRegisters;
+ TEnergyMeterAction.Take:
+ Obj.TakeSample;
+ TEnergyMeterAction.Zonedump:
+ Obj.ZoneDump;
+ end;
+end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TEnergyMeter.DefineProperties;
+procedure SetOptions(Obj: TObj; Value: TStringList);
+var
+ i: Integer;
+begin
+ for i := 0 to Value.Count - 1 do
+ case AnsiLowerCase(Value.Strings[i][1])[1] of
+ 'e':
+ Obj.ExcessFlag := TRUE;
+ 't':
+ Obj.ExcessFlag := FALSE;
+ 'r':
+ Obj.ZoneIsRadial := TRUE;
+ 'm':
+ Obj.ZoneIsRadial := FALSE;
+ 'c':
+ Obj.VoltageUEOnly := FALSE;
+ 'v':
+ Obj.VoltageUEOnly := TRUE;
+ end;
+ Value.Free;
+end;
+
+function GetOptions(Obj: TObj; Index: Integer): TStringList;
begin
+ Result := TStringList.Create();
+
+ if Obj.ExcessFlag then
+ Result.Add('E')
+ else
+ Result.Add('T');
+
+ if Obj.ZoneIsRadial then
+ Result.Add('R')
+ else
+ Result.Add('M');
+
+ if Obj.VoltageUEOnly then
+ Result.Add('V')
+ else
+ Result.Add('C');
+end;
+procedure TEnergyMeter.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
+begin
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
-
- PropertyName^[1] := 'element';
- PropertyName^[2] := 'terminal';
- PropertyName^[3] := 'action';
- PropertyName^[4] := 'option';
- PropertyName^[5] := 'kVAnormal';
- PropertyName^[6] := 'kVAemerg';
- PropertyName^[7] := 'peakcurrent';
- PropertyName^[8] := 'Zonelist';
- PropertyName^[9] := 'LocalOnly';
- PropertyName^[10] := 'Mask';
- PropertyName^[11] := 'Losses';
- PropertyName^[12] := 'LineLosses';
- PropertyName^[13] := 'XfmrLosses';
- PropertyName^[14] := 'SeqLosses';
- PropertyName^[15] := '3phaseLosses';
- PropertyName^[16] := 'VbaseLosses'; // segregate losses by voltage base
- PropertyName^[17] := 'PhaseVoltageReport'; // Compute Avg phase voltages in zone
- PropertyName^[18] := 'Int_Rate';
- PropertyName^[19] := 'Int_Duration';
- PropertyName^[20] := 'SAIFI'; // Read only
- PropertyName^[21] := 'SAIFIkW'; // Read only
- PropertyName^[22] := 'SAIDI'; // Read only
- PropertyName^[23] := 'CAIDI'; // Read only
- PropertyName^[24] := 'CustInterrupts'; // Read only
-
-{ PropertyName^[11] := 'Feeder'; **** removed - not used}
-
- PropertyHelp[1] := 'Name (Full Object name) of element to which the monitor is connected.';
- PropertyHelp[2] := 'Number of the terminal of the circuit element to which the monitor is connected. ' +
- '1 or 2, typically.';
- PropertyHelp[3] := '{Clear (reset) | Save | Take | Zonedump | Allocate | Reduce} ' + CRLF + CRLF +
- '(A)llocate = Allocate loads on the meter zone to match PeakCurrent.' + CRLF +
- '(C)lear = reset all registers to zero' + CRLF +
- '(R)educe = reduces zone by merging lines (see Set Keeplist & ReduceOption)' + CRLF +
- '(S)ave = saves the current register values to a file.' + CRLF +
- ' File name is "MTR_metername.CSV".' + CRLF +
- '(T)ake = Takes a sample at present solution' + CRLF +
- '(Z)onedump = Dump names of elements in meter zone to a file' + CRLF +
- ' File name is "Zone_metername.CSV".';
- PropertyHelp[4] := 'Enter a string ARRAY of any combination of the following. Options processed left-to-right:' + CRLF + CRLF +
- '(E)xcess : (default) UE/EEN is estimate of energy over capacity ' + CRLF +
- '(T)otal : UE/EEN is total energy after capacity exceeded' + CRLF +
- '(R)adial : (default) Treats zone as a radial circuit' + CRLF +
- '(M)esh : Treats zone as meshed network (not radial).' + CRLF +
- '(C)ombined : (default) Load UE/EEN computed from combination of overload and undervoltage.' + CRLF +
- '(V)oltage : Load UE/EEN computed based on voltage only.' + CRLF + CRLF +
- 'Example: option=(E, R)';
- PropertyHelp[5] := 'Upper limit on kVA load in the zone, Normal configuration. Default is 0.0 (ignored). ' +
- 'Overrides limits on individual lines for overload EEN. ' +
- 'With "LocalOnly=Yes" option, uses only load in metered branch.';
- PropertyHelp[6] := 'Upper limit on kVA load in the zone, Emergency configuration. Default is 0.0 (ignored). ' +
- 'Overrides limits on individual lines for overload UE. ' +
- 'With "LocalOnly=Yes" option, uses only load in metered branch.';
- PropertyHelp[7] := 'ARRAY of current magnitudes representing the peak currents measured at this location ' +
- 'for the load allocation function. Default is (400, 400, 400). Enter one current for each phase';
- PropertyHelp[8] := 'ARRAY of full element names for this meter''s zone. Default is for meter to find it''s own zone. ' +
- 'If specified, DSS uses this list instead. Can access the names in a single-column text file. Examples: ' + crlf + crlf +
- 'zonelist=[line.L1, transformer.T1, Line.L3] ' + CRLF +
- 'zonelist=(file=branchlist.txt)';
- PropertyHelp[9] := '{Yes | No} Default is NO. If Yes, meter considers only the monitored element ' +
- 'for EEN and UE calcs. Uses whole zone for losses.';
- PropertyHelp[10] := 'Mask for adding registers whenever all meters are totalized. Array of floating point numbers ' +
- 'representing the multiplier to be used for summing each register from this meter. ' +
- 'Default = (1, 1, 1, 1, ... ). You only have to enter as many as are changed (positional). ' +
- 'Useful when two meters monitor same energy, etc.';
- PropertyHelp[11] := '{Yes | No} Default is YES. Compute Zone losses. If NO, then no losses at all are computed.';
- PropertyHelp[12] := '{Yes | No} Default is YES. Compute Line losses. If NO, then none of the losses are computed.';
- PropertyHelp[13] := '{Yes | No} Default is YES. Compute Transformer losses. If NO, transformers are ignored in loss calculations.';
- PropertyHelp[14] := '{Yes | No} Default is YES. Compute Sequence losses in lines and segregate by line mode losses and zero mode losses.';
- PropertyHelp[15] := '{Yes | No} Default is YES. Compute Line losses and segregate by 3-phase and other (1- and 2-phase) line losses. ';
- PropertyHelp[16] := '{Yes | No} Default is YES. Compute losses and segregate by voltage base. If NO, then voltage-based tabulation is not reported.';
- PropertyHelp[17] := '{Yes | No} Default is NO. Report min, max, and average phase voltages for the zone and tabulate by voltage base. ' +
- 'Demand Intervals must be turned on (Set Demand=true) and voltage bases must be defined for this property to take effect. ' +
- 'Result is in a separate report file.';
- PropertyHelp[18] := 'Average number of annual interruptions for head of the meter zone (source side of zone or feeder).';
- PropertyHelp[19] := 'Average annual duration, in hr, of interruptions for head of the meter zone (source side of zone or feeder).';
- PropertyHelp[20] := '(Read only) Makes SAIFI result available via return on query (? energymeter.myMeter.SAIFI.';
- PropertyHelp[21] := '(Read only) Makes SAIFIkW result available via return on query (? energymeter.myMeter.SAIFIkW.';
- PropertyHelp[22] := '(Read only) Makes SAIDI result available via return on query (? energymeter.myMeter.SAIDI.';
- PropertyHelp[23] := '(Read only) Makes CAIDI result available via return on query (? energymeter.myMeter.CAIDI.';
- PropertyHelp[24] := '(Read only) Makes Total Customer Interrupts value result available via return on query (? energymeter.myMeter.CustInterrupts.';
-(**** Not used in present version
- PropertyHelp[11]:= '{Yes/True | No/False} Default is NO. If set to Yes, a Feeder object is created corresponding to ' +
- 'the energymeter. Feeder is enabled if Radial=Yes; diabled if Radial=No. Feeder is ' +
- 'synched automatically with the meter zone. Do not create feeders for zones in meshed transmission systems.';
-*****)
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ PropertyType[ord(TProp.peakcurrent)] := TPropertyType.DoubleVArrayProperty;
+ PropertyOffset[ord(TProp.peakcurrent)] := ptruint(@obj.SensorCurrent);
+ PropertyOffset2[ord(TProp.peakcurrent)] := ptruint(@obj.Fnphases);
+
+ PropertyType[ord(TProp.Mask)] := TPropertyType.DoubleFArrayProperty;
+ PropertyOffset[ord(TProp.Mask)] := ptruint(@obj.TotalsMask); //TODO: validate the actual values?
+ PropertyOffset2[ord(TProp.Mask)] := NumEMRegisters;
+
+ // boolean properties
+ PropertyType[ord(TProp.LocalOnly)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.Losses)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.LineLosses)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.XfmrLosses)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.SeqLosses)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.__3PhaseLosses)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.VBaseLosses)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.PhaseVoltageReport)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.LocalOnly)] := ptruint(@obj.LocalOnly);
+ PropertyOffset[ord(TProp.Losses)] := ptruint(@obj.FLosses);
+ PropertyOffset[ord(TProp.LineLosses)] := ptruint(@obj.FLineLosses);
+ PropertyOffset[ord(TProp.XfmrLosses)] := ptruint(@obj.FXfmrLosses);
+ PropertyOffset[ord(TProp.SeqLosses)] := ptruint(@obj.FSeqLosses);
+ PropertyOffset[ord(TProp.__3PhaseLosses)] := ptruint(@obj.F3PhaseLosses);
+ PropertyOffset[ord(TProp.VBaseLosses)] := ptruint(@obj.FVBaseLosses);
+ PropertyOffset[ord(TProp.PhaseVoltageReport)] := ptruint(@obj.FPhaseVoltageReport);
+
+ // object reference
+ PropertyType[ord(TProp.element)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.element)] := ptruint(@obj.MeteredElement);
+ PropertyOffset2[ord(TProp.element)] := 0;
+ //PropertyFlags[ord(TProp.element)] := [TPropertyFlag.CheckForVar]; // not required for general cktelements
+
+ // integer properties
+ PropertyType[ord(TProp.terminal)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.terminal)] := ptruint(@obj.MeteredTerminal);
+
+ // read-only doubles
+ PropertyFlags[ord(TProp.SAIFI)] := [TPropertyFlag.SilentReadOnly];
+ PropertyFlags[ord(TProp.SAIFIkW)] := [TPropertyFlag.SilentReadOnly];
+ PropertyFlags[ord(TProp.SAIDI)] := [TPropertyFlag.SilentReadOnly];
+ PropertyFlags[ord(TProp.CAIDI)] := [TPropertyFlag.SilentReadOnly];
+ PropertyFlags[ord(TProp.CustInterrupts)] := [TPropertyFlag.SilentReadOnly];
+ PropertyOffset[ord(TProp.SAIFI)] := ptruint(@obj.SAIFI);
+ PropertyOffset[ord(TProp.SAIFIkW)] := ptruint(@obj.SAIFIkW);
+ PropertyOffset[ord(TProp.SAIDI)] := ptruint(@obj.SAIDI);
+ PropertyOffset[ord(TProp.CAIDI)] := ptruint(@obj.CAIDI);
+ PropertyOffset[ord(TProp.CustInterrupts)] := ptruint(@obj.CustInterrupts);
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.kVAnormal)] := ptruint(@obj.MaxZonekVA_Norm);
+ PropertyOffset[ord(TProp.kVAemerg)] := ptruint(@obj.MaxZonekVA_Emerg);
+ PropertyOffset[ord(TProp.Int_Rate)] := ptruint(@obj.Source_NumInterruptions);
+ PropertyOffset[ord(TProp.Int_Duration)] := ptruint(@obj.Source_IntDuration);
+
+ // string list
+ PropertyType[ord(TProp.Zonelist)] := TPropertyType.StringListProperty;
+ PropertyOffset[ord(TProp.Zonelist)] := ptruint(@obj.DefinedZoneList);
+
+ // custom list
+ PropertyType[ord(TProp.option)] := TPropertyType.StringListProperty;
+ PropertyOffset[ord(TProp.option)] := 1; // dummy value
+ PropertyWriteFunction[ord(TProp.option)] := @SetOptions;
+ PropertyReadFunction[ord(TProp.option)] := @GetOptions;
+ PropertyFlags[ord(TProp.option)] := [TPropertyFlag.WriteByFunction, TPropertyFlag.ReadByFunction];
+
+ // enum action
+ PropertyType[ord(TProp.Action)] := TPropertyType.StringEnumActionProperty;
+ PropertyOffset[ord(TProp.Action)] := ptruint(@DoAction);
+ PropertyOffset2[ord(TProp.Action)] := PtrInt(ActionEnum);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
+ inherited DefineProperties;
+end;
+function TEnergyMeter.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
+begin
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TEnergyMeter.NewObject(const ObjName: String): Integer;
+procedure TEnergyMeterObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
+var
+ i: Integer;
begin
- // create a new object of this class and add to list
- with ActiveCircuit do
- begin
- ActiveCktElement := TEnergyMeterObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
+ case Idx of
+ 1, 2:
+ begin
+ MeteredElementChanged := TRUE;
+ Include(Flags, Flg.NeedsRecalc);
+ end;
+ ord(TProp.Mask):
+ for i := previousIntVal + 1 to NumEMRegisters do
+ TotalsMask[i] := 1.0; // Set the rest to 1
end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TEnergyMeter.Edit: Integer;
-
+function TEnergyMeter.BeginEdit(ptr: Pointer; SetActive_: Boolean): Pointer;
var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
- DoRecalc: Boolean;
-
+ Obj: TObj;
begin
+ Obj := TObj(inherited BeginEdit(ptr, SetActive_));
+ if SetActive_ then
+ DSS.ActiveEnergyMeterObj := Obj;
+ Obj.MeteredElementChanged := FALSE;
+ Result := Obj;
+end;
- // continue parsing WITH contents of Parser
- // continue parsing WITH contents of Parser
- DSS.ActiveEnergyMeterObj := ElementList.Active;
- DSS.ActiveCircuit.ActiveCktElement := DSS.ActiveEnergyMeterObj;
-
- Result := 0;
-
- DoRecalc := FALSE;
-
- with DSS.ActiveEnergyMeterObj do
+function TEnergyMeter.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
begin
-
- MeteredElementChanged := FALSE;
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if (Length(ParamName) = 0) then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 520);
- 1:
- ElementName := lowercase(param);
- 2:
- MeteredTerminal := Parser.IntValue;
- 3:
- begin {Actions}
- param := lowercase(param);
- case param[1] of
- 'a':
- AllocateLoad;
- 'c':
- ResetRegisters;
- 'r':
- ReduceZone;
- 's':
- SaveRegisters;
- 't':
- TakeSample;
- 'z':
- ZoneDump;
- end;
- end;
- 4:
- ProcessOptions(Param);
- 5:
- MaxZonekVA_Norm := Parser.DblValue;
- 6:
- MaxZonekVA_Emerg := Parser.DblValue;
- 7:
- parser.ParseAsVector(Fnphases, SensorCurrent); // Inits to zero
- 8:
- InterpretAndAllocStrArray(Param, DefinedZoneListSize, DefinedZoneList);
- 9:
- LocalOnly := InterpretYesNo(Param);
- 10:
- InterpretRegisterMaskArray(TotalsMask);
- 11:
- FLosses := InterpretYesNo(Param);
- 12:
- FLineLosses := InterpretYesNo(Param);
- 13:
- FXfmrLosses := InterpretYesNo(Param);
- 14:
- FSeqLosses := InterpretYesNo(Param);
- 15:
- F3PhaseLosses := InterpretYesNo(Param);
- 16:
- FVBaseLosses := InterpretYesNo(Param);
- 17:
- FPhaseVoltageReport := InterpretYesNo(Param);
- 18:
- Source_NumInterruptions := Parser.dblvalue; // Annual interruptions for upline circuit
- 19:
- Source_IntDuration := Parser.dblValue; // hours
- 20:
- PropertyValue[20] := ''; // placeholder, do nothing just throw value away if someone tries to set it.
- 21:
- PropertyValue[21] := ''; // placeholder, do nothing just throw value away if someone tries to set it.
- 22:
- PropertyValue[22] := ''; // placeholder, do nothing just throw value away if someone tries to set it.
- 23:
- PropertyValue[23] := ''; // placeholder, do nothing just throw value away if someone tries to set it.
- 24:
- PropertyValue[24] := ''; // placeholder, do nothing just throw value away if someone tries to set it.
- else
- ClassEdit(DSS.ActiveEnergyMeterObj, ParamPointer - NumPropsthisClass)
- end;
-
- case ParamPointer of
- 1, 2:
- begin
- MeteredElementChanged := TRUE;
- DoRecalc := TRUE;
- end;
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
- if DoRecalc then
+ if flg.NeedsRecalc in Flags then
RecalcElementData; // When some basic data have changed
+ Exclude(Flags, Flg.EditionActive);
end;
+ Result := True;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TEnergyMeter.MakeLike(const EnergyMeterName: String): Integer;
+procedure TEnergyMeterObj.MakeLike(OtherPtr: Pointer);
var
- OtherEnergyMeter: TEnergyMeterObj;
+ Other: TObj;
i: Integer;
begin
- Result := 0;
- {See IF we can find this EnergyMeter name in the present collection}
- OtherEnergyMeter := Find(EnergyMeterName);
- if OtherEnergyMeter <> NIL then
- with DSS.ActiveEnergyMeterObj do
- begin
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
- NPhases := OtherEnergyMeter.Fnphases;
- NConds := OtherEnergyMeter.Fnconds; // Force Reallocation of terminal stuff
-
- ElementName := OtherEnergyMeter.ElementName;
- MeteredElement := OtherEnergyMeter.MeteredElement; // Pointer to target circuit element
- MeteredTerminal := OtherEnergyMeter.MeteredTerminal;
- ExcessFlag := OtherEnergyMeter.ExcessFlag;
-
- MaxZonekVA_Norm := OtherEnergyMeter.MaxZonekVA_Norm;
- MaxZonekVA_Emerg := OtherEnergyMeter.MaxZonekVA_emerg;
-
- // Reliability
- Source_NumInterruptions := OtherEnergyMeter.Source_NumInterruptions;
- Source_IntDuration := OtherEnergyMeter.Source_IntDuration;
-
- FreeStringArray(DefinedZoneList, DefinedZoneListSize);
- DefinedZoneListSize := OtherEnergyMeter.DefinedZoneListSize;
- DefinedZoneList := AllocStringArray(DefinedZoneListSize);
- // Copy Strings over (actually incr ref count on string)
- for i := 1 to DefinedZoneListSize do
- DefinedZoneList^[i] := OtherEnergyMeter.DefinedZoneList^[i];
-
- LocalOnly := OtherEnergyMeter.LocalOnly;
- VoltageUEOnly := OtherEnergyMeter.VoltageUEOnly;
-
- {Boolean Flags}
- FLosses := OtherEnergyMeter.FLosses;
- FLineLosses := OtherEnergyMeter.FLineLosses;
- FXfmrLosses := OtherEnergyMeter.FXfmrLosses;
- FSeqLosses := OtherEnergyMeter.FSeqLosses;
- F3PhaseLosses := OtherEnergyMeter.F3PhaseLosses;
- FVBaseLosses := OtherEnergyMeter.FVBaseLosses;
- FPhaseVoltageReport := OtherEnergyMeter.FPhaseVoltageReport;
-
- for i := 1 to ParentClass.NumProperties do
- // Skip Read Only properties
- if i < 20 then
- PropertyValue[i] := OtherEnergyMeter.PropertyValue[i];
+ MeteredElement := Other.MeteredElement; // Pointer to target circuit element
+ MeteredTerminal := Other.MeteredTerminal;
+ ExcessFlag := Other.ExcessFlag;
- end
- else
- DoSimpleMsg('Error in EnergyMeter MakeLike: "' + EnergyMeterName + '" Not Found.', 521);
+ MaxZonekVA_Norm := Other.MaxZonekVA_Norm;
+ MaxZonekVA_Emerg := Other.MaxZonekVA_emerg;
+
+ // Reliability
+ Source_NumInterruptions := Other.Source_NumInterruptions;
+ Source_IntDuration := Other.Source_IntDuration;
+
+ DefinedZoneList.Clear;
+ // Copy Strings over (actually incr ref count on string)
+ for i := 0 to (Other.DefinedZoneList.Count - 1) do
+ DefinedZoneList.Add(Other.DefinedZoneList[i]);
+ LocalOnly := Other.LocalOnly;
+ VoltageUEOnly := Other.VoltageUEOnly;
+
+ // Boolean Flags
+ FLosses := Other.FLosses;
+ FLineLosses := Other.FLineLosses;
+ FXfmrLosses := Other.FXfmrLosses;
+ FSeqLosses := Other.FSeqLosses;
+ F3PhaseLosses := Other.F3PhaseLosses;
+ FVBaseLosses := Other.FVBaseLosses;
+ FPhaseVoltageReport := Other.FPhaseVoltageReport;
end;
-{--------------------------------------------------------------------------}
procedure TEnergyMeter.ResetMeterZonesAll; // Force all EnergyMeters in the circuit to reset their meter zones
-
var
mtr: TEnergyMeterObj;
pCktElement: TDSSCktElement;
PDElem: TPDElement;
PCElem: TPCElement;
i: Integer;
-
begin
with ActiveCircuit do
begin
if Energymeters.Count = 0 then
Exit; // Do not do anything
- // initialize the Checked Flag FOR all circuit Elements
+ // initialize the Checked Flag FOR all circuit Elements
pCktElement := CktElements.First;
while (pCktElement <> NIL) do
begin
with pCktElement do
begin
- Checked := FALSE;
- IsIsolated := TRUE;
+ Exclude(Flags, Flg.Checked);
+ Include(Flags, Flg.IsIsolated);
for i := 1 to NTerms do
TerminalsChecked[i - 1] := FALSE;
end;
pCktElement := CktElements.Next;
end;
- {Clear some things that will be set by the Meter Zone}
+ // Clear some things that will be set by the Meter Zone
PDElem := PDElements.First;
while PDElem <> NIL do
begin
@@ -903,14 +800,14 @@ procedure TEnergyMeter.ResetMeterZonesAll; // Force all EnergyMeters in the cir
PCElem := PCElements.Next;
end;
- // Set up the bus adjacency lists for faster searches to build meter zone lists.
+ // Set up the bus adjacency lists for faster searches to build meter zone lists.
BuildActiveBusAdjacencyLists(ActiveCircuit, BusAdjPD, BusAdjPC);
- {Set Hasmeter flag for all cktelements}
+ // Set Hasmeter flag for all cktelements
SetHasMeterFlag;
DSS.SensorClass.SetHasSensorFlag; // Set all Sensor branch flags, too.
- // initialize the Checked Flag for all Buses
+ // initialize the Checked Flag for all Buses
for i := 1 to NumBuses do
Buses^[i].BusChecked := FALSE;
@@ -925,31 +822,26 @@ procedure TEnergyMeter.ResetMeterZonesAll; // Force all EnergyMeters in the cir
end;
end;
-{--------------------------------------------------------------------------}
procedure TEnergyMeter.ResetAll; // Force all EnergyMeters in the circuit to reset
-
var
mtr: TEnergyMeterObj;
CasePath: String;
-
begin
-
if DSS.DIFilesAreOpen then
CloseAllDIFiles;
if FSaveDemandInterval then
begin
-
CasePath := DSS.OutputDirectory + DSS.ActiveCircuit.CaseName;
- {Make directories to save data}
-
+
+ //Make directories to save data
if not DirectoryExists(CasePath) then
begin
try
mkDir(CasePath);
except
On E: Exception do
- DoSimpleMsg('Error making Directory: "' + CasePath + '". ' + E.Message, 522);
+ DoSimpleMsg('Error making Directory: "%s". %s', [CasePath, E.Message], 522);
end;
end;
DI_Dir := CasePath + PathDelim + 'DI_yr_' + Trim(IntToStr(ActiveCircuit.Solution.Year));
@@ -959,7 +851,7 @@ procedure TEnergyMeter.ResetAll; // Force all EnergyMeters in the circuit to re
mkDir(DI_Dir);
except
On E: Exception do
- DoSimpleMsg('Error making Demand Interval Directory: "' + DI_Dir + '". ' + E.Message, 523);
+ DoSimpleMsg('Error making Demand Interval Directory: "%s". %s', [DI_Dir, E.Message], 523);
end;
end;
@@ -975,8 +867,7 @@ procedure TEnergyMeter.ResetAll; // Force all EnergyMeters in the circuit to re
SystemMeter.Reset;
-
- // Reset Generator Objects, too
+ // Reset Generator Objects, too
DSS.GeneratorClass.ResetRegistersAll;
if DSS_CAPI_LEGACY_MODELS then
@@ -991,15 +882,11 @@ procedure TEnergyMeter.ResetAll; // Force all EnergyMeters in the circuit to re
end;
end;
-{--------------------------------------------------------------------------}
procedure TEnergyMeter.SampleAll; // Force all EnergyMeters in the circuit to take a sample
-
var
mtr: TEnergyMeterObj;
i: Integer;
-
begin
-
mtr := DSS.ActiveCircuit.EnergyMeters.First;
while mtr <> NIL do
begin
@@ -1011,7 +898,8 @@ procedure TEnergyMeter.SampleAll; // Force all EnergyMeters in the circuit to t
SystemMeter.TakeSample;
if FSaveDemandInterval then
- begin {Write Totals Demand interval file}
+ begin
+ // Write Totals Demand interval file
with DSS.ActiveCircuit.Solution do
WriteintoMem(TDI_MHandle, DynaVars.dblHour);
for i := 1 to NumEMRegisters do
@@ -1024,7 +912,7 @@ procedure TEnergyMeter.SampleAll; // Force all EnergyMeters in the circuit to t
WriteVoltageReport;
end;
- // Sample Generator ans Storage Objects, too
+ // Sample Generator ans Storage Objects, too
DSS.GeneratorClass.SampleAll;
if DSS_CAPI_LEGACY_MODELS then
@@ -1039,12 +927,9 @@ procedure TEnergyMeter.SampleAll; // Force all EnergyMeters in the circuit to t
end;
end;
-{--------------------------------------------------------------------------}
procedure TEnergyMeter.SaveAll; // Force all EnergyMeters in the circuit to take a sample
-
var
mtr: TEnergyMeterObj;
-
begin
mtr := DSS.ActiveCircuit.EnergyMeters.First;
while mtr <> NIL do
@@ -1055,30 +940,21 @@ procedure TEnergyMeter.SaveAll; // Force all EnergyMeters in the circuit to tak
end;
SystemMeter.Save;
-
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TEnergyMeter Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TEnergyMeterObj.Create(ParClass: TDSSClass; const EnergyMeterName: String);
-
var
i: Integer;
-
begin
inherited Create(ParClass);
- Name := LowerCase(EnergyMeterName);
+ Name := AnsiLowerCase(EnergyMeterName);
DSSObjType := ParClass.DSSClassType; //ENERGY_METER;
- NPhases := 3; // Directly set conds and phases
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 1; // this forces allocation of terminals and conductors in base class
ExcessFlag := TRUE; // Default to Excess energy FOR UE
- ElementName := 'Vsource.' + TDSSCktElement(ActiveCircuit.CktElements.Get(1)).Name; // Default to first circuit element (source)
- MeteredElement := NIL;
+ MeteredElement := TDSSCktElement(ActiveCircuit.CktElements.Get(1)); // Default to first circuit element (source)
BranchList := NIL; // initialize to NIL, set later when inited
SequenceList := NIL;
LoadList := NIL;
@@ -1086,8 +962,6 @@ constructor TEnergyMeterObj.Create(ParClass: TDSSClass; const EnergyMeterName: S
This_Meter_DIFileIsOpen := FALSE;
VPhaseReportFileIsOpen := FALSE;
- InitPropertyValues(0);
-
// Max zone kW limits ignored unless the user provides a rating
MaxZonekVA_Norm := 0.0;
MaxZonekVA_Emerg := 0.0;
@@ -1101,12 +975,10 @@ constructor TEnergyMeterObj.Create(ParClass: TDSSClass; const EnergyMeterName: S
Source_NumInterruptions := 0.0; // Annual interruptions for upline circuit
Source_IntDuration := 0.0; // Aver interruption duration of upline circuit
-
ZoneIsRadial := TRUE;
- DefinedZoneList := NIL;
- DefinedZoneListSize := 0;
+ DefinedZoneList := TStringList.Create();
- FLosses := TRUE; {Loss Reporting switches}
+ FLosses := TRUE; // Loss Reporting switches
FLineLosses := TRUE;
FXfmrLosses := TRUE;
FSeqLosses := TRUE;
@@ -1128,13 +1000,13 @@ constructor TEnergyMeterObj.Create(ParClass: TDSSClass; const EnergyMeterName: S
ReallocMem(VBaseNoLoadLosses, MaxVBaseCount * SizeOf(VBaseNoLoadLosses^[1]));
ReallocMem(VBaseLoad, MaxVBaseCount * SizeOf(VBaseLoad^[1]));
-// Init pointers to Nil before allocating
+ // Init pointers to Nil before allocating
VphaseMax := NIL;
VPhaseMin := NIL;
VPhaseAccum := NIL;
VPhaseAccumCount := NIL;
- // Arrays for phase voltage report
+ // Arrays for phase voltage report
ReallocMem(VphaseMax, MaxVBaseCount * 3 * SizeOf(Double));
ReallocMem(VPhaseMin, MaxVBaseCount * 3 * SizeOf(Double));
ReallocMem(VPhaseAccum, MaxVBaseCount * 3 * SizeOf(Double));
@@ -1143,17 +1015,23 @@ constructor TEnergyMeterObj.Create(ParClass: TDSSClass; const EnergyMeterName: S
LocalOnly := FALSE;
VoltageUEOnly := FALSE;
-//*************No append files by default***************************************
+ //*************No append files by default***************************************
+ //TODO:
+ // 1. These seem to be always false
+ // 2. If not always false, check below. Since these vars were globals in the
+ // original codebase, this means that a new meter resets the append status for
+ // all meters.
+
+ DI_Append := FALSE;
+ PHV_Append := FALSE;
with DSS.EnergyMeterClass do
begin
OV_Append := FALSE;
VR_Append := FALSE;
- DI_Append := FALSE;
SDI_Append := FALSE;
TDI_Append := FALSE;
SM_Append := FALSE;
EMT_Append := FALSE;
- PHV_Append := FALSE;
FM_Append := FALSE;
end;
// Set Register names that correspond to the register quantities
@@ -1192,7 +1070,7 @@ constructor TEnergyMeterObj.Create(ParClass: TDSSClass; const EnergyMeterName: S
RegisterNames[30] := 'Gen kvarh';
RegisterNames[31] := 'Gen Max kW';
RegisterNames[32] := 'Gen Max kVA';
- {Registers for capturing losses by base voltage, names assigned later}
+ // Registers for capturing losses by base voltage, names assigned later
for i := Reg_VBaseStart + 1 to NumEMRegisters do
RegisterNames[i] := '';
@@ -1214,7 +1092,6 @@ constructor TEnergyMeterObj.Create(ParClass: TDSSClass; const EnergyMeterName: S
// RecalcElementData;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TEnergyMeterObj.Destroy;
var
i: Integer;
@@ -1249,7 +1126,8 @@ destructor TEnergyMeterObj.Destroy;
SequenceList.Free;
if Assigned(LoadList) then
LoadList.Free;
- FreeStringArray(DefinedZoneList, DefinedZoneListSize);
+
+ DefinedZoneList.Free;
if Assigned(FeederSections) then
Reallocmem(FeederSections, 0);
@@ -1257,42 +1135,37 @@ destructor TEnergyMeterObj.Destroy;
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TEnergyMeterObj.RecalcElementData;
-
-var
- DevIndex: Integer;
-
begin
- Devindex := GetCktElementIndex(ElementName); // Global function
- if DevIndex > 0 then
+ Exclude(Flags, Flg.NeedsRecalc);
+ if MeteredElement <> NIL then
begin // Monitored element must already exist
- MeteredElement := DSS.ActiveCircuit.CktElements.Get(DevIndex); // Get pointer to metered element
- {MeteredElement must be a PDElement}
+ // MeteredElement must be a PDElement
if not (MeteredElement is TPDElement) then
begin
MeteredElement := NIL; // element not found
- DoErrorMsg('EnergyMeter: "' + Self.Name + '"', 'Circuit Element "' + ElementName + '" is not a Power Delivery (PD) element.',
- ' Element must be a PD element.', 525);
+ DoErrorMsg(
+ Format(_('EnergyMeter: "%s"'), [Self.Name]),
+ Format(_('Circuit Element "%s" is not a Power Delivery (PD) element.'), [MeteredElement.Name]),
+ _('Element must be a PD element.'), 525);
Exit;
end;
-
if MeteredTerminal > MeteredElement.Nterms then
begin
- DoErrorMsg('EnergyMeter: "' + Name + '"',
- 'Terminal no. "' + IntToStr(MeteredTerminal) + '" does not exist.',
- 'Respecify terminal no.', 524);
+ DoErrorMsg(
+ Format(_('EnergyMeter: "%s"'), [Self.Name]),
+ Format(_('Terminal no. "%d" does not exist.'), [MeteredTerminal]),
+ _('Respecify terminal no.'), 524);
end
else
begin
-
if MeteredElementChanged then
begin
// Sets name of i-th terminal's connected bus in monitor's buslist
// This value will be used to set the NodeRef array (see TakeSample)
Setbus(1, MeteredElement.GetBus(MeteredTerminal));
- Nphases := MeteredElement.NPhases;
+ FNphases := MeteredElement.NPhases;
Nconds := MeteredElement.Nconds;
AllocateSensorArrays;
@@ -1301,23 +1174,22 @@ procedure TEnergyMeterObj.RecalcElementData;
BranchList.Free;
BranchList := NIL;
end;
-
end;
- end
- else
- begin
- MeteredElement := NIL; // element not found
- DoErrorMsg('EnergyMeter: "' + Self.Name + '"', 'Circuit Element "' + ElementName + '" Not Found.',
- ' Element must be defined previously.', 525);
+ Exit;
end;
+
+ // element not found/set
+ DoErrorMsg(Format(_('EnergyMeter: "%s"'), [Self.Name]),
+ _('Circuit Element not set.'),
+ _('Element must be defined previously.'), 525);
end;
-procedure TEnergyMeterobj.MakePosSequence;
+procedure TEnergyMeterobj.MakePosSequence();
begin
if MeteredElement <> NIL then
begin
Setbus(1, MeteredElement.GetBus(MeteredTerminal));
- Nphases := MeteredElement.NPhases;
+ FNphases := MeteredElement.NPhases;
Nconds := MeteredElement.Nconds;
AllocateSensorArrays;
if BranchList <> NIL then
@@ -1329,21 +1201,18 @@ procedure TEnergyMeterobj.MakePosSequence;
function TEnergyMeterObj.MakeVPhaseReportFileName: String;
begin
- Result := DSS.EnergyMeterClass.DI_Dir + PathDelim + Name + '_PhaseVoltageReport' + DSS._Name + '.CSV';
+ Result := DSS.EnergyMeterClass.DI_Dir + PathDelim + Name + '_PhaseVoltageReport' + DSS._Name + '.csv';
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TEnergyMeterObj.ResetRegisters;
-
var
i: Integer;
-
begin
for i := 1 to NumEMregisters do
Registers[i] := 0.0;
for i := 1 to NumEMregisters do
Derivatives[i] := 0.0;
- {Initialize DragHand registers to some big negative number}
+ // Initialize DragHand registers to some big negative number
Registers[Reg_MaxkW] := -1.0e50;
Registers[Reg_MaxkVA] := -1.0e50;
Registers[Reg_ZoneMaxkW] := -1.0e50;
@@ -1358,39 +1227,30 @@ procedure TEnergyMeterObj.ResetRegisters;
FirstSampleAfterReset := TRUE; // initialize for trapezoidal integration
// Removed .. open in solution loop See Solve Yearly If EnergyMeterClass.SaveDemandInterval Then OpenDemandIntervalFile;
-
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TEnergyMeterObj.CalcYPrim;
-
begin
-
- // YPrim is all zeros. Just leave as NIL so it is ignored.
-
+ // YPrim is all zeros. Just leave as NIL so it is ignored.
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TEnergyMeterObj.SaveRegisters;
-
var
CSVName: String;
F: TFileStream = nil;
i: Integer;
sout: String;
begin
-
try
- CSVName := 'MTR_' + Name + '.CSV';
- F := TFileStream.Create(DSS.OutputDirectory + CSVName, fmCreate);
+ CSVName := 'MTR_' + Name + '.csv';
+ F := TBufferedFileStream.Create(DSS.OutputDirectory + CSVName, fmCreate);
DSS.GlobalResult := CSVName;
SetLastResultFile(DSS, CSVName);
except
On E: Exception do
begin
- DoSimpleMsg('Error opening Meter File "' + CRLF + CSVName + '": ' + E.Message, 526);
+ DoSimpleMsg('Error opening Meter File "%s": %s', [CSVName, E.Message], 526);
FreeAndNil(F);
Exit;
end
@@ -1408,36 +1268,29 @@ procedure TEnergyMeterObj.SaveRegisters;
finally
F.Free();
end;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TEnergyMeterObj.Integrate(Reg: Integer; const Deriv: Double; const Interval: Double);
-
begin
if DSS.ActiveCircuit.TrapezoidalIntegration then
begin
- {Trapezoidal Rule Integration}
+ // Trapezoidal Rule Integration
if not FirstSampleAfterReset then
Registers[Reg] := Registers[Reg] + 0.5 * Interval * (Deriv + Derivatives[Reg]);
end
else
- begin {Plain Euler integration}
+ begin // Plain Euler integration
Registers[Reg] := Registers[Reg] + Interval * Deriv;
end;
-{ Set the derivatives so that the proper value shows up in Demand Interval Files
- and prepare for next time step in Trapezoidal integration }
+ // Set the derivatives so that the proper value shows up in Demand Interval Files
+ // and prepare for next time step in Trapezoidal integration
Derivatives[Reg] := Deriv;
-
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TEnergyMeterObj.TakeSample;
// Update registers from metered zone
// Assumes one time period has taken place since last sample.
-
var
i, j, idx: Integer;
@@ -1486,10 +1339,10 @@ procedure TEnergyMeterObj.TakeSample;
if not CheckBranchList(545) then
Exit;
-// Compute energy in branch to which meter is connected
+ // Compute energy in branch to which meter is connected
//----MeteredElement.ActiveTerminalIdx := MeteredTerminal; // needed for Excess kVA calcs
- S_Local := CmulReal(MeteredElement.Power[MeteredTerminal], 0.001);
+ S_Local := MeteredElement.Power[MeteredTerminal] * 0.001;
S_Local_kVA := Cabs(S_Local);
DSS.EnergyMeterClass.Delta_Hrs := DSS.ActiveCircuit.Solution.IntervalHrs;
Integrate(Reg_kWh, S_Local.re, DSS.EnergyMeterClass.Delta_Hrs); // Accumulate the power
@@ -1497,9 +1350,9 @@ procedure TEnergyMeterObj.TakeSample;
SetDragHandRegister(Reg_MaxkW, S_Local.re); // 3-10-04 removed abs()
SetDragHandRegister(Reg_MaxkVA, S_Local_kVA);
-// Compute Maximum overload energy in all branches in zone
-// and mark all load downline from an overloaded branch as unserved
-// If localonly, check only metered element
+ // Compute Maximum overload energy in all branches in zone
+ // and mark all load downline from an overloaded branch as unserved
+ // If localonly, check only metered element
TotalLosses := CZERO; // Initialize loss accumulators
TotalLoadLosses := CZERO;
@@ -1539,9 +1392,9 @@ procedure TEnergyMeterObj.TakeSample;
MaxExcesskWNorm := 0.0;
MaxExcesskWEmerg := 0.0;
- {--------------------------------------------------------------------------}
- {------------------------ Local Zone Only --------------------------------}
- {--------------------------------------------------------------------------}
+ //--------------------------------------------------------------------------
+ //------------------------ Local Zone Only --------------------------------
+ //--------------------------------------------------------------------------
if LocalOnly then
begin
CktElem := MeteredElement as TPDElement;
@@ -1549,21 +1402,21 @@ procedure TEnergyMeterObj.TakeSample;
MaxExcesskWEmerg := Abs(CktElem.ExcesskVAEmerg[MeteredTerminal].re);
end
else
- {--------------------------------------------------------------------------}
- {--------Cyle Through Entire Zone Setting EEN/UE --------------------------}
- {--------------------------------------------------------------------------}
+ //--------------------------------------------------------------------------
+ //--------Cyle Through Entire Zone Setting EEN/UE --------------------------
+ //--------------------------------------------------------------------------
while CktElem <> NIL do
begin // loop thru all ckt elements on zone
with CktElem do
begin
ActiveTerminalIdx := BranchList.Presentbranch.FromTerminal;
- // Invoking this property sets the Overload_UE flag in the PD Element
+ // Invoking this property sets the Overload_UE flag in the PD Element
EEN := Abs(ExcesskVANorm[ActiveTerminalIdx].re);
UE := Abs(ExcesskVAEmerg[ActiveTerminalIdx].re);
end;
- {For radial circuits just keep the maximum overload; for mesh, add 'em up}
+ // For radial circuits just keep the maximum overload; for mesh, add 'em up
if (ZoneIsRadial) then
begin
if UE > MaxExcesskWEmerg then
@@ -1577,9 +1430,9 @@ procedure TEnergyMeterObj.TakeSample;
MaxExcesskWNorm := MaxExcesskWNorm + EEN;
end;
- // Even if this branch is not overloaded, if the parent element is overloaded
- // mark load on this branch as unserved also
- // Use the larger of the two factors
+ // Even if this branch is not overloaded, if the parent element is overloaded
+ // mark load on this branch as unserved also
+ // Use the larger of the two factors
ParenElem := BranchList.Parent;
if (ParenElem <> NIL) then
begin
@@ -1587,9 +1440,9 @@ procedure TEnergyMeterObj.TakeSample;
CktElem.OverLoad_UE := Max(CktElem.OverLoad_UE, ParenElem.OverLoad_UE);
end;
- // Mark loads (not generators) by the degree of overload if the meter's zone is to be considered radial
- // This overrides and supercedes the load's own determination of unserved based on voltage
- // If voltage only is to be used for Load UE/EEN, don't mark (set to 0.0 and load will calc UE based on voltage)
+ // Mark loads (not generators) by the degree of overload if the meter's zone is to be considered radial
+ // This overrides and supercedes the load's own determination of unserved based on voltage
+ // If voltage only is to be used for Load UE/EEN, don't mark (set to 0.0 and load will calc UE based on voltage)
PCElem := Branchlist.FirstObject;
while (PCElem <> NIL) do
begin
@@ -1613,7 +1466,7 @@ procedure TEnergyMeterObj.TakeSample;
end;
- // Get the Losses, and unserved bus energies
+ // Get the Losses, and unserved bus energies
TotalZonekw := 0.0;
TotalZonekvar := 0.0;
TotalLoad_EEN := 0.0;
@@ -1622,9 +1475,9 @@ procedure TEnergyMeterObj.TakeSample;
TotalGenkvar := 0.0;
- {--------------------------------------------------------------------------}
- {-------- Cycle Through Zone Accumulating Load and Losses --------}
- {--------------------------------------------------------------------------}
+ //--------------------------------------------------------------------------
+ //-------- Cycle Through Zone Accumulating Load and Losses --------
+ //--------------------------------------------------------------------------
CktElem := BranchList.First;
while (CktElem <> NIL) do
begin
@@ -1648,7 +1501,7 @@ procedure TEnergyMeterObj.TakeSample;
Accumulate_Gen(pGen, TotalGenkW, TotalGenkvar);
end;
else
- {Ignore other types of PC Elements}
+ //Ignore other types of PC Elements
end;
PCElem := BranchList.NextObject
end;
@@ -1656,43 +1509,43 @@ procedure TEnergyMeterObj.TakeSample;
if Flosses then
begin // Compute and Report Losses
- {Get losses from the present circuit element}
+ // Get losses from the present circuit element
CktElem.GetLosses(S_TotalLosses, S_LoadLosses, S_NoLoadLosses); // returns watts, vars
- {Convert to kW}
- CmulRealAccum(S_TotalLosses, 0.001);
- CmulRealAccum(S_LoadLosses, 0.001);
- CmulRealAccum(S_NoLoadLosses, 0.001);
- {Update accumulators}
- Caccum(TotalLosses, S_TotalLosses); // Accumulate total losses in meter zone
- Caccum(TotalLoadLosses, S_LoadLosses); // Accumulate total load losses in meter zone
- Caccum(TotalNoLoadLosses, S_NoLoadLosses); // Accumulate total no load losses in meter zone
-
- {Line and Transformer Elements}
+ // Convert to kW
+ S_TotalLosses *= 0.001;
+ S_LoadLosses *= 0.001;
+ S_NoLoadLosses *= 0.001;
+ // Update accumulators
+ TotalLosses += S_TotalLosses; // Accumulate total losses in meter zone
+ TotalLoadLosses += S_LoadLosses; // Accumulate total load losses in meter zone
+ TotalNoLoadLosses += S_NoLoadLosses; // Accumulate total no load losses in meter zone
+
+ // Line and Transformer Elements
if IsLineElement(Cktelem) and FLineLosses then
begin
- Caccum(TotalLineLosses, S_TotalLosses); // Accumulate total losses in meter zone
+ TotalLineLosses += S_TotalLosses; // Accumulate total losses in meter zone
if FseqLosses then
begin
CktElem.GetSeqLosses(S_PosSeqLosses, S_NegSeqLosses, S_ZeroSeqLosses);
- Caccum(S_PosSeqLosses, S_NegSeqLosses); // add line modes together
- CmulRealAccum(S_PosSeqLosses, 0.001); // convert to kW
- CmulRealAccum(S_ZeroSeqLosses, 0.001);
- Caccum(TotalLineModeLosses, S_PosSeqLosses);
- Caccum(TotalZeroModeLosses, S_ZeroSeqLosses);
+ S_PosSeqLosses += S_NegSeqLosses; // add line modes together
+ S_PosSeqLosses *= 0.001; // convert to kW
+ S_ZeroSeqLosses *= 0.001;
+ TotalLineModeLosses += S_PosSeqLosses;
+ TotalZeroModeLosses += S_ZeroSeqLosses;
end;
- {Separate Line losses into 3- and "1-phase" losses}
+ // Separate Line losses into 3- and "1-phase" losses
if F3PhaseLosses then
begin
if Cktelem.NPhases = 3 then
- Caccum(Total3phaseLosses, S_TotalLosses)
+ Total3phaseLosses += S_TotalLosses
else
- Caccum(Total1phaseLosses, S_TotalLosses);
+ Total1phaseLosses += S_TotalLosses;
end;
end
else
if IsTransformerElement(Cktelem) and FXfmrLosses then
begin
- Caccum(TotalTransformerLosses, S_TotalLosses); // Accumulate total losses in meter zone
+ TotalTransformerLosses += S_TotalLosses; // Accumulate total losses in meter zone
end;
if FVbaseLosses then
@@ -1710,7 +1563,7 @@ procedure TEnergyMeterObj.TakeSample;
end;
end;
- // Compute min, max, and average pu voltages for 1st 3 phases (nodes designated 1, 2, or 3)
+ // Compute min, max, and average pu voltages for 1st 3 phases (nodes designated 1, 2, or 3)
if FPhaseVoltageReport then
with BranchList.PresentBranch do
if VoltBaseIndex > 0 then
@@ -1722,18 +1575,18 @@ procedure TEnergyMeterObj.TakeSample;
j := Buses^[FromBusReference].GetNum(i);
if (j > 0) and (j < 4) then
begin
- puV := Cabs(Solution.NodeV^[Buses^[FromBusReference].GetRef(i)]) / Buses^[FromBusReference].kVBase;
+ puV := Cabs(Solution.NodeV^[Buses^[FromBusReference].RefNo[i]]) / Buses^[FromBusReference].kVBase;
idx := jiIndex(j, VoltBaseIndex);
if puV > VphaseMax^[idx] then
begin
VphaseMax^[jiIndex(j, VoltBaseIndex)] := puV;
- // VmaxBus := FromBusReference;
+ // VmaxBus := FromBusReference;
end;
if puV < VphaseMin^[idx] then
begin
VphaseMin^[jiIndex(j, VoltBaseIndex)] := puV;
- // VminBus := FromBusReference;
+ // VminBus := FromBusReference;
end;
DblInc(VphaseAccum^[jiIndex(j, VoltBaseIndex)], puV);
@@ -1741,18 +1594,18 @@ procedure TEnergyMeterObj.TakeSample;
end;
end;
end;
- end; {If FLosses}
+ end; // If FLosses
CktElem := BranchList.GoForward;
end;
Delta_hrs_local := DSS.EnergyMeterClass.Delta_Hrs;
- {NOTE: Integrate proc automatically sets derivatives array}
+ // NOTE: Integrate proc automatically sets derivatives array
Integrate(Reg_LoadEEN, TotalLoad_EEN, Delta_hrs_local);
Integrate(Reg_LoadUE, TotalLoad_UE, Delta_hrs_local);
- {Accumulate losses in appropriate registers}
+ // Accumulate losses in appropriate registers
Integrate(Reg_ZoneLosseskWh, TotalLosses.re, Delta_hrs_local);
Integrate(Reg_ZoneLosseskvarh, TotalLosses.im, Delta_hrs_local);
Integrate(Reg_LoadLosseskWh, TotalLoadLosses.re, Delta_hrs_local);
@@ -1775,9 +1628,9 @@ procedure TEnergyMeterObj.TakeSample;
end;
- {--------------------------------------------------------------------------}
- {--------------- Total Zone Load and Generation -------------------------}
- {--------------------------------------------------------------------------}
+ //--------------------------------------------------------------------------
+ //--------------- Total Zone Load and Generation -------------------------
+ //--------------------------------------------------------------------------
Integrate(Reg_ZonekWh, TotalZonekW, Delta_hrs_local);
Integrate(Reg_Zonekvarh, TotalZonekvar, Delta_hrs_local);
@@ -1786,9 +1639,9 @@ procedure TEnergyMeterObj.TakeSample;
GenkVA := Sqrt(Sqr(TotalGenkvar) + Sqr(TotalGenkW));
LoadkVA := Sqrt(Sqr(TotalZonekvar) + Sqr(TotalZonekW));
- {--------------------------------------------------------------------------}
- {--------------- Set Drag Hand Registers ------------------------------}
- {--------------------------------------------------------------------------}
+ //--------------------------------------------------------------------------
+ //--------------- Set Drag Hand Registers -------------------------------
+ //--------------------------------------------------------------------------
SetDragHandRegister(Reg_LossesMaxkW, Abs(TotalLosses.Re));
SetDragHandRegister(Reg_LossesMaxkvar, Abs(TotalLosses.im));
@@ -1796,23 +1649,23 @@ procedure TEnergyMeterObj.TakeSample;
SetDragHandRegister(Reg_MaxNoLoadLosses, Abs(TotalNoLoadLosses.Re));
SetDragHandRegister(Reg_ZoneMaxkW, TotalZonekW); // Removed abs() 3-10-04
SetDragHandRegister(Reg_ZoneMaxkVA, LoadkVA);
- {Max total generator registers}
+ // Max total generator registers
SetDragHandRegister(Reg_GenMaxkW, TotalGenkW); // Removed abs() 3-10-04
SetDragHandRegister(Reg_GenMaxkVA, GenkVA);
- {--------------------------------------------------------------------------}
- {--------------------- Overload Energy ---------------------------------}
- {--------------------------------------------------------------------------}
- {Overload energy for the entire zone}
+ //--------------------------------------------------------------------------
+ //--------------------- Overload Energy ---------------------------------
+ //--------------------------------------------------------------------------
+ // Overload energy for the entire zone
if LocalOnly then
ZonekW := S_Local.Re
else
ZonekW := TotalZonekW;
- {Either the max excess kW of any PD element or the excess over zone limits}
+ // Either the max excess kW of any PD element or the excess over zone limits
- {regs 9 and 10}
- {Fixed these formulas 2-7-07 per discussions with Daniel Brooks }
+ // regs 9 and 10
+ // Fixed these formulas 2-7-07 per discussions with Daniel Brooks
if (MaxZonekVA_Norm > 0.0) then
begin
if (S_Local_KVA = 0.0) then
@@ -1840,113 +1693,98 @@ procedure TEnergyMeterObj.TakeSample;
WriteDemandIntervalData;
end;
-{---------------------------------------------------------------------------------}
-
procedure TEnergyMeterObj.TotalUpDownstreamCustomers;
var
i: Integer;
- {, Accumulator}
- // PresentNode: TCktTreeNode;
+ //, Accumulator
+ // PresentNode: TCktTreeNode;
CktElem: TPDElement;
-
begin
if not CheckBranchList(529) then
Exit;
- {Init totsls and checked flag}
+ // Init totals and checked flag
CktElem := SequenceList.First;
while CktElem <> NIL do
begin
- CktElem.Checked := FALSE;
+ Exclude(CktElem.Flags, Flg.Checked);
CktElem.BranchTotalCustomers := 0;
CktElem := SequenceList.Next;
end;
- {This algorithm could be made more efficient with a Sequence list}
- (*********
- For i := 1 to Branchlist.ZoneEndsList.NumEnds Do
- Begin
- {Busref := } Branchlist.ZoneEndsList.Get(i, PresentNode);
- If PresentNode <> Nil Then
- Begin
- CktElem := PresentNode.CktObject;
- if Not CktElem.Checked then // don't do a zone end element more than once
- Begin
- CktElem.Checked := TRUE;
- Accumulator := CktElem.NumCustomers;
- Repeat {Trace back to the source}
-
- Inc(CktElem.TotalCustomers, Accumulator);
- PresentNode := PresentNode.ParentBranch;
- If PresentNode=Nil Then Break;
- CktElem := PresentNode.CktObject;
- If not CktElem.Checked Then Begin // avoid double counting
- Inc(Accumulator, CktElem.NumCustomers);
- CktElem.Checked := TRUE;
- End;
-
- Until FALSE;
- End;
- End;
- End; {For}
- *******)
-
- // Backward Sweep - Order is guaranteed to process end branches first
- // sum numcustomers branch by branch
+ // This algorithm could be made more efficient with a Sequence list
+
+ // For i := 1 to Branchlist.ZoneEndsList.NumEnds Do
+ // Begin
+ // {Busref := } Branchlist.ZoneEndsList.Get(i, PresentNode);
+ // If PresentNode <> Nil Then
+ // Begin
+ // CktElem := PresentNode.CktObject;
+ // if Not CktElem.Checked then // don't do a zone end element more than once
+ // Begin
+ // CktElem.Checked := TRUE;
+ // Accumulator := CktElem.NumCustomers;
+ // Repeat // Trace back to the source
+ // Inc(CktElem.TotalCustomers, Accumulator);
+ // PresentNode := PresentNode.ParentBranch;
+ // If PresentNode=Nil Then Break;
+ // CktElem := PresentNode.CktObject;
+ // If not CktElem.Checked Then Begin // avoid double counting
+ // Inc(Accumulator, CktElem.NumCustomers);
+ // CktElem.Checked := TRUE;
+ // End;
+ // Until FALSE;
+ // End;
+ // End;
+ // End;
+
+ // Backward Sweep - Order is guaranteed to process end branches first
+ // sum numcustomers branch by branch
for i := SequenceList.Count downto 1 do
begin
CktElem := SequenceList.Get(i);
- if not CktElem.Checked then // Avoid double counting
+ if not (Flg.Checked in CktElem.Flags) then // Avoid double counting
with CktElem do
begin
- Checked := TRUE;
+ Include(Flags, Flg.Checked);
Inc(BranchTotalCustomers, BranchNumCustomers);
if ParentPDElement <> NIL then
Inc(ParentPDElement.BranchTotalCustomers, BranchTotalCustomers);
end;
- end; {For i}
-
+ end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TEnergyMeter.SetHasMeterFlag;
// Set the HasMeter Flag for all cktElement;
var
i: Integer;
ThisMeter: TEnergyMeterObj;
CktElem: TDSSCktElement;
-
begin
- {Initialize all to FALSE}
+ // Initialize all to FALSE
with ActiveCircuit do
begin
CktElem := PDElements.First;
while CktElem <> NIL do
begin
- CktElem.HasEnergyMeter := FALSE;
+ Exclude(CktElem.Flags, Flg.HasEnergyMeter);
CktElem := PDElements.Next;
- end; {WHILE}
- end; {WITH}
+ end;
+ end;
for i := 1 to DSS.ActiveCircuit.EnergyMeters.Count do
begin
ThisMeter := DSS.ActiveCircuit.EnergyMeters.Get(i);
with ThisMeter do
if Enabled and (MeteredElement <> NIL) then
- MeteredElement.HasEnergyMeter := TRUE;
- end; {FOR}
+ Include(MeteredElement.Flags, Flg.HasEnergyMeter);
+ end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TEnergyMeterObj.MakeMeterZoneLists;
-
// This gets fired off whenever the buslists are rebuilt
// Must be updated whenever there is a change in the circuit
-
var
-
TestBusNum,
ZoneListCounter: Integer;
j, iTerm,
@@ -1961,37 +1799,36 @@ procedure TEnergyMeterObj.MakeMeterZoneLists;
BusAdjPC: TAdjArray;
BusAdjPD: TAdjArray;
begin
-
ZoneListCounter := 0;
- VBasecount := 0; {Build the voltage base list over in case a base added or deleted}
+ VBasecount := 0; // Build the voltage base list over in case a base added or deleted
for j := 1 to MaxVBaseCount do
VBaseList^[j] := 0.0;
- // Make a new branch list
+ // Make a new branch list
if BranchList <> NIL then
BranchList.Free;
if Enabled then
- BranchList := TCktTree.Create {Instantiates ZoneEndsList, too}
+ BranchList := TCktTree.Create // Instantiates ZoneEndsList, too
else
begin
BranchList := NIL;
Exit;
end;
- // Get Started
+ // Get Started
if Assigned(MeteredElement) then
- BranchList.New := MeteredElement
+ BranchList.Add(MeteredElement)
else
begin // oops
- DoSimpleMsg('Metered Element for EnergyMeter ' + Name + ' not defined.', 527);
+ DoSimpleMsg('Metered Element for EnergyMeter %s not defined.', [Name], 527);
Exit;
end;
- {Initialize SensorObj property of the first branch to this TMeterElement Object.
- Before starting, all sensorObj definitions are cleared in PCElements and PDElements. The
- SensorObj property is passed down to the Load objects for LoadAllocation and State Estimation
- }
+ // Initialize SensorObj property of the first branch to this TMeterElement Object.
+ // Before starting, all sensorObj definitions are cleared in PCElements and PDElements. The
+ // SensorObj property is passed down to the Load objects for LoadAllocation and State Estimation
+
if MeteredElement is TPDElement then
with TPDElement(MeteredElement) do
begin
@@ -2010,7 +1847,7 @@ procedure TEnergyMeterObj.MakeMeterZoneLists;
MeteredElement.TerminalsChecked[MeteredTerminal - 1] := TRUE;
with BranchList.PresentBranch do
begin
- // This bus is the head of the feeder or zone; do not mark as radial bus
+ // This bus is the head of the feeder or zone; do not mark as radial bus
FromBusReference := MeteredElement.Terminals[MeteredTerminal - 1].BusRef;
DSS.ActiveCircuit.Buses^[FromBusReference].DistFromMeter := 0.0;
VoltBaseIndex := AddToVoltBaseList(FromBusReference);
@@ -2019,15 +1856,15 @@ procedure TEnergyMeterObj.MakeMeterZoneLists;
TPDElement(MeteredElement).FromTerminal := MeteredTerminal;
end;
- // Check off this element so we don't use it again
+ // Check off this element so we don't use it again
with MeteredElement do
begin
- Checked := TRUE;
- IsIsolated := FALSE;
+ Include(Flags, Flg.Checked);
+ Exclude(Flags, Flg.IsIsolated);
end;
- // Make SequenceList for use in reliability calcs or anything that
- // needs to run through the tree quickly in a radial sequence
+ // Make SequenceList for use in reliability calcs or anything that
+ // needs to run through the tree quickly in a radial sequence
if Assigned(SequenceList) then
SequenceList.Free;
SequenceList := TDSSPointerList.Create(1024); //make it a big initial allocation
@@ -2035,17 +1872,16 @@ procedure TEnergyMeterObj.MakeMeterZoneLists;
LoadList.Free;
LoadList := TDSSPointerList.Create(1024); //make it a big initial allocation
- // Now start looking for other branches
- // Finds any branch connected to the TestBranch and adds it to the list
- // Goes until end of circuit, another energy meter, an open terminal, or disabled device.
+ // Now start looking for other branches
+ // Finds any branch connected to the TestBranch and adds it to the list
+ // Goes until end of circuit, another energy meter, an open terminal, or disabled device.
ActiveBranch := MeteredElement;
- { **************** MAIN LOOP *****************************}
+ // **************** MAIN LOOP *****************************
BusAdjPC := DSS.EnergyMeterClass.BusAdjPC;
BusAdjPD := DSS.EnergyMeterClass.BusAdjPD;
while ActiveBranch <> NIL do
begin
-
Sequencelist.Add(ActiveBranch); // When done, this should be the correct order.
with BranchList.PresentBranch do
@@ -2063,8 +1899,8 @@ procedure TEnergyMeterObj.MakeMeterZoneLists;
if not ActiveBranch.TerminalsChecked[iTerm - 1] then
with ActiveCircuit do
begin
- // Now find all loads and generators connected to the bus on this end of branch
- // attach them as generic objects to cktTree node.
+ // Now find all loads and generators connected to the bus on this end of branch
+ // attach them as generic objects to cktTree node.
TestBusNum := ActiveBranch.Terminals[iTerm - 1].BusRef;
with BranchList.PresentBranch do
begin
@@ -2080,63 +1916,63 @@ procedure TEnergyMeterObj.MakeMeterZoneLists;
for iPC := 0 to adjLst.Count - 1 do
begin
pPCelem := adjLst[iPC];
- // IF pPCelem.Enabled Then Begin only enabled elements in the search list
- if not pPCelem.Checked then
+ // IF pPCelem.Enabled Then Begin only enabled elements in the search list
+ if not (Flg.Checked in pPCelem.Flags) then
begin
; // skip ones we already checked
BranchList.PresentBranch.IsDangling := FALSE; // Something is connected here
- // Is this a load or a generator or a Capacitor or reactor??
+ // Is this a load or a generator or a Capacitor or reactor??
PCElementType := (pPCelem.DSSObjType and CLASSMASK);
if (PCElementType = LOAD_ELEMENT) or (PCElementType = GEN_ELEMENT) or (PCElementType = PVSYSTEM_ELEMENT) or (PCElementType = STORAGE_ELEMENT) or (PCElementType = CAP_ELEMENT) // Capacitor and Reactor put on the PC list if IsShunt=TRUE
or (PCElementType = REACTOR_ELEMENT) then
begin
- BranchList.NewObject := pPCelem; // This adds element to the Shunt list in CktTree
- pPCelem.Checked := TRUE; // So we don't pick this element up again
- pPCelem.IsIsolated := FALSE;
+ BranchList.AddNewObject(pPCelem); // This adds element to the Shunt list in CktTree
+ Include(pPCelem.Flags, Flg.Checked); // So we don't pick this element up again
+ Exclude(pPCelem.Flags, Flg.IsIsolated);
pPCelem.ActiveTerminalIdx := 1;
- {Totalize Number of Customers if Load Type}
+ // Totalize Number of Customers if Load Type
if (pPCelem is TLoadObj) then
begin
pLoad := pPCelem as TLoadObj;
Inc(TPDElement(ActiveBranch).BranchNumCustomers, pLoad.NumCustomers);
LoadList.Add(pPCElem); // Add to list of loads in this zone.)
end;
- {If object does not have a sensor attached, it acquires the sensor of its parent branch}
- if not pPCelem.HasSensorObj then
+ // If object does not have a sensor attached, it acquires the sensor of its parent branch
+ if not (Flg.HasSensorObj in pPCelem.Flags) then
pPCelem.SensorObj := TPDElement(ActiveBranch).SensorObj;
pPCelem.MeterObj := Self;
- end; {IF}
+ end;
end;
end;
- // Now find all branches connected to this bus that we haven't found already
- // Do not include in this zone if branch has open terminals or has another meter
+ // Now find all branches connected to this bus that we haven't found already
+ // Do not include in this zone if branch has open terminals or has another meter
- if DefinedZoneListSize = 0 then
+ if DefinedZoneList.Count = 0 then
begin // Search tree for connected branches (default)
IsFeederEnd := TRUE;
adjLst := BusAdjPD[TestBusNum];
for iPD := 0 to adjLst.Count - 1 do
begin
TestElement := adjLst[iPD]; // Only enabled objects are in this list
- // **** See ResetMeterZonesAll
+ // **** See ResetMeterZonesAll
if not (TestElement = ActiveBranch) then // Skip self
- if not TestElement.HasEnergyMeter then
+ if not (Flg.HasEnergyMeter in TestElement.Flags) then
begin // Stop at other meters so zones don't interfere
for j := 1 to TestElement.Nterms do
begin // Check each terminal
if TestBusNum = TestElement.Terminals[j - 1].BusRef then
begin
BranchList.PresentBranch.IsDangling := FALSE; // We found something it was connected to
- {Check for loops and parallel branches and mark them}
- if (TestElement.Checked) then {This branch is on some meter's list already }
+ // Check for loops and parallel branches and mark them
+ if (Flg.Checked in TestElement.Flags) then // This branch is on some meter's list already
with BranchList.PresentBranch do
begin
- IsLoopedHere := TRUE; {It's a loop}
+ IsLoopedHere := TRUE; // It's a loop
LoopLineObj := TestElement;
if IsLineElement(ActiveBranch) and IsLineElement(TestElement) then
if CheckParallel(ActiveBranch, TestElement) then
- IsParallel := TRUE; {It's paralleled with another line}
+ IsParallel := TRUE; // It's paralleled with another line
end
else
begin // push TestElement onto stack and set properties
@@ -2146,31 +1982,31 @@ procedure TEnergyMeterObj.MakeMeterZoneLists;
begin
TerminalsChecked[j - 1] := TRUE;
FromTerminal := j;
- Checked := TRUE;
- IsIsolated := FALSE;
- {Branch inherits sensor of upline branch if it doesn't have its own}
- if not HasSensorObj then
+ Include(Flags, Flg.Checked);
+ Exclude(Flags, Flg.IsIsolated);
+ // Branch inherits sensor of upline branch if it doesn't have its own
+ if not (Flg.HasSensorObj in Flags) then
SensorObj := TPDElement(ActiveBranch).SensorObj;
MeterObj := Self; // Set meterobj to this meter
ParentPDElement := TPDElement(ActiveBranch); // record the parent so we can easily back up for reconductoring, etc.
end;
Break;
- end; {Else}
- end; {IF TestBusNum}
- end; {FOR terminals}
- end; {ELSE}
- end; {FOR iPD}
+ end;
+ end; // IF TestBusNum
+ end; // FOR terminals
+ end; // ELSE
+ end; // FOR iPD
if IsFeederEnd then
BranchList.ZoneEndsList.Add(BranchList.PresentBranch, TestBusNum);
- {This is an end of the feeder and testbusnum is the end bus}
+ // This is an end of the feeder and testbusnum is the end bus
end
else
begin // Zone is manually specified; Just add next element in list as a child
Inc(ZoneListCounter);
- while ZoneListCounter <= DefinedZoneListSize do
+ while ZoneListCounter <= DefinedZoneList.Count do
begin
- if SetElementActive(DefinedZoneList^[ZoneListCounter]) = 0 then
+ if SetElementActive(DefinedZoneList[ZoneListCounter - 1]) = 0 then
Inc(ZoneListCounter) // Not Found. Let's search for another
else
begin
@@ -2188,11 +2024,11 @@ procedure TEnergyMeterObj.MakeMeterZoneLists;
end;
end; // while
end;
- end; {WITH Active Circuit}
- end; {FOR iTerm}
+ end; // WITH Active Circuit
+ end; // FOR iTerm
ActiveBranch := BranchList.GoForward; // Sets PresentBranch
- { **************** END MAIN LOOP *****************************}
+ // **************** END MAIN LOOP *****************************
end;
TotalupDownstreamCustomers;
@@ -2200,46 +2036,35 @@ procedure TEnergyMeterObj.MakeMeterZoneLists;
AssignVoltBaseRegisterNames;
end;
-{--------------------------------------------------------------------------}
procedure TEnergyMeterObj.GetCurrents(Curr: pComplexArray); //Get present value of terminal Curr FOR reports
-
var
i: Integer;
-
begin
for i := 1 to Fnconds do
Curr^[i] := CZERO;
end;
-{--------------------------------------------------------------------------}
-
procedure TEnergyMeterObj.ZoneDump;
-
var
CSVName: String;
F: TFileStream = nil;
pdelem: TPDelement;
LoadElem: TDSSCktElement;
-
begin
-
try
-
- CSVName := 'Zone_' + Name + '.CSV';
- F := TFileStream.Create(DSS.OutputDirectory + CSVName, fmCreate);
+ CSVName := 'Zone_' + Name + '.csv';
+ F := TBufferedFileStream.Create(DSS.OutputDirectory + CSVName, fmCreate);
DSS.GlobalResult := CSVName;
SetLastResultFile(DSS, CSVName);
-
except
On E: Exception do
begin
- DoSimpleMsg('Error opening File "' + CSVName + '": ' + E.Message, 528);
+ DoSimpleMsg('Error opening File "%s": %s', [CSVName, E.Message], 528);
FreeAndNil(F);
Exit;
end;
-
end;
try
@@ -2253,7 +2078,7 @@ procedure TEnergyMeterObj.ZoneDump;
FSWriteln(F, Format('%d, %s.%s, %s, %s, %10.4f',
[BranchList.Level, PDelem.ParentClass.Name, PDelem.Name,
PDelem.FirstBus, PDelem.NextBus,
- {BusList.NameOfIndex(BranchList.PresentBranch.ToBusReference),}
+ // BusList.NameOfIndex(BranchList.PresentBranch.ToBusReference),
Buses^[BranchList.PresentBranch.ToBusReference].DistFromMeter]));
BranchList.PresentBranch.ResetToBusList;
LoadElem := Branchlist.FirstObject;
@@ -2272,48 +2097,20 @@ procedure TEnergyMeterObj.ZoneDump;
end;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TEnergyMeterObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
+procedure TEnergyMeterObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i: Integer;
pdelem: TPDelement;
LoadElem: TDSSCktElement;
-
-
begin
inherited DumpProperties(F, complete);
with ParentClass do
for i := 1 to NumProperties do
- case i of
- 4:
- begin // option
- FSWrite(F, '~ ', PropertyName^[i], '=(');
- if ExcessFlag then
- FSWrite(F, 'E,')
- else
- FSWrite(F, 'T,');
- if ZoneIsRadial then
- FSWrite(F, ' R,')
- else
- FSWrite(F, ' M,');
- if VoltageUEOnly then
- FSWrite(F, ' V')
- else
- FSWrite(F, ' C');
- FSWriteln(F, ')');
- end;
- 7:
- FSWriteln(F, '~ ' + PropertyName^[i] + '=(' + PropertyValue[i] + ')');
- else
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
+ FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
if complete then
begin
-
FSWriteln(F, 'Registers');
for i := 1 to NumEMregisters do
begin
@@ -2331,54 +2128,19 @@ procedure TEnergyMeterObj.DumpProperties(F: TFileStream; Complete: Boolean);
LoadElem := Branchlist.FirstObject;
while LoadElem <> NIL do
begin
- FSWriteln(F, ' Shunt Element = ' + LoadElem.ParentClass.name + '.' + LoadElem.Name);
+ FSWriteln(F, ' Shunt Element = ' + LoadElem.FullName);
LoadElem := BranchList.NextObject
end;
PDElem := BranchList.GoForward;
end;
end;
-
end;
-
-end;
-
-procedure TEnergyMeter.ProcessOptions(const Opts: String);
-
-var
- {S1,} S2: String;
-begin
-
- AuxParser.CmdString := Opts; // Load up aux Parser
-
- {Loop until no more options found}
- with DSS.ActiveEnergymeterObj do
- repeat
- {S1 := }AuxParser.NextParam; // ignore any parameter name not expecting any
- S2 := lowercase(AuxParser.StrValue);
- if Length(S2) > 0 then
- case s2[1] of
- 'e':
- ExcessFlag := TRUE;
- 't':
- ExcessFlag := FALSE;
- 'r':
- ZoneIsRadial := TRUE;
- 'm':
- ZoneIsRadial := FALSE;
- 'c':
- VoltageUEOnly := FALSE;
- 'v':
- VoltageUEOnly := TRUE;
- end;
- until Length(S2) = 0;
-
end;
function TEnergyMeterObj.AddToVoltBaseList(BusRef: Integer): Integer;
-{Add to VoltBase list if not already there and return index}
+// Add to VoltBase list if not already there and return index
var
i: Integer;
-
begin
with DSS.ActiveCircuit.Buses^[BusRef] do
begin
@@ -2400,37 +2162,30 @@ function TEnergyMeterObj.AddToVoltBaseList(BusRef: Integer): Integer;
else
Result := 0;
end;
-
end;
procedure TEnergyMeterObj.AllocateLoad;
-
var
ConnectedPhase: Integer;
CktElem: TPDElement;
LoadElem: TLoadobj;
-
begin
+ // PREREQUISITE: EXECUTE CALCALLOCATIONFACTORS FOR ALL ENERGYMETERS AND SENSORS
+ // ****Done in calling procedure now *** CalcAllocationFactors; for this meter. Inherited from Meterelement
+ // See ExecHelper
+ // Now go through the meter's zone and adjust the loads.
-{PREREQUISITE: EXECUTE CALCALLOCATIONFACTORS FOR ALL ENERGYMETERS AND SENSORS}
-{****Done in calling procedure now *** CalcAllocationFactors;} {for this meter. Inherited from Meterelement}
-{See ExecHelper}
+ // While the AllocationFactor property is adjusted for all loads, it will only
+ // have an effect on loads defined with either the XFKVA property or the
+ // kWh property.
- { Now go through the meter's zone and adjust the loads.
-
- While the AllocationFactor property is adjusted for all loads, it will only
- have an effect on loads defined with either the XFKVA property or the
- kWh property.
-
- Loads have a SensorObj property that points to its upstream sensor that has the adjustments for
- the allocation factors. This is established in the MakeMeterZoneLists proc in this Unit.
-
- Sensors consist of EnergyMeters, which drive the load allocation process and Sensor objects that
- are simply voltage and current measuring points. A Sensor may be attached to a line or transformer
- or it may be connected directly to a load.
- }
+ // Loads have a SensorObj property that points to its upstream sensor that has the adjustments for
+ // the allocation factors. This is established in the MakeMeterZoneLists proc in this Unit.
+ // Sensors consist of EnergyMeters, which drive the load allocation process and Sensor objects that
+ // are simply voltage and current measuring points. A Sensor may be attached to a line or transformer
+ // or it may be connected directly to a load.
CktElem := BranchList.First;
while CktElem <> NIL do
@@ -2440,7 +2195,7 @@ procedure TEnergyMeterObj.AllocateLoad;
begin
if (LoadElem.DSSObjType and CLASSMASK) = LOAD_ELEMENT then // only for loads not other shunts
case LoadElem.NPhases of
- {For Single phase loads, allocate based on phase factor, else average factor}
+ // For Single phase loads, allocate based on phase factor, else average factor
1:
with LoadElem do
begin
@@ -2455,52 +2210,11 @@ procedure TEnergyMeterObj.AllocateLoad;
else
with LoadElem do
AllocationFactor := AllocationFactor * SensorObj.AvgAllocFactor;
- end; {CASE}
- LoadElem := BranchList.NextObject {Next load at this bus}
- end; {While Loadelem}
- CktElem := BranchList.GoForward; {Go on down the tree}
- end; {While CktElem}
-
-end;
-
-procedure TEnergyMeterObj.InitPropertyValues(ArrayOffset: Integer);
-var
- i: Integer;
- S: String;
-begin
-
- PropertyValue[1] := ''; //'element';
- PropertyValue[2] := '1'; //'terminal';
- PropertyValue[3] := 'clear'; //'action';
- PropertyValue[4] := '(E, R, C)'; //'Option';
- PropertyValue[5] := '0.0'; //'kWnormal';
- PropertyValue[6] := '0.0'; //'kwEmerg';
- PropertyValue[7] := '(400, 400, 400)'; //'PeakCurrent';
- PropertyValue[8] := ''; // ZoneList
- PropertyValue[9] := 'No';
- {Define mask as 1 for all registers}
- S := '[';
- for i := 1 to NumEMregisters do
- S := S + '1 ';
- PropertyValue[10] := S + ']';
- PropertyValue[11] := 'Yes';
- PropertyValue[12] := 'Yes';
- PropertyValue[13] := 'Yes';
- PropertyValue[14] := 'Yes';
- PropertyValue[15] := 'Yes'; // segregate losses by voltage base
- PropertyValue[16] := 'Yes';
- PropertyValue[17] := 'No';
- PropertyValue[18] := '0';
- PropertyValue[19] := '0';
- PropertyValue[20] := '0';
- PropertyValue[21] := '0';
- PropertyValue[22] := '0';
- PropertyValue[23] := '0';
- PropertyValue[24] := '0';
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
+ end;
+ LoadElem := BranchList.NextObject // Next load at this bus
+ end;
+ CktElem := BranchList.GoForward; // Go on down the tree
+ end;
end;
procedure TEnergyMeterObj.Accumulate_Gen;
@@ -2508,10 +2222,9 @@ procedure TEnergyMeterObj.Accumulate_Gen;
S: Complex;
begin
//----pGen.ActiveTerminalIdx := 1;
- S := Cnegate(CmulReal(pGen.Power[1], 0.001));
+ S := -pGen.Power[1] * 0.001;
TotalZonekw := TotalZonekW + S.re;
TotalZonekvar := TotalZonekvar + S.im;
-
end;
function TEnergyMeterObj.Accumulate_Load(pLoad: TLoadObj;
@@ -2522,20 +2235,21 @@ function TEnergyMeterObj.Accumulate_Load(pLoad: TLoadObj;
Load_EEN,
Load_UE: Double;
begin
- with pLoad do
+ with pLoad do
begin
//----ActiveTerminalIdx := 1;
- S_Load := CmulReal(pLoad.Power[1], 0.001); // Get Power in Terminal 1
+ S_Load := pLoad.Power[1] * 0.001; // Get Power in Terminal 1
kW_Load := S_Load.re;
Result := kw_Load;
- {Accumulate load in zone}
+ // Accumulate load in zone
TotalZonekw := TotalZonekW + kW_Load;
TotalZonekvar := TotalZonekvar + S_Load.im;
- {always integrate even if the value is 0.0
- otherwise the Integrate function is not correct}
- {Invoking the ExceedsNormal and Unserved Properties causes the factors to be computed}
+ // always integrate even if the value is 0.0
+ // otherwise the Integrate function is not correct
+
+ // Invoking the ExceedsNormal and Unserved Properties causes the factors to be computed
if ExcessFlag then
begin // Return Excess load as EEN/UE
if (ExceedsNormal) then
@@ -2562,24 +2276,22 @@ function TEnergyMeterObj.Accumulate_Load(pLoad: TLoadObj;
TotalLoad_EEN := TotalLoad_EEN + Load_EEN;
TotalLoad_UE := TotalLoad_UE + Load_UE;
- end; {WITH}
+ end;
end;
procedure TEnergyMeterObj.ReduceZone;
-
-{Reduce the zone by merging lines}
-
+// Reduce the zone by merging lines
begin
- // Make sure zone list is built
+ // Make sure zone list is built
if not assigned(BranchList) then
MakeMeterZoneLists;
case DSS.ActiveCircuit.ReductionStrategy of
rsShortlines:
- DoReduceShortLines(DSS, BranchList); {See ReduceAlgs.Pas}
- {rsTapEnds: DoReduceTapEnds (BranchList);}
+ DoReduceShortLines(DSS, BranchList); // See ReduceAlgs.Pas
+ // rsTapEnds: DoReduceTapEnds (BranchList);
rsMergeParallel:
DoMergeParallelLines(DSS, BranchList);
rsDangling:
@@ -2592,7 +2304,7 @@ procedure TEnergyMeterObj.ReduceZone;
DoRemoveAll_1ph_Laterals(DSS, BranchList);
else
- {Default}
+ // Default
DoReduceDefault(DSS, BranchList);
end;
end;
@@ -2602,7 +2314,7 @@ function TEnergyMeterObj.CheckBranchList(code: Integer): Boolean;
if not Assigned(BranchList) then
begin
Result := FALSE;
- DoSimpleMsg('Meter Zone Lists need to be built. Do Solve or Makebuslist first!', code);
+ DoSimpleMsg(_('Meter Zone Lists need to be built. Do Solve or Makebuslist first!'), code);
Exit;
end;
Result := TRUE;
@@ -2610,8 +2322,8 @@ function TEnergyMeterObj.CheckBranchList(code: Integer): Boolean;
procedure TEnergyMeterObj.InterpolateCoordinates;
-{Start at the ends of the zone and work toward the start
- interpolating between known coordinates}
+// Start at the ends of the zone and work toward the start
+// interpolating between known coordinates
var
i, BusRef,
FirstCoordRef, SecondCoordRef,
@@ -2625,14 +2337,13 @@ procedure TEnergyMeterObj.InterpolateCoordinates;
with ActiveCircuit do
begin
-
for i := 1 to Branchlist.ZoneEndsList.NumEnds do
begin
Busref := Branchlist.ZoneEndsList.Get(i, PresentNode);
FirstCoordRef := BusRef;
- SecondCoordRef := FirstCoordRef; {so compiler won't issue stupid warning}
- {Find a bus with a coordinate}
+ SecondCoordRef := FirstCoordRef; // so compiler won't issue stupid warning
+ // Find a bus with a coordinate
if not Buses^[BusRef].CoordDefined then
begin
while not Buses^[PresentNode.FromBusReference].CoordDefined do
@@ -2647,12 +2358,13 @@ procedure TEnergyMeterObj.InterpolateCoordinates;
while PresentNode <> NIL do
begin
- {Back up until we find another Coord defined}
- LineCount := 0; {number of line segments in this segment}
+ // Back up until we find another Coord defined
+ LineCount := 0; // number of line segments in this segment
StartNode := PresentNode;
CktElem := PresentNode.CktObject;
if FirstCoordRef <> PresentNode.FromBusReference then
- begin {Handle special case for end branch}
+ begin
+ // Handle special case for end branch
if Buses^[PresentNode.FromBusReference].CoordDefined then
FirstCoordRef := PresentNode.FromBusReference
else
@@ -2660,14 +2372,14 @@ procedure TEnergyMeterObj.InterpolateCoordinates;
end;
repeat
- CktElem.Checked := TRUE;
+ Include(CktElem.Flags, Flg.Checked);
PresentNode := PresentNode.ParentBranch;
if PresentNode = NIL then
Break;
CktElem := PresentNode.CktObject;
SecondCoordRef := PresentNode.FromBusReference;
Inc(LineCount);
- until Buses^[SecondCoordRef].CoordDefined or CktElem.Checked;
+ until Buses^[SecondCoordRef].CoordDefined or (Flg.Checked in CktElem.Flags);
if (PresentNode <> NIL) and (LineCount > 1) then
if Buses^[SecondCoordRef].CoordDefined then
@@ -2675,26 +2387,21 @@ procedure TEnergyMeterObj.InterpolateCoordinates;
CalcBusCoordinates(StartNode, FirstCoordRef, SecondCoordRef, LineCount);
end
else
- Break; {While - went as far as we could go this way}
+ Break; // While - went as far as we could go this way
FirstCoordRef := SecondCoordRef;
end;
-
- end; {For}
-
- end; {With}
-
+ end; // For
+ end; // With
end;
procedure TEnergyMeterObj.CalcBusCoordinates(StartBranch: TCktTreeNode;
FirstCoordRef, SecondCoordref, LineCount: Integer);
-
var
X, Y, Xinc, Yinc: Double;
begin
-
if LineCount = 1 then
- Exit; {Nothing to do!}
+ Exit; // Nothing to do!
with ActiveCircuit do
begin
@@ -2704,7 +2411,7 @@ procedure TEnergyMeterObj.CalcBusCoordinates(StartBranch: TCktTreeNode;
X := Buses^[FirstCoordref].X;
Y := Buses^[FirstCoordref].Y;
- {Either start with the "to" end of StartNode or the "from" end;}
+ // Either start with the "to" end of StartNode or the "from" end;
if FirstCoordRef <> StartBranch.FromBusReference then
begin // Start with "to" end
X := X - Xinc;
@@ -2729,9 +2436,6 @@ procedure TEnergyMeterObj.CalcBusCoordinates(StartBranch: TCktTreeNode;
end;
end;
-
-{--------------------------- CalcReliabilityIndices ----------------------------}
-
procedure TEnergyMeterObj.CalcReliabilityIndices(AssumeRestoration: Boolean);
var
PD_Elem: TPDElement;
@@ -2740,12 +2444,10 @@ procedure TEnergyMeterObj.CalcReliabilityIndices(AssumeRestoration: Boolean);
pBus: TDSSBus;
dblNcusts: Double;
dblkW: Double;
-
begin
-
if not Assigned(SequenceList) then
begin
- DoSimpleMsg('Energymeter.' + Name + ' Zone not defined properly.', 52901);
+ DoSimpleMsg('%s Zone not defined properly.', [FullName], 52901);
Exit;
end;
@@ -2764,14 +2466,14 @@ procedure TEnergyMeterObj.CalcReliabilityIndices(AssumeRestoration: Boolean);
end;
// Forward sweep to get number of interruptions
- // Initialize number of interruptions and Duration
+ // Initialize number of interruptions and Duration
PD_Elem := TPDElement(SequenceList.Get(1));
pBus := DSS.ActiveCircuit.Buses^[PD_Elem.Terminals[PD_Elem.FromTerminal - 1].BusRef];
pBus.Bus_Num_Interrupt := Source_NumInterruptions;
pBus.BusCustInterrupts := Source_NumInterruptions * pBus.BusTotalNumCustomers;
pBus.Bus_Int_Duration := Source_IntDuration;
- // init for defining sections
+ // init for defining sections
SectionCount := 0;
pBus.BusSectionID := SectionCount; // section before 1st OCP device is zero
@@ -2781,13 +2483,13 @@ procedure TEnergyMeterObj.CalcReliabilityIndices(AssumeRestoration: Boolean);
if SectionCount = 0 then
begin // Error - no OCP devices
DoSimpleMsg
- ('Error: No Overcurrent Protection device (Relay, Recloser, or Fuse) defined. Aborting Reliability calc.',
+ (_('Error: No Overcurrent Protection device (Relay, Recloser, or Fuse) defined. Aborting Reliability calc.'),
52902);
Exit;
end;
- // Now have number of sections so allocate FeederSections array
- ReallocMem(FeederSections, SizeOf(FeederSections^[1]) * SectionCount);
+ // Now have number of sections so allocate FeederSections array
+ ReallocMem(FeederSections, SizeOf(FeederSections^[1]) * (SectionCount + 1));
for idx := 0 to SectionCount do
with FeederSections^[idx] do // Initialize all Section data
begin
@@ -2818,7 +2520,7 @@ procedure TEnergyMeterObj.CalcReliabilityIndices(AssumeRestoration: Boolean);
pBus := DSS.ActiveCircuit.Buses^[PD_Elem.Terminals[PD_Elem.ToTerminal - 1].BusRef];
DblInc(pSection.SumBranchFltRates, pBus.Bus_Num_Interrupt * PD_Elem.BranchFltRate);
DblInc(pSection.SumFltRatesXRepairHrs, (pBus.Bus_Num_Interrupt * PD_Elem.BranchFltRate * PD_Elem.HrsToRepair));
- if PD_Elem.HasOCPDevice then
+ if Flg.HasOCPDevice in PD_Elem.Flags then
begin // set Section properties
pSection.OCPDeviceType := GetOCPDeviceType(PD_Elem);
pSection.SeqIndex := idx; // index of pdelement with OCP device at head of section
@@ -2842,13 +2544,12 @@ procedure TEnergyMeterObj.CalcReliabilityIndices(AssumeRestoration: Boolean);
end;
- { Compute Avg Interruption duration of each Section except 0 Section}
+ // Compute Avg Interruption duration of each Section except 0 Section
for idx := 1 to SectionCount do
with FeederSections^[idx] do
AverageRepairTime := SumFltRatesXRepairHrs / SumBranchFltRates;
- { Set Bus_int_Duration}
-
+ // Set Bus_int_Duration
with ActiveCircuit do
for idx := 1 to NumBuses do
begin
@@ -2868,9 +2569,9 @@ procedure TEnergyMeterObj.CalcReliabilityIndices(AssumeRestoration: Boolean);
AverageRepairTime * 60.0, SumFltRatesXRepairHrs, SumBranchFltRates]));
{$ENDIF}
- {Compute SAIFI based on numcustomers and load kW}
- {SAIFI is weighted by specified load weights}
- {SAIFI is for the EnergyMeter Zone}
+ // Compute SAIFI based on numcustomers and load kW
+ // SAIFI is weighted by specified load weights
+ // SAIFI is for the EnergyMeter Zone
SAIFI := 0.0;
CAIDI := 0.0;
SAIFIkW := 0.0;
@@ -2878,28 +2579,28 @@ procedure TEnergyMeterObj.CalcReliabilityIndices(AssumeRestoration: Boolean);
dblkW := 0.0;
CustInterrupts := 0.0;
- // Use LoadList for SAIFI calculation
+ // Use LoadList for SAIFI calculation
with ActiveCircuit do
begin
for idx := 1 to LoadList.Count do // all loads in meter zone
begin
- // Compute CustInterrupts based on interrupts at each load
+ // Compute CustInterrupts based on interrupts at each load
with TLoadObj(LoadList.Get(idx)) do
begin
pBus := Buses^[Terminals[0].BusRef]; // pointer to Load's bus
CustInterrupts := CustInterrupts + (NumCustomers * RelWeighting * pBus.Bus_Num_Interrupt);
SAIFIkW := SAIFIkW + kWBase * RelWeighting * pBus.Bus_Num_Interrupt;
DblInc(dblNcusts, NumCustomers * RelWeighting);
- // total up weighted numcustomers
+ // total up weighted numcustomers
DblInc(dblkW, kWBase * RelWeighting); // total up weighted kW
- // Set BusCustDurations for Branch reliability export
+ // Set BusCustDurations for Branch reliability export
pBus.BusCustDurations := (pBus.BusTotalNumCustomers + NumCustomers) *
RelWeighting * pBus.Bus_Int_Duration * pBus.Bus_Num_Interrupt;
end;
end;
end;
- // Compute SAIDI from Sections list
+ // Compute SAIDI from Sections list
SAIDI := 0.0;
for idx := 1 to SectionCount do // ignore idx=0
with FeederSections^[idx] do
@@ -2918,54 +2619,6 @@ procedure TEnergyMeterObj.CalcReliabilityIndices(AssumeRestoration: Boolean);
if dblkW > 0.0 then
SAIFIkW := SAIFIkW / dblkW; // Normalize to total kW
-
-end;
-
-{-------------------------------------------------------------------------------}
-function TEnergyMeterObj.GetPropertyValue(Index: Integer): String;
-begin
- case Index of
- 4, 7:
- Result := '(';
- else
- Result := '';
- end;
-
- case Index of
- 4:
- begin // option
- if ExcessFlag then
- Result := Result + 'E,'
- else
- Result := Result + 'T,';
- if ZoneIsRadial then
- Result := Result + ' R,'
- else
- Result := Result + ' M,';
- if VoltageUEOnly then
- Result := Result + ' V'
- else
- Result := Result + ' C';
- end;
- 20:
- Result := Format('%.11g', [SAIFI]);
- 21:
- Result := Format('%.11g', [SAIFIkW]);
- 22:
- Result := Format('%.11g', [SAIDI]);
- 23:
- Result := Format('%.11g', [CAIDI]);
- 24:
- Result := Format('%.11g', [CustInterrupts]);
- else
- Result := Result + inherited GetPropertyValue(index);
- end;
-
- case Index of
- 4, 7:
- Result := Result + ')';
- else
- end;
end;
procedure TEnergyMeterObj.SaveZone(const dirname: String);
@@ -2976,9 +2629,9 @@ procedure TEnergyMeterObj.SaveZone(const dirname: String);
FBranches, FShunts, FLoads, FGens, FCaps, FXfmrs: TFileStream;
NBranches, NShunts, Nloads, NGens, NCaps, NXfmrs: Integer;
begin
- {We are in the directory indicated by dirname}
+ // We are in the directory indicated by dirname
-{Run down the zone and write each element into a file}
+ // Run down the zone and write each element into a file
if BranchList = NIL then
Exit;
@@ -2990,81 +2643,80 @@ procedure TEnergyMeterObj.SaveZone(const dirname: String);
FCaps := nil;
FXfmrs := nil;
- {Open some files:}
+ // Open some files:
try
- FBranches := TFileStream.Create(DSS.CurrentDSSDir + 'Branches.dss', fmCreate); // Both lines and transformers
+ FBranches := TBufferedFileStream.Create(DSS.CurrentDSSDir + 'Branches.dss', fmCreate); // Both lines and transformers
NBranches := 0;
except
On E: Exception do
begin
- DoSimpleMsg('Error creating Branches.dss for Energymeter: ' + Self.Name + '. ' + E.Message, 530);
+ DoSimpleMsg('Error creating Branches.dss for Energymeter: %s. %s', [Self.Name, E.Message], 530);
FreeAndNil(FBranches);
Exit;
end;
end;
try
- FXfmrs := TFileStream.Create(DSS.CurrentDSSDir + 'Transformers.dss', fmCreate); // Both lines and transformers
+ FXfmrs := TBufferedFileStream.Create(DSS.CurrentDSSDir + 'Transformers.dss', fmCreate); // Both lines and transformers
NXfmrs := 0;
except
On E: Exception do
begin
- DoSimpleMsg('Error creating Transformers.dss for Energymeter: ' + Self.Name + '. ' + E.Message, 53001);
+ DoSimpleMsg('Error creating Transformers.dss for Energymeter: %s. %s', [Self.Name, E.Message], 53001);
FreeAndNil(FXfmrs);
Exit;
end;
end;
try
- FShunts := TFileStream.Create(DSS.CurrentDSSDir + 'Shunts.dss', fmCreate);
+ FShunts := TBufferedFileStream.Create(DSS.CurrentDSSDir + 'Shunts.dss', fmCreate);
NShunts := 0;
except
On E: Exception do
begin
- DoSimpleMsg('Error creating Shunts.dss for Energymeter: ' + Self.Name + '. ' + E.Message, 531);
+ DoSimpleMsg('Error creating Shunts.dss for Energymeter: %s. %s', [Self.Name, E.Message], 531);
FreeAndNil(FShunts);
Exit;
end;
end;
try
- FLoads := TFileStream.Create(DSS.CurrentDSSDir + 'Loads.dss', fmCreate);
+ FLoads := TBufferedFileStream.Create(DSS.CurrentDSSDir + 'Loads.dss', fmCreate);
Nloads := 0;
except
On E: Exception do
begin
- DoSimpleMsg('Error creating Loads.dss for Energymeter: ' + Self.Name + '. ' + E.Message, 532);
+ DoSimpleMsg('Error creating Loads.dss for Energymeter: %s. %s', [Self.Name, E.Message], 532);
FreeAndNil(FLoads);
Exit;
end;
end;
try
- FGens := TFileStream.Create(DSS.CurrentDSSDir + 'Generators.dss', fmCreate);
+ FGens := TBufferedFileStream.Create(DSS.CurrentDSSDir + 'Generators.dss', fmCreate);
NGens := 0;
except
On E: Exception do
begin
- DoSimpleMsg('Error creating Generators.dss for Energymeter: ' + Self.Name + '. ' + E.Message, 533);
+ DoSimpleMsg('Error creating Generators.dss for Energymeter: %s. %s', [Self.Name, E.Message], 533);
FreeAndNil(FGens);
Exit;
end;
end;
try
- FCaps := TFileStream.Create(DSS.CurrentDSSDir + 'Capacitors.dss', fmCreate);
+ FCaps := TBufferedFileStream.Create(DSS.CurrentDSSDir + 'Capacitors.dss', fmCreate);
Ncaps := 0;
except
On E: Exception do
begin
- DoSimpleMsg('Error creating Capacitors.dss for Energymeter: ' + Self.Name + '. ' + E.Message, 534);
+ DoSimpleMsg('Error creating Capacitors.dss for Energymeter: %s. %s', [Self.Name, E.Message], 534);
FreeAndNil(FCaps);
Exit;
end;
end;
-
cktElem := BranchList.First;
with ActiveCircuit do
while cktElem <> NIL do
@@ -3077,7 +2729,7 @@ procedure TEnergyMeterObj.SaveZone(const dirname: String);
begin
Inc(NXfmrs);
WriteActiveDSSObject(DSS, FXfmrs, 'New'); // sets HasBeenSaved := TRUE
- if cktElem.HasControl then
+ if Flg.HasControl in cktElem.Flags then
begin
pControlElem := cktElem.ControlElementList.First;
while pControlElem <> NIL do
@@ -3089,10 +2741,11 @@ procedure TEnergyMeterObj.SaveZone(const dirname: String);
end;
end
else
- begin {Mostly LINE elements}
+ begin
+ // Mostly LINE elements
Inc(NBranches);
WriteActiveDSSObject(DSS, FBranches, 'New'); // sets HasBeenSaved := TRUE
- if cktElem.HasControl then
+ if Flg.HasControl in cktElem.Flags then
begin
pControlElem := cktElem.ControlElementList.First;
while pControlElem <> NIL do
@@ -3114,9 +2767,9 @@ procedure TEnergyMeterObj.SaveZone(const dirname: String);
LoadElement := TLoadObj(shuntElement);
if LoadElement.HasBeenAllocated then
begin
- {Manually set the allocation factor so it shows up}
- Parser.CmdString := 'allocationfactor=' + Format('%-.4g', [LoadElement.AllocationFactor]);
- LoadElement.Edit;
+ // Manually set the allocation factor so it shows up
+ DSS.Parser.CmdString := 'allocationfactor=' + Format('%-.4g', [LoadElement.AllocationFactor]);
+ LoadElement.Edit(DSS.Parser);
end;
ActiveCktElement := shuntElement; // reset in case Edit mangles it
Inc(NLoads);
@@ -3127,7 +2780,7 @@ procedure TEnergyMeterObj.SaveZone(const dirname: String);
begin
Inc(NGens);
WriteActiveDSSObject(DSS, FGens, 'New');
- if shuntElement.HasControl then
+ if Flg.HasControl in shuntElement.Flags then
begin
pControlElem := shuntElement.ControlElementList.First;
while pControlElem <> NIL do
@@ -3143,7 +2796,7 @@ procedure TEnergyMeterObj.SaveZone(const dirname: String);
begin
Inc(NCaps);
WriteActiveDSSObject(DSS, FCaps, 'New');
- if shuntElement.HasControl then
+ if Flg.HasControl in shuntElement.Flags then
begin
pControlElem := shuntElement.ControlElementList.First;
while pControlElem <> NIL do
@@ -3161,10 +2814,10 @@ procedure TEnergyMeterObj.SaveZone(const dirname: String);
end;
shuntElement := BranchList.NextObject
end;
- end; {if enabled}
+ end; // if enabled
cktElem := BranchList.GoForward;
- end;{WHILE}
+ end; // WHILE
FreeAndNil(FBranches);
FreeAndNil(FXfmrs);
@@ -3173,7 +2826,7 @@ procedure TEnergyMeterObj.SaveZone(const dirname: String);
FreeAndNil(FGens);
FreeAndNil(FCaps);
- {If any records were written to the file, record their relative names}
+ // If any records were written to the file, record their relative names
if NBranches > 0 then
DSS.SavedFileList.Add(dirname + PathDelim + 'Branches.dss')
else
@@ -3200,7 +2853,6 @@ procedure TEnergyMeterObj.SaveZone(const dirname: String);
DeleteFile('Capacitors.dss');
end;
-
procedure TEnergyMeterObj.GetPCEatZone(const allowEmpty: Boolean);
var
cktElem,
@@ -3235,7 +2887,7 @@ procedure TEnergyMeterObj.GetPCEatZone(const allowEmpty: Boolean);
begin
ActiveCktElement := shuntElement;
SetLength(ZonePCE, length(ZonePCE) + 1);
- ZonePCE[high(ZonePCE)] := shuntElement.DSSClassName + '.' + shuntElement.Name;
+ ZonePCE[high(ZonePCE)] := shuntElement.FullName;
shuntElement := BranchList.NextObject;
end;
end;
@@ -3263,27 +2915,23 @@ procedure TEnergyMeterObj.CloseDemandIntervalFile;
var
i: Integer;
begin
-
try
- if This_Meter_DIFileIsOpen then with DSS.EnergyMeterClass do
+ if This_Meter_DIFileIsOpen then
begin
- if DSS.EnergyMeterClass.DI_MHandle <> NIL then
+ if DI_MHandle <> NIL then
CloseMHandler(DSS, DI_MHandle, MakeDIFileName, DI_Append);
- DI_MHandle := NIL;
This_Meter_DIFileIsOpen := FALSE;
if PHV_MHandle <> NIL then
if VPhaseReportFileIsOpen then
CloseMHandler(DSS, PHV_MHandle, MakeVPhaseReportFileName, PHV_Append);
- PHV_MHandle := NIL;
VPhaseReportFileIsOpen := FALSE;
end;
except
ON E: Exception do
- DoSimpleMsg('Error Closing Demand Interval file for Meter "' + Name + '"', 534);
+ DoSimpleMsg('Error Closing Demand Interval file for Meter "%s"', [Name], 534);
end;
-
- {Write Registers to Totals File}
+ // Write Registers to Totals File
with DSS.EnergyMeterClass do
begin
WriteintoMemStr(EMT_MHandle, '"' + Self.Name + '"');
@@ -3297,16 +2945,13 @@ procedure TEnergyMeterObj.OpenDemandIntervalFile;
var
i, j: Integer;
vbase: Double;
-
begin
-
try
if This_Meter_DIFileIsOpen then
CloseDemandIntervalFile;
- if (DSS.EnergyMeterClass.DI_Verbose) then with DSS.EnergyMeterClass do
+ if (DSS.EnergyMeterClass.DI_Verbose) then
begin
-
This_Meter_DIFileIsOpen := TRUE;
if DI_MHandle <> NIL then
DI_MHandle.free;
@@ -3315,7 +2960,7 @@ procedure TEnergyMeterObj.OpenDemandIntervalFile;
WriteintoMemStr(DI_MHandle, ', "' + RegisterNames[i] + '"');
WriteintoMemStr(DI_MHandle, Char(10));
- {Phase Voltage Report, if requested}
+ // Phase Voltage Report, if requested
if FPhaseVoltageReport then
begin
if PHV_MHandle <> NIL then
@@ -3341,9 +2986,8 @@ procedure TEnergyMeterObj.OpenDemandIntervalFile;
end;
except
On E: Exception do
- DoSimpleMsg('Error opening demand interval file "' + Name + DSS._Name + '.CSV' + ' for writing.' + CRLF + E.Message, 535);
+ DoSimpleMsg('Error opening demand interval file "%s.csv" for writing. %s', [Name + DSS._Name, CRLF + E.Message], 535);
end;
-
end;
procedure TEnergyMeterObj.WriteDemandIntervalData;
@@ -3359,7 +3003,7 @@ procedure TEnergyMeterObj.WriteDemandIntervalData;
end;
begin
- if DSS.EnergyMeterClass.DI_Verbose and This_Meter_DIFileIsOpen then with DSS.EnergyMeterClass do
+ if DSS.EnergyMeterClass.DI_Verbose and This_Meter_DIFileIsOpen then
begin
with DSS.ActiveCircuit.Solution do
WriteintoMem(DI_MHandle, DynaVars.dblHour);
@@ -3368,14 +3012,14 @@ procedure TEnergyMeterObj.WriteDemandIntervalData;
WriteIntoMemStr(DI_MHandle, Char(10));
end;
- {Add to Class demand interval registers}
+ // Add to Class demand interval registers
with DSS.EnergyMeterClass do
for i := 1 to NumEMRegisters do
DI_RegisterTotals[i] := DI_RegisterTotals[i] + Derivatives[i] * TotalsMask[i];
- {Phase Voltage Report, if requested}
- if VPhaseReportFileIsOpen then with DSS.EnergyMeterClass do
+ // Phase Voltage Report, if requested
+ if VPhaseReportFileIsOpen then
begin
with DSS.ActiveCircuit.Solution do
WriteintoMem(PHV_MHandle, DynaVars.dblHour);
@@ -3391,25 +3035,23 @@ procedure TEnergyMeterObj.WriteDemandIntervalData;
end;
WriteintoMemStr(PHV_MHandle, Char(10));
end;
-
end;
procedure TEnergyMeter.CloseAllDIFiles;
var
mtr: TEnergyMeterObj;
-
begin
if FSaveDemandInterval then
begin
- {While closing DI files, write all meter registers to one file}
+ // While closing DI files, write all meter registers to one file
try
CreateMeterTotals;
except
On E: Exception do
- DoSimpleMsg('Error on Rewrite of totals file: ' + E.Message, 536);
+ DoSimpleMsg('Error on Rewrite of totals file: %s', [E.Message], 536);
end;
- {Close all the DI file for each meter}
+ // Close all the DI file for each meter
mtr := DSS.ActiveCircuit.EnergyMeters.First;
while mtr <> NIL do
begin
@@ -3418,46 +3060,40 @@ procedure TEnergyMeter.CloseAllDIFiles;
mtr := DSS.ActiveCircuit.EnergyMeters.Next;
end;
- WriteTotalsFile; // Sum all energymeter registers to "Totals_{}.CSV"
+ WriteTotalsFile; // Sum all energymeter registers to "Totals_{}.csv"
SystemMeter.CloseDemandIntervalFile;
SystemMeter.Save;
if EMT_MHandle <> NIL then
- CloseMHandler(DSS, EMT_MHandle, DI_Dir + PathDelim + 'EnergyMeterTotals' + DSS._Name + '.CSV', EMT_Append);
- EMT_MHandle := NIL;
+ CloseMHandler(DSS, EMT_MHandle, DI_Dir + PathDelim + 'EnergyMeterTotals' + DSS._Name + '.csv', EMT_Append);
if TDI_MHandle <> NIL then
- CloseMHandler(DSS, TDI_MHandle, DI_Dir + PathDelim + 'DI_Totals' + DSS._Name + '.CSV', TDI_Append);
- TDI_MHandle := NIL;
+ CloseMHandler(DSS, TDI_MHandle, DI_Dir + PathDelim + 'DI_Totals' + DSS._Name + '.csv', TDI_Append);
DSS.DIFilesAreOpen := FALSE;
if OverloadFileIsOpen then
begin
if OV_MHandle <> NIL then
- CloseMHandler(DSS, OV_MHandle, DSS.EnergyMeterClass.DI_Dir + PathDelim + 'DI_Overloads' + DSS._Name + '.CSV', OV_Append);
- OV_MHandle := NIL;
+ CloseMHandler(DSS, OV_MHandle, DSS.EnergyMeterClass.DI_Dir + PathDelim + 'DI_Overloads' + DSS._Name + '.csv', OV_Append);
OverloadFileIsOpen := FALSE;
end;
if VoltageFileIsOpen then
begin
if VR_MHandle <> NIL then
- CloseMHandler(DSS, VR_MHandle, DSS.EnergyMeterClass.DI_Dir + PathDelim + 'DI_VoltExceptions' + DSS._Name + '.CSV', VR_Append);
- VR_MHandle := NIL;
+ CloseMHandler(DSS, VR_MHandle, DSS.EnergyMeterClass.DI_Dir + PathDelim + 'DI_VoltExceptions' + DSS._Name + '.csv', VR_Append);
VoltageFileIsOpen := FALSE;
end;
end;
end;
procedure TEnergyMeterObj.AppendDemandIntervalFile;
-
var
FileNm: String;
begin
-
- {Only called if "SaveDemandInterval"}
+ // Only called if "SaveDemandInterval"
if This_Meter_DIFileIsOpen then
Exit;
try
- if DSS.Energymeterclass.FDI_Verbose then with DSS.EnergyMeterClass do
+ if DSS.Energymeterclass.FDI_Verbose then
begin
FileNm := MakeDIFileName; // Creates directory if it doesn't exist
if FileExists(FileNm) then
@@ -3471,7 +3107,7 @@ procedure TEnergyMeterObj.AppendDemandIntervalFile;
end;
except
On E: Exception do
- DoSimpleMsg('Error opening demand interval file "' + Name + DSS._Name + '.CSV' + ' for appending.' + CRLF + E.Message, 537);
+ DoSimpleMsg('Error opening demand interval file "%s.csv" for appending. %s', [Name + DSS._Name, CRLF + E.Message], 537);
end;
end;
@@ -3517,11 +3153,9 @@ procedure TEnergyMeter.AppendAllDIFiles;
var
mtr: TEnergyMeterObj;
Filenm: String;
-
begin
if FSaveDemandInterval then
begin
-
ClearDI_Totals; // clears accumulator arrays
mtr := DSS.ActiveCircuit.EnergyMeters.First;
@@ -3534,26 +3168,27 @@ procedure TEnergyMeter.AppendAllDIFiles;
SystemMeter.AppendDemandIntervalFile;
- {Open FDI_Totals}
+ // Open FDI_Totals
try
- FileNm := DI_Dir + PathDelim + 'DI_Totals' + DSS._Name + '.CSV';
- {File Must Exist}
+ FileNm := DI_Dir + PathDelim + 'DI_Totals' + DSS._Name + '.csv';
+
+ // File Must Exist
if FileExists(FileNm) then
TDI_Append := TRUE;
CreateFDI_Totals;
except
On E: Exception do
- DoSimpleMsg('Error opening demand interval file "' + Name + DSS._Name + '.CSV' + ' for appending.' + CRLF + E.Message, 538);
+ DoSimpleMsg('Error opening demand interval file "%s.csv" for appending.', [Name + DSS._Name, CRLF + E.Message], 538);
end;
DSS.DIFilesAreOpen := TRUE;
- end;{IF}
+ end;
end;
function TEnergyMeterObj.MakeDIFileName: String;
begin
- Result := DSS.EnergyMeterClass.DI_Dir + PathDelim + Self.Name + DSS._Name + '.CSV';
+ Result := DSS.EnergyMeterClass.DI_Dir + PathDelim + Self.Name + DSS._Name + '.csv';
end;
procedure TEnergyMeter.Set_SaveDemandInterval(const Value: Boolean);
@@ -3581,11 +3216,10 @@ procedure TEnergyMeter.WriteOverloadReport;
dBuffer: pDoubleArray;
begin
-{
- Scans the active circuit for overloaded PD elements and writes each to a file
- This is called only if in Demand Interval (DI) mode and the file is open.
-}
-{ Prepares everything for using seasonal ratings if required}
+// Scans the active circuit for overloaded PD elements and writes each to a file
+// This is called only if in Demand Interval (DI) mode and the file is open.
+
+ // Prepares everything for using seasonal ratings if required
RatingIdx := -1;
if DSS.SeasonalRating then
begin
@@ -3601,7 +3235,7 @@ procedure TEnergyMeter.WriteOverloadReport;
DSS.SeasonalRating := FALSE; // The user didn't define the seasonal signal
end;
- { CHECK PDELEMENTS ONLY}
+ // CHECK PDELEMENTS ONLY
PDelem := DSS.ActiveCircuit.PDElements.First;
while PDelem <> NIL do
begin
@@ -3613,9 +3247,9 @@ procedure TEnergyMeter.WriteOverloadReport;
PDelem.ComputeIterminal;
Cmax := PDelem.MaxTerminalOneImag; // For now, check only terminal 1 for overloads
- // Section introduced in 02/20/2019 for allowing the automatic change of ratings
- // when the seasonal ratings option is active
- ClassName := lowercase(PDElem.DSSClassName);
+ // Section introduced in 02/20/2019 for allowing the automatic change of ratings
+ // when the seasonal ratings option is active
+ ClassName := AnsiLowerCase(PDElem.DSSClassName);
if DSS.SeasonalRating and (ClassName = 'line') and (PDElem.NumAmpRatings > 1) then
begin
if (RatingIdx > PDElem.NumAmpRatings) or (RatingIdx < 0) then
@@ -3638,8 +3272,7 @@ procedure TEnergyMeter.WriteOverloadReport;
if (Cmax > NormAmps) or (Cmax > EmergAmps) then
begin
-
- // Gets the currents for the active Element
+ // Gets the currents for the active Element
dBuffer := Allocmem(sizeof(Double) * PDElem.NPhases * PDElem.NTerms);
PDElem.Get_Current_Mags(dBuffer);
dVector := Allocmem(sizeof(Double) * 3); // for storing
@@ -3675,7 +3308,7 @@ procedure TEnergyMeter.WriteOverloadReport;
with DSS.ActiveCircuit.Solution do
WriteintoMem(OV_MHandle, DynaVars.dblHour);
- WriteintoMemStr(OV_MHandle, ', ' + FullName(PDelem));
+ WriteintoMemStr(OV_MHandle, ', ' + EncloseQuotes(PDelem.FullName));
WriteintoMem(OV_MHandle, PDElem.NormAmps);
WriteintoMem(OV_MHandle, pdelem.EmergAmps);
if PDElem.Normamps > 0.0 then
@@ -3688,14 +3321,14 @@ procedure TEnergyMeter.WriteOverloadReport;
WriteintoMem(OV_MHandle, 0.0);
with ActiveCircuit do // Find bus of first terminal
WriteintoMem(OV_MHandle, Buses^[MapNodeToBus^[PDElem.NodeRef^[1]].BusRef].kVBase);
- // Adds the currents in Amps per phase at the end of the report
+ // Adds the currents in Amps per phase at the end of the report
for i := 1 to 3 do
WriteintoMem(OV_MHandle, dVector^[i]);
WriteintoMemStr(OV_MHandle, ' ' + Char(10));
end;
- end; { }
+ end;
end;
PDelem := DSS.ActiveCircuit.PDElements.Next;
end;
@@ -3730,28 +3363,25 @@ procedure TEnergyMeter.CreateFDI_Totals;
WriteintoMemStr(TDI_MHandle, Char(10));
except
On E: Exception do
- DoSimpleMsg('Error creating: "' + DI_Dir + PathDelim + 'DI_Totals' + DSS._Name + '.CSV": ' + E.Message, 539)
+ DoSimpleMsg('Error creating: "%sDI_Totals%s.csv": %s', [DI_Dir + PathDelim, DSS._Name, E.Message], 539)
end;
end;
-{ TSystemMeter }
-
procedure TSystemMeter.AppendDemandIntervalFile;
var
FileNm: String;
begin
-
- {Only called if "SaveDemandInterval"}
+ // Only called if "SaveDemandInterval"
if This_Meter_DIFileIsOpen then
Exit;
try
- FileNm := DSS.EnergyMeterClass.Di_Dir + PathDelim + 'DI_SystemMeter' + DSS._Name + '.CSV';
- {File Must Exist}
+ FileNm := DSS.EnergyMeterClass.Di_Dir + PathDelim + 'DI_SystemMeter' + DSS._Name + '.csv';
+ // File Must Exist
if FileExists(FileNm) then
begin
-// DI_MMFView:= MapFile2Memory(EnergyMeterClass.DI_Dir+ PathDelim + 'DI_SystemMeter' + DSS._Name + '.CSV', DI_MMFHandle);
+// DI_MMFView:= MapFile2Memory(EnergyMeterClass.DI_Dir+ PathDelim + 'DI_SystemMeter' + DSS._Name + '.csv', DI_MMFHandle);
// DI_Cursor := GetMMFCursor(DI_MMFView);
end
else
@@ -3760,10 +3390,9 @@ procedure TSystemMeter.AppendDemandIntervalFile;
except
On E: Exception do
begin
- DosimpleMsg(DSS, 'Error opening demand interval file "' + FileNm + ' for appending.' + CRLF + E.Message, 540);
+ DosimpleMsg(DSS, 'Error opening demand interval file "%s" for appending. %s', [FileNm, CRLF + E.Message], 540);
end;
end;
-
end;
procedure TSystemMeter.Clear;
@@ -3788,39 +3417,55 @@ procedure TSystemMeter.CloseDemandIntervalFile;
begin
if This_Meter_DIFileIsOpen then with DSS.EnergyMeterClass do
begin
- File_Path := DSS.EnergyMeterClass.DI_Dir + PathDelim + 'DI_SystemMeter' + DSS._Name + '.CSV';
- CloseMHandler(DSS, SDI_MHandle, File_Path, SDI_Append);
- SDI_MHandle := NIL;
+ File_Path := DSS.EnergyMeterClass.DI_Dir + PathDelim + 'DI_SystemMeter' + DSS._Name + '.csv';
+ CloseMHandler(DSS, SDI_MHandle, File_Path, DSS.EnergyMeterClass.SDI_Append);
This_Meter_DIFileIsOpen := FALSE;
end;
end;
-constructor TSystemMeter.Create(dssContext: TDSSContext);
+constructor TSystemMeter.Create(EnergyMeterClass: TEnergyMeter);
begin
- DSS := dssContext;
+ DSS := EnergyMeterClass.DSS;
Clear;
This_Meter_DIFileIsOpen := FALSE;
+ with EnergyMeterClass do
+ begin
+ SDI_MHandle := NIL;
+ TDI_MHandle := NIL;
+ VR_MHandle := NIL;
+ OV_MHandle := NIL;
+ end;
end;
destructor TSystemMeter.Destroy;
begin
+ with DSS.EnergyMeterClass do
+ begin
+ if SDI_MHandle <> NIL then
+ FreeAndNil(SDI_MHandle);
+ if TDI_MHandle <> NIL then
+ FreeAndNil(TDI_MHandle);
+ if VR_MHandle <> NIL then
+ FreeAndNil(VR_MHandle);
+ if OV_MHandle <> NIL then
+ FreeAndNil(OV_MHandle);
+ end;
inherited;
-
end;
procedure TSystemMeter.Integrate(var Reg: Double; Value: Double; var Deriv: Double);
begin
if DSS.ActiveCircuit.TrapezoidalIntegration then
begin
- {Trapezoidal Rule Integration}
+ // Trapezoidal Rule Integration
if not FirstSampleAfterReset then
Reg := Reg + 0.5 * DSS.ActiveCircuit.Solution.IntervalHrs * (Value + Deriv);
end
- else {Plain Euler integration}
+ else
+ // Plain Euler integration
Reg := Reg + DSS.ActiveCircuit.Solution.IntervalHrs * Value;
Deriv := Value;
-
end;
procedure TSystemMeter.OpenDemandIntervalFile;
@@ -3838,10 +3483,8 @@ procedure TSystemMeter.OpenDemandIntervalFile;
end;
except
On E: Exception do
- DoSimpleMsg(DSS, 'Error opening demand interval file "DI_SystemMeter' + DSS._Name + '.CSV" for writing.' + CRLF + E.Message, 541);
+ DoSimpleMsg(DSS, 'Error opening demand interval file "DI_SystemMeter%s.csv" for writing.', [DSS._Name, CRLF + E.Message], 541);
end;
-
-
end;
procedure TSystemMeter.Reset;
@@ -3855,9 +3498,9 @@ procedure TSystemMeter.Save;
CSVName, Folder: String;
begin
try
- CSVName := 'SystemMeter' + DSS._Name + '.CSV';
- {If we are doing a simulation and saving interval data, create this in the
- same directory as the demand interval data}
+ CSVName := 'SystemMeter' + DSS._Name + '.csv';
+ // If we are doing a simulation and saving interval data, create this in the
+ // same directory as the demand interval data
if DSS.energyMeterClass.SaveDemandInterval then
Folder := DSS.energyMeterClass.DI_DIR + PathDelim
else
@@ -3868,7 +3511,7 @@ procedure TSystemMeter.Save;
except
On E: Exception do
begin
- DoSimpleMsg(DSS, 'Error opening System Meter File "' + CRLF + CSVName + '": ' + E.Message, 542);
+ DoSimpleMsg(DSS, 'Error opening System Meter File "%s": %s', [CSVName, E.Message], 542);
Exit;
end
end;
@@ -3876,7 +3519,7 @@ procedure TSystemMeter.Save;
with DSS.EnergyMeterClass do
try
if SM_MHandle <> NIL then
- SM_MHandle.Free;
+ FreeAndNil(SM_MHandle);
SM_MHandle := Create_Meter_Space('Year, ');
WriteintoMemStr(SM_MHandle, 'kWh, kvarh, "Peak kW", "peak kVA", "Losses kWh", "Losses kvarh", "Peak Losses kW"' + Char(10));
WriteintoMemStr(SM_MHandle, inttostr(DSS.ActiveCircuit.Solution.Year));
@@ -3885,17 +3528,14 @@ procedure TSystemMeter.Save;
finally
CloseMHandler(DSS, SM_MHandle, Folder + CSVName, SM_Append);
- SM_MHandle := NIL;
end;
end;
procedure TSystemMeter.TakeSample;
-
begin
+ // Get total system energy out of the sources
- {Get total system energy out of the sources}
-
- cPower := CmulReal(GetTotalPowerFromSources(DSS), 0.001); // convert to kW
+ cPower := GetTotalPowerFromSources(DSS) * 0.001; // convert to kW
Integrate(kWh, cPower.re, dkwh);
Integrate(kvarh, cPower.im, dkvarh);
@@ -3903,9 +3543,9 @@ procedure TSystemMeter.TakeSample;
PeakkW := Max(cPower.re, PeakkW);
Peakkva := Max(Cabs(cPower), Peakkva);
- {Get total circuit losses}
+ // Get total circuit losses
cLosses := DSS.ActiveCircuit.Losses; // PD Elements except shunts
- cLosses := CmulReal(cLosses, 0.001); // convert to kW
+ cLosses := cLosses * 0.001; // convert to kW
Integrate(Losseskwh, cLosses.re, dLosseskwh);
Integrate(Losseskvarh, cLosses.im, dLosseskvarh);
@@ -3915,7 +3555,6 @@ procedure TSystemMeter.TakeSample;
FirstSampleAfterReset := FALSE;
if This_Meter_DIFileIsOpen then
WriteDemandIntervalData;
-
end;
procedure TEnergyMeter.CreateMeterTotals;
@@ -3981,7 +3620,7 @@ procedure TEnergyMeter.WriteTotalsFile;
Regsum: TRegisterArray;
i: Integer;
begin
- {Sum up all registers of all meters and write to Totals.CSV}
+ // Sum up all registers of all meters and write to Totals.csv
for i := 1 to NumEMRegisters do
RegSum[i] := 0.0;
@@ -4010,14 +3649,11 @@ procedure TEnergyMeter.WriteTotalsFile;
for i := 1 to NumEMRegisters do
WriteintoMem(FM_MHandle, Double(RegSum[i]));
WriteintoMemStr(FM_MHandle, Char(10));
- CloseMHandler(DSS, FM_MHandle, DI_Dir + PathDelim + 'Totals' + DSS._Name + '.CSV', FM_Append);
- FM_MHandle := NIL;
-
+ CloseMHandler(DSS, FM_MHandle, DI_Dir + PathDelim + 'Totals' + DSS._Name + '.csv', FM_Append);
except
On E: Exception do
- DoSimpleMsg('Error writing demand interval file Totals' + DSS._Name + '.CSV.' + CRLF + E.Message, 543);
+ DoSimpleMsg('Error writing demand interval file Totals%s.csv. %s', [DSS._Name, CRLF + E.Message], 543);
end;
-
end;
procedure TEnergyMeter.WriteVoltageReport;
@@ -4031,9 +3667,8 @@ procedure TEnergyMeter.WriteVoltageReport;
MinBus: Integer;
MaxBus: Integer;
BusCounted: Boolean;
-
begin
- {For any bus with a defined voltage base, test for > Vmax or < Vmin}
+ // For any bus with a defined voltage base, test for > Vmax or < Vmin
OverCount := 0;
UnderCount := 0;
@@ -4052,7 +3687,7 @@ procedure TEnergyMeter.WriteVoltageReport;
begin
for j := 1 to NumNodesThisBus do
begin
- Vmagpu := Cabs(Solution.NodeV^[GetRef(j)]) / kvbase * 0.001;
+ Vmagpu := Cabs(Solution.NodeV^[RefNo[j]]) / kvbase * 0.001;
if Vmagpu > 0.1 then
begin // ignore neutral buses
if Vmagpu < underVmin then
@@ -4087,7 +3722,7 @@ procedure TEnergyMeter.WriteVoltageReport;
end;
end;
end;
- end; {For i}
+ end; // For i
with Solution do
WriteintoMem(VR_MHandle, DynaVars.dblHour);
@@ -4098,8 +3733,8 @@ procedure TEnergyMeter.WriteVoltageReport;
WriteintoMemStr(VR_MHandle, ', ' + BusList.NameOfIndex(minbus));
WriteintoMemStr(VR_MHandle, ', ' + BusList.NameOfIndex(maxbus));
- // Klugy but it works
- // now repeat for buses under 1 kV
+ // Klugy but it works
+ // now repeat for buses under 1 kV
OverCount := 0;
UnderCount := 0;
MinBus := 0;
@@ -4115,7 +3750,7 @@ procedure TEnergyMeter.WriteVoltageReport;
begin
for j := 1 to NumNodesThisBus do
begin
- Vmagpu := Cabs(Solution.NodeV^[GetRef(j)]) / kvbase * 0.001;
+ Vmagpu := Cabs(Solution.NodeV^[RefNo[j]]) / kvbase * 0.001;
if Vmagpu > 0.1 then
begin // ignore neutral buses
if Vmagpu < underVmin then
@@ -4150,7 +3785,7 @@ procedure TEnergyMeter.WriteVoltageReport;
end;
end;
end;
- end; {For i}
+ end; // For i
WriteintoMemStr(VR_MHandle, ', ' + inttostr(UnderCount));
WriteintoMem(VR_MHandle, UnderVmin);
@@ -4160,30 +3795,16 @@ procedure TEnergyMeter.WriteVoltageReport;
WriteintoMemStr(VR_MHandle, ', ' + BusList.NameOfIndex(maxbus));
WriteintoMemStr(VR_MHandle, Char(10));
end;
-
-end;
-
-procedure TEnergyMeter.InterpretRegisterMaskArray(var Mask: TRegisterArray);
-
-var
- i, n: Integer;
-begin
- n := Parser.ParseAsVector(NumEMRegisters, pDoubleArray(@Mask));
- for i := n + 1 to NumEMRegisters do
- Mask[i] := 1.0; // Set the rest to 1
end;
procedure TEnergyMeter.OpenAllDIFiles;
-{Similar to Append, by creates the files.}
-
+// Similar to Append, by creates the files.
var
mtr: TEnergyMeterObj;
// Filenm:String;
begin
-
if FSaveDemandInterval then
begin
-
ClearDI_Totals; // clears accumulator arrays
mtr := DSS.ActiveCircuit.EnergyMeters.First;
@@ -4196,26 +3817,24 @@ procedure TEnergyMeter.OpenAllDIFiles;
SystemMeter.OpenDemandIntervalFile;
- {Optional Exception Reporting}
+ // Optional Exception Reporting
if Do_OverloadReport then
OpenOverloadReportFile;
if Do_VoltageExceptionReport then
OpenVoltageReportFile;
- {Open FDI_Totals}
+ // Open FDI_Totals
try
CreateFDI_Totals;
except
On E: Exception do
- DoSimpleMsg('Error creating the memory space for demand interval "' + Name + DSS._Name + '.CSV' + ' for appending.' + CRLF + E.Message, 538);
+ DoSimpleMsg('Error creating the memory space for demand interval "%s.csv" for appending.', [Name + DSS._Name, CRLF + E.Message], 538);
end;
DSS.DIFilesAreOpen := TRUE;
- end;{IF}
-
-
+ end;
end;
procedure TEnergyMeter.OpenOverloadReportFile;
@@ -4229,9 +3848,8 @@ procedure TEnergyMeter.OpenOverloadReportFile;
OV_MHandle := Create_Meter_Space('"Hour", "Element", "Normal Amps", "Emerg Amps", "% Normal", "% Emerg", "kVBase", "I1(A)", "I2(A)", "I3(A)"' + Char(10));
except
On E: Exception do
- DosimpleMsg('Error creating memory space (Overload report) for writing.' + CRLF + E.Message, 541);
+ DosimpleMsg('Error creating memory space (Overload report) for writing: %s', [E.Message], 541);
end;
-
end;
procedure TEnergyMeter.OpenVoltageReportFile;
@@ -4246,19 +3864,10 @@ procedure TEnergyMeter.OpenVoltageReportFile;
WriteintoMemStr(VR_MHandle, ', "LV Undervoltages", "Min LV Voltage", "LV Overvoltage", "Max LV Voltage", "Min LV Bus", "Max LV Bus"' + Char(10));
except
On E: Exception do
- DosimpleMsg('Error creating memory space (Voltage report) for writing.' + CRLF + E.Message, 541);
+ DosimpleMsg('Error creating memory space (Voltage report) for writing: %s', [E.Message], 541);
end;
-
end;
-initialization
-
- {RegisterNameList := TCommandList.Create(['kWh', 'kvarh', 'Max kW', 'Max kVA', 'Zone kWh',
- 'Zone kvarh', 'Zone Max kW','Zone Max kVA','Overload kWh Normal','Overload kWh Emerg','Load EEN',
- 'Load UE', 'Zone Losses kWh', 'Zone Losses kvarh', 'Zone Max kW Losses', 'Zone Max kvar Losses',
- 'Gen kWh', 'Gen kvarh', 'Gen Max kW', 'Gen Max kVA']); }
-
-
finalization
-
+ ActionEnum.Free;
end.
diff --git a/src/Meters/LD_fm_infos.pas b/src/Meters/LD_fm_infos.pas
deleted file mode 100644
index 3c8480088..000000000
--- a/src/Meters/LD_fm_infos.pas
+++ /dev/null
@@ -1,47 +0,0 @@
-unit LD_fm_infos;
-
-{
- ----------------------------------------------------------
- Copyright (c) 2017, Electric Power Research Institute, Inc.
- Added by Ying.
- ----------------------------------------------------------
-
- Definition of Fmonitor (virtue leader) Public Data Record
-}
-
-interface
-
-uses
- UComplex;
- //PointerList;
-
-type
-
- {Fmonitor public data/state variable structure}
- TLD_fm_infos = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}packed{$ENDIF} record
- //properties for Nodes
- // highest voltage node
- ndnum_hghst: Integer;
- b_ctrl_hghst: Boolean; //can contribute more to the high volt problem
- volt_hghst: Double; //low volt in pu
- volt_hgh_lmt: Double; //low limit in pu
- Pinjec_hghst: Double; //net P injection on this node
-
- // lowest voltage node
- ndnum_lwst: Integer;
- b_ctrl_lwst: Boolean; //can contribute more to the high volt problem
- volt_lwst: Double; //low volt in pu
- volt_lw_lmt: Double; //low limit in pu
- Pinjec_lwst: Double; // net P injection on this node
-
- // overview information
- volt_avg: Double;
- total_pg: Double; //total generation of this cluster
- total_pl: Double; //total load of this cluster
-
- b_Curt_Ctrl: Boolean;
- end;
-
-implementation
-
-end.
diff --git a/src/Meters/MemoryMap_lib.pas b/src/Meters/MemoryMap_lib.pas
index 5f52437c7..de006f60c 100644
--- a/src/Meters/MemoryMap_lib.pas
+++ b/src/Meters/MemoryMap_lib.pas
@@ -12,7 +12,8 @@
interface
uses
- Classes, DSSClass;
+ Classes,
+ DSSClass;
type
DoubleArray1d = array of Double;
@@ -25,7 +26,7 @@ interface
function Create_Meter_Space(Init_Str: String): TBytesStream; OVERLOAD;
procedure WriteintoMemStr(Mem_Space: TBytesStream; Content: String); OVERLOAD;
procedure WriteintoMem(Mem_Space: TBytesStream; Content: Double); OVERLOAD;
-procedure CloseMHandler(DSS: TDSSContext; Mem_Space: TBytesStream; const Dest_Path: String; AppendFile: Boolean); OVERLOAD;
+procedure CloseMHandler(DSS: TDSSContext; var Mem_Space: TBytesStream; const Dest_Path: String; AppendFile: Boolean);
procedure ReadMHandler(Mem_Space: TBytesStream; X_axis: pDoubleArray2d; Ylabels: pStringArray1d; Y_axis: pDoubleArray2d); OVERLOAD;
procedure Write_String(Mem_Space: TBytesStream; const Content: String);
@@ -36,6 +37,7 @@ implementation
windows,
{$ENDIF}
sysutils,
+ BufStream,
math,
DSSGlobals,
Utilities,
@@ -56,12 +58,8 @@ function Create_Meter_Space(Init_Str: String): TBytesStream; OVERLOAD;
wordBuf: Word;
begin
Mem_Space := TBytesStream.Create();
-{$IFNDEF FPC}
- Mem_Space.WriteData($01A0); // Header for identifying String type data
-{$ELSE}
wordBuf := $01A0;
Mem_Space.Write(wordBuf, 2);
-{$ENDIF}
;
Write_String(Mem_Space, Init_Str);
Result := Mem_Space;
@@ -73,13 +71,8 @@ procedure WriteintoMemStr(Mem_Space: TBytesStream; Content: String); OVERLOAD;
var
wordBuf: Word;
begin
-{$IFNDEF FPC}
- Mem_Space.WriteData($01A0); // Header for identifying String type data
-{$ELSE}
wordBuf := $01A0;
Mem_Space.Write(wordBuf, 2);
-{$ENDIF}
- ;
Write_String(Mem_Space, Content);
end;
//******************************************************************************
@@ -89,21 +82,15 @@ procedure WriteintoMem(Mem_Space: TBytesStream; Content: Double); OVERLOAD;
var
wordBuf: Word;
begin
-{$IFNDEF FPC}
- Mem_Space.WriteData($02A0); // Header for identifying a double type data
- Mem_Space.WriteData(Content);
-{$ELSE}
wordBuf := $02A0;
Mem_Space.Write(wordBuf, 2);
Mem_Space.Write(Content, sizeof(Double));
-{$ENDIF}
- ;
end;
//******************************************************************************
// Saves the content of the BytesStream into the specified file path
// and destroys the ByteStream
//******************************************************************************
-procedure CloseMHandler(DSS: TDSSContext; Mem_Space: TBytesStream; const Dest_Path: String; AppendFile: Boolean); OVERLOAD;
+procedure CloseMHandler(DSS: TDSSContext; var Mem_Space: TBytesStream; const Dest_Path: String; AppendFile: Boolean);
var
F: TFileStream = nil;
buffer: Uint8;
@@ -113,23 +100,23 @@ procedure CloseMHandler(DSS: TDSSContext; Mem_Space: TBytesStream; const Dest_Pa
MSize: Longint;
TVariableDbl: Double;
begin
-
-{ Open Output file; check for errors}
+ // Open Output file; check for errors
try
if AppendFile then
begin
- F := TFileStream.Create(Dest_path, fmOpenReadWrite);
+ F := TBufferedFileStream.Create(Dest_path, fmOpenReadWrite);
F.Seek(0, soEnd);
end
else
begin
- F := TFileStream.Create(Dest_path, fmCreate);
+ F := TBufferedFileStream.Create(Dest_path, fmCreate);
end;
except
On E: Exception do
begin
- DoSimpleMsg(DSS, 'Error Attempting to open file: "' + Dest_path + '. ' + E.Message, 159000);
+ DoSimpleMsg(DSS, 'Error Attempting to open file: "%s". %s', [Dest_path, E.Message], 159000);
FreeAndNil(F);
+ FreeAndNil(Mem_Space);
Exit;
end;
end;
@@ -205,6 +192,7 @@ procedure CloseMHandler(DSS: TDSSContext; Mem_Space: TBytesStream; const Dest_Pa
end;
finally // make sure we close the file
FreeAndNil(F);
+ FreeAndNil(Mem_Space);
end;
end;
//******************************************************************************
@@ -225,7 +213,6 @@ procedure ReadMHandler(Mem_Space: TBytesStream; X_axis: pDoubleArray2d;
dblXCounter: Integer;
begin
-
SetLength(X_axis^, 1, 0);
SetLength(Y_axis^, 1, 0);
SetLength(Ylabels^, 1);
@@ -355,26 +342,16 @@ procedure ReadMHandler(Mem_Space: TBytesStream; X_axis: pDoubleArray2d;
finally
end;
end;
-//******************************************************************************
+
// Writes the incomming String into the specified BytesStream
-//******************************************************************************
procedure Write_String(Mem_Space: TBytesStream; const Content: String);
var
- // Str_Sz : Integer;
idx: Integer;
begin
-
-{ Str_Sz := length(Content)-1;
- For idx := 0 to Str_Sz do Mem_Space.WriteData(Content[idx+1]);}
-{$IFNDEF FPC}
- for idx := 1 to length(Content) do
- Mem_Space.WriteData(Content[idx]);
-{$ELSE}
for idx := 1 to length(Content) do
Mem_Space.Write(Content[idx], Length(Content[idx])); // TODO - verify AnsiString vs. unicode
Mem_Space.WriteByte(0);
-{$ENDIF}
end;
end.
diff --git a/src/Meters/MeterClass.pas b/src/Meters/MeterClass.pas
index e3ea3a26a..a3f07c0a2 100644
--- a/src/Meters/MeterClass.pas
+++ b/src/Meters/MeterClass.pas
@@ -1,17 +1,10 @@
unit MeterClass;
-
{
----------------------------------------------------------
Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
All rights reserved.
----------------------------------------------------------
}
-{
- Base for Meter classes
-}
-
-
-{$M+}
interface
@@ -21,42 +14,32 @@ interface
type
TMeterClass = class(TCktElementClass)
- PRIVATE
-
PROTECTED
- procedure ClassEdit(const ActiveMeterObj: Pointer; const ParamPointer: Integer);
- procedure ClassMakeLike(const OtherObj: Pointer);
-
- procedure CountProperties; // Add no. of intrinsic properties
- procedure DefineProperties; // Add Properties of this class to propName
-
+ procedure CountPropertiesAndAllocate; override;
+ procedure DefineProperties; override;
PUBLIC
- NumMeterClassProps: Integer;
- constructor Create(dssContext: TDSSContext);
+ constructor Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
destructor Destroy; OVERRIDE;
procedure ResetAll; VIRTUAL;
procedure SampleAll; VIRTUAL; // Force all monitors to take a sample
procedure SaveAll; VIRTUAL; // Force all monitors to save their buffers to disk
- PUBLISHED
-
end;
-
implementation
uses
MeterElement,
- ParserDel,
DSSClassDefs,
DSSGlobals;
-constructor TMeterClass.Create(dssContext: TDSSContext);
-begin
+const
+ NumPropsThisClass = 0;
- inherited Create(dssContext);
- NumMeterClassProps := 0;
- DSSClassType := METER_ELEMENT;
+constructor TMeterClass.Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
+begin
+ inherited Create(dssContext, DSSClsType or METER_ELEMENT, DSSClsName);
+ ClassParents.Add('MeterClass');
end;
destructor TMeterClass.Destroy;
@@ -65,57 +48,19 @@ destructor TMeterClass.Destroy;
inherited Destroy;
end;
-procedure TMeterClass.CountProperties;
+procedure TMeterClass.CountPropertiesAndAllocate;
begin
- NumProperties := NumProperties + NumMeterClassProps;
- inherited CountProperties;
+ NumProperties := NumProperties + NumPropsThisClass;
+ inherited CountPropertiesAndAllocate;
end;
procedure TMeterClass.DefineProperties;
-
-// Define the properties for the base power delivery element class
-
begin
- // no properties
- // PropertyName^[ActiveProperty + 1] := 'propname';
- // PropertyHelp^[ActiveProperty + 1] := 'prop help';
-
- ActiveProperty := ActiveProperty + NumMeterClassProps;
-
+ // no properties
+ ActiveProperty := ActiveProperty + NumPropsThisClass;
inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TMeterClass.ClassEdit(const ActiveMeterObj: Pointer; const ParamPointer: Integer);
-begin
- // continue parsing with contents of Parser
- if ParamPointer > 0 then
- with TMeterElement(ActiveMeterObj) do
- begin
-
- //CASE ParamPointer OF
- //1: BaseFrequency := Parser.Dblvalue;
- //ELSE
- inherited ClassEdit(ActiveMeterObj, ParamPointer - NumMeterClassProps)
- //END;
- end;
-end;
-
-procedure TMeterClass.ClassMakeLike(const OtherObj: Pointer);
-
-//Var
-// OtherMeterObj : TMeterElement;
-begin
-
-// OtherMeterObj := TMeterElement(OtherObj);
- TMeterElement.Create(OtherObj);
- //With TPCElement(ActiveDSSObject) Do
- //Begin
- // value:= OtherMeterObj.value;
- //End;
-
-end;
-
procedure TMeterClass.ResetAll;
begin
DoSimpleMsg('Programming Error: Base MeterClass.ResetAll Reached for Class: ' + Name, 760);
diff --git a/src/Meters/MeterElement.pas b/src/Meters/MeterElement.pas
index 0fa6f10d1..c9575606f 100644
--- a/src/Meters/MeterElement.pas
+++ b/src/Meters/MeterElement.pas
@@ -12,7 +12,7 @@ interface
uses
CktElement,
Bus,
- ucomplex,
+ UComplex, DSSUcomplex,
DSSClass,
Arraydef;
@@ -20,8 +20,6 @@ interface
TMeterElement = class(TDSSCktElement)
PUBLIC
-
- ElementName: String;
MeteredElement: TDSSCktElement; // Pointer to target circuit element
MeteredTerminal: Integer;
MeteredElementChanged: Boolean;
@@ -41,7 +39,6 @@ TMeterElement = class(TDSSCktElement)
procedure CalcAllocationFactors;
end;
-
implementation
uses
@@ -89,7 +86,6 @@ constructor TMeterElement.Create(ParClass: TDSSClass);
inherited Create(ParClass);
DSSObjType := METER_ELEMENT;
- ElementName := '';
MeteredElement := NIL;
MeteredTerminal := 1;
SensorCurrent := NIL;
@@ -97,7 +93,6 @@ constructor TMeterElement.Create(ParClass: TDSSClass);
PhsAllocationFactor := NIL;
CalculatedCurrent := NIL;
CalculatedVoltage := NIL;
-
end;
destructor TMeterElement.Destroy;
@@ -120,7 +115,7 @@ destructor TMeterElement.Destroy;
procedure TMeterElement.TakeSample;
begin
// virtual function - should be overridden
- DoSimpleMsg('Programming Error: Reached base Meterelement class for TakeSample.' + CRLF + 'Device: ' + Name, 723);
+ DoSimpleMsg('Programming Error: Reached base Meterelement class for TakeSample.' + CRLF + 'Device: ' + Name, 723);
end;
end.
diff --git a/src/Meters/Monitor.pas b/src/Meters/Monitor.pas
index 4ca87eb76..18adcdb98 100644
--- a/src/Meters/Monitor.pas
+++ b/src/Meters/Monitor.pas
@@ -12,88 +12,73 @@
// official one. Compatibility could be added if required later,
// adding extra spaces before most fields.
-{
- Change Log
- 12-7-99 Modified Getcurrents override
- 1-22-00 Derived from MeterElement Class
- 5-30-00 Added test for positive sequence ckt model
- Fixed resetting of Nphases to match metered element
- 10-27-00 Changed default to magnitude and angle instead of real and imag
- 12-18-01 Added Transformer Tap Monitor Code
- 12-18-02 Added Monitor Stream
- 2-19-08 Added SampleCount
- 01-19-13 Added flicker meter mode
- 08-18-15 Added Solution monitor mode
- 08-10-16 Added mode 6 for storing capacitor switching
- 06-04-18 Added modes 7-9
- 11-29-18 Added mode 10; revised mode 8
- 12-4-18 Added link to AutoTransformer
- 08-21-20 Added mode 11
-}
-
-{
- A monitor is a circuit element that is connected to a terminal of another
- circuit element. It records the voltages and currents at that terminal as
- a function of time and can report those values upon demand.
-
- A Monitor is defined by a New commands:
-
- New Type=Monitor Name=myname Element=elemname Terminal=[1,2,...] Buffer=clear|save
-
- Upon creation, the monitor buffer is established. There is a file associated
- with the buffer. It is named "Mon_elemnameN.mon" where N is the terminal no.
- The file is truncated to zero at creation or buffer clearing.
-
- The Monitor keeps results in the in-memory buffer until it is filled. Then it
- appends the buffer to the associated file and resets the in-memory buffer.
-
- For buffer=save, the present in-memory buffer is appended to the disk file so
- that it is saved for later reference.
-
- The Monitor is a passive device that takes a sample whenever its "TakeSample"
- method is invoked. The SampleAll method of the Monitor ckt element class will
- force all monitors elements to take a sample. If the present time (for the most
- recent solution is greater than the last time entered in to the monitor buffer,
- the sample is appended to the buffer. Otherwise, it replaces the last entry.
-
- Monitor Files are simple binary files of singles. The first record
- contains the number of conductors per terminal (NCond). (always use 'round' function
- when converting this to an integer). Then subsequent records consist of time and
- voltage and current samples for each terminal (all complex doubles) in the order
- shown below:
-
-
- <--- All voltages first ---------------->|<--- All currents ----->|
- .... ...
- .... ...
- .... ...
-
- The time values will not necessarily be in a uniform time step; they will
- be at times samples or solutions were taken. This could vary from several
- hours down to a few milliseconds.
-
- The monitor ID can be determined from the file name. Thus, these values can
- be post-processed at any later time, provided that the monitors are not reset.
-
- Modes are:
- 0: Standard mode - V and I,each phase, Mag and Angle
- 1: Power each phase, complex (kw and kvars)
- 2: Transformer Tap
- 3: State Variables
- 4: Flicker level and severity index by phase (no modifiers apply)
- 5: Solution Variables (Iteration count, etc.)
- 6: Capacitor Switching (Capacitors only)
- 7: Storage Variables
- 8: Transformer Winding Currents
- 9: Losses (watts and vars)
- 10: Transformer Winding Voltages (across winding)
- 11: All terminal V and I, all conductors, mag and angle
-
- +16: Sequence components: V012, I012
- +32: Magnitude Only
- +64: Pos Seq only or Average of phases
-}
+// 06-04-18 Added modes 7-9
+// 11-29-18 Added mode 10; revised mode 8
+// 12-4-18 Added link to AutoTransformer
+// 08-21-20 Added mode 11
+
+
+// A monitor is a circuit element that is connected to a terminal of another
+// circuit element. It records the voltages and currents at that terminal as
+// a function of time and can report those values upon demand.
+//
+// A Monitor is defined by a New commands:
+//
+// New Type=Monitor Name=myname Element=elemname Terminal=[1,2,...] Buffer=clear|save
+//
+// Upon creation, the monitor buffer is established. There is a file associated
+// with the buffer. It is named "Mon_elemnameN.mon" where N is the terminal no.
+// The file is truncated to zero at creation or buffer clearing.
+//
+// The Monitor keeps results in the in-memory buffer until it is filled. Then it
+// appends the buffer to the associated file and resets the in-memory buffer.
+//
+// For buffer=save, the present in-memory buffer is appended to the disk file so
+// that it is saved for later reference.
+//
+// The Monitor is a passive device that takes a sample whenever its "TakeSample"
+// method is invoked. The SampleAll method of the Monitor ckt element class will
+// force all monitors elements to take a sample. If the present time (for the most
+// recent solution is greater than the last time entered in to the monitor buffer,
+// the sample is appended to the buffer. Otherwise, it replaces the last entry.
+//
+// Monitor Files are simple binary files of singles. The first record
+// contains the number of conductors per terminal (NCond). (always use 'round' function
+// when converting this to an integer). Then subsequent records consist of time and
+// voltage and current samples for each terminal (all complex doubles) in the order
+// shown below:
+//
+//
+// <--- All voltages first ---------------->|<--- All currents ----->|
+// .... ...
+// .... ...
+// .... ...
+//
+// The time values will not necessarily be in a uniform time step; they will
+// be at times samples or solutions were taken. This could vary from several
+// hours down to a few milliseconds.
+//
+// The monitor ID can be determined from the file name. Thus, these values can
+// be post-processed at any later time, provided that the monitors are not reset.
+//
+// Modes are:
+// 0: Standard mode - V and I,each phase, Mag and Angle
+// 1: Power each phase, complex (kw and kvars)
+// 2: Transformer Tap
+// 3: State Variables
+// 4: Flicker level and severity index by phase (no modifiers apply)
+// 5: Solution Variables (Iteration count, etc.)
+// 6: Capacitor Switching (Capacitors only)
+// 7: Storage Variables
+// 8: Transformer Winding Currents
+// 9: Losses (watts and vars)
+// 10: Transformer Winding Voltages (across winding)
+// 11: All terminal V and I, all conductors, mag and angle
+//
+// +16: Sequence components: V012, I012
+// +32: Magnitude Only
+// +64: Pos Seq only or Average of phases
interface
@@ -103,26 +88,36 @@ interface
Meterelement,
DSSClass,
Arraydef,
- ucomplex,
+ UComplex, DSSUcomplex,
utilities,
Classes;
type
+{$SCOPEDENUMS ON}
+ TMonitorProp = (
+ INVALID = 0,
+ element = 1, // TODO: has specific type according to the current mode
+ terminal = 2,
+ mode = 3,
+ action = 4, // buffer=clear|save
+ residual = 5, // buffer=clear|save
+ VIPolar = 6, // V I in mag and angle rather then re and im
+ PPolar = 7 // Power in power PF rather then power and vars
+ );
+{$SCOPEDENUMS OFF}
+
TLegacyMonitorStrBuffer = array[1..256] of AnsiChar;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TDSSMonitor = class(TMeterClass)
- PRIVATE
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const MonitorName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function BeginEdit(ptr: Pointer; SetActive_: Boolean=True): Pointer; override;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
procedure ResetAll; OVERRIDE;
procedure SampleAll; OVERRIDE; // Force all monitors to take a sample
@@ -132,7 +127,6 @@ TDSSMonitor = class(TMeterClass)
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TMonitorObj = class(TMeterElement)
PRIVATE
BufferSize: Integer;
@@ -158,13 +152,13 @@ TMonitorObj = class(TMeterElement)
SolutionBuffer: pDoubleArray;
- IncludeResidual: Boolean;
- VIpolar: Boolean;
- Ppolar: Boolean;
+ IncludeResidual: LongBool;
+ VIpolar: LongBool;
+ Ppolar: LongBool;
FileSignature: Integer;
- BaseFrequency: Double;
+ // BaseFrequency: Double; -- duplicated
BufferFile: String; // Name of file for catching buffer overflow
@@ -172,6 +166,8 @@ TMonitorObj = class(TMeterElement)
ValidMonitor: Boolean;
IsProcessed: Boolean;
+ recalc: Int8; // Used in Edit
+
procedure AddDblsToBuffer(Dbl: pDoubleArray; Ndoubles: Integer);
procedure AddDblToBuffer(const Dbl: Double);
@@ -187,11 +183,11 @@ TMonitorObj = class(TMeterElement)
RecordSize: Integer;
FileVersion: Integer;
-
constructor Create(ParClass: TDSSClass; const MonitorName: String);
destructor Destroy; OVERRIDE;
+ procedure MakeLike(OtherPtr: Pointer); override;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model, reset nphases
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model, reset nphases
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE; // Always Zero for a monitor
procedure TakeSample; OVERRIDE; // Go add a sample to the buffer
@@ -206,19 +202,16 @@ TMonitorObj = class(TMeterElement)
procedure TranslateToCSV(Show: Boolean);
procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present value of terminal Curr
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
//Property MonitorFileName:String read BufferFile;
property CSVFileName: String READ Get_FileName;
end;
-
-{--------------------------------------------------------------------------}
implementation
uses
- ParserDel,
+ BufStream,
DSSClassDefs,
DSSGlobals,
Circuit,
@@ -239,203 +232,139 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TMonitorObj;
+ TProp = TMonitorProp;
+{$PUSH}
+{$Z4} // keep enums as int32 values
+ TMonitorAction = (
+ Clear = 0,
+ Save = 1,
+ Take = 2,
+ Process = 3
+ );
+{$POP}
const
+ NumPropsThisClass = Ord(High(TProp));
+
SEQUENCEMASK = 16;
MAGNITUDEMASK = 32;
POSSEQONLYMASK = 64;
MODEMASK = 15;
- NumPropsThisClass = 7;
NumSolutionVars = 12;
var
EMPTY_LEGACY_HEADER: TLegacyMonitorStrBuffer;
+ PropInfo: Pointer = NIL;
+ ActionEnum: TDSSEnum;
-{--------------------------------------------------------------------------}
-constructor TDSSMonitor.Create(dssContext: TDSSContext); // Creates superstructure for all Monitor objects
+constructor TDSSMonitor.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
-
- Class_name := 'Monitor';
- DSSClassType := DSSClassType + MON_ELEMENT;
-
- DefineProperties;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ ActionEnum := TDSSEnum.Create('Monitor: Action', True, 1, 1,
+ ['Clear', 'Save', 'Take', 'Process', 'Reset'],
+ [ord(TMonitorAction.Clear), ord(TMonitorAction.Save), ord(TMonitorAction.Take), ord(TMonitorAction.Process), ord(TMonitorAction.Clear)]);
+ end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, MON_ELEMENT, 'Monitor');
end;
-{--------------------------------------------------------------------------}
destructor TDSSMonitor.Destroy;
-
begin
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TDSSMonitor.DefineProperties;
+procedure DoAction(obj: TObj; action: TMonitorAction);
begin
+ case action of
+ TMonitorAction.Save:
+ Obj.Save;
+ TMonitorAction.Clear:
+ Obj.ResetIt;
+ TMonitorAction.Take:
+ Obj.TakeSample;
+ TMonitorAction.Process:
+ begin
+ Obj.PostProcess;
+ dec(Obj.recalc)
+ end
+ end;
+end;
+procedure TDSSMonitor.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
+begin
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
-
- PropertyName[1] := 'element';
- PropertyName[2] := 'terminal';
- PropertyName[3] := 'mode';
- PropertyName[4] := 'action'; // buffer=clear|save
- PropertyName[5] := 'residual'; // buffer=clear|save
- PropertyName[6] := 'VIPolar'; // V I in mag and angle rather then re and im
- PropertyName[7] := 'PPolar'; // Power in power PF rather then power and vars
-
- PropertyHelp[1] := 'Name (Full Object name) of element to which the monitor is connected.';
- PropertyHelp[2] := 'Number of the terminal of the circuit element to which the monitor is connected. ' +
- '1 or 2, typically. For monitoring states, attach monitor to terminal 1.';
- PropertyHelp[3] := 'Bitmask integer designating the values the monitor is to capture: ' + CRLF +
- '0 = Voltages and currents at designated terminal' + CRLF+
- '1 = Powers at designated terminal'+CRLF+
- '2 = Tap Position (Transformer Device only)' + CRLF +
- '3 = State Variables (PCElements only)' + CRLF +
- '4 = Flicker level and severity index (Pst) for voltages. No adders apply.' + CRLF +
- ' Flicker level at simulation time step, Pst at 10-minute time step.' + CRLF +
- '5 = Solution variables (Iterations, etc).' + CRLF +
- 'Normally, these would be actual phasor quantities from solution.' + CRLF +
- '6 = Capacitor Switching (Capacitors only)' + CRLF +
- '7 = Storage state vars (Storage device only)' + CRLF +
- '8 = All winding currents (Transformer device only)' + CRLF +
- '9 = Losses, watts and var (of monitored device)' + CRLF +
- '10 = All Winding voltages (Transformer device only)' + CRLF +
- '11 = All terminal node voltages and line currents of monitored device' + CRLF +
- '12 = All terminal node voltages LL and line currents of monitored device' + CRLF +
- 'Normally, these would be actual phasor quantities from solution.' + CRLF +
- 'Combine mode with adders below to achieve other results for terminal quantities:' + CRLF +
- '+16 = Sequence quantities' + CRLF +
- '+32 = Magnitude only' + CRLF +
- '+64 = Positive sequence only or avg of all phases' + CRLF +
- CRLF +
- 'Mix adder to obtain desired results. For example:' + CRLF +
- 'Mode=112 will save positive sequence voltage and current magnitudes only' + CRLF +
- 'Mode=48 will save all sequence voltages and currents, but magnitude only.';
- PropertyHelp[4] := '{Clear | Save | Take | Process}' + CRLF +
- '(C)lears or (S)aves current buffer.' + CRLF +
- '(T)ake action takes a sample.' + CRLF +
- '(P)rocesses the data taken so far (e.g. Pst for mode 4).' + CRLF + CRLF +
- 'Note that monitors are automatically reset (cleared) when the Set Mode= command is issued. ' +
- 'Otherwise, the user must explicitly reset all monitors (reset monitors command) or individual ' +
- 'monitors with the Clear action.';
- PropertyHelp[5] := '{Yes/True | No/False} Default = No. Include Residual cbannel (sum of all phases) for voltage and current. ' +
- 'Does not apply to sequence quantity modes or power modes.';
- PropertyHelp[6] := '{Yes/True | No/False} Default = YES. Report voltage and current in polar form (Mag/Angle). (default) Otherwise, it will be real and imaginary.';
- PropertyHelp[7] := '{Yes/True | No/False} Default = YES. Report power in Apparent power, S, in polar form (Mag/Angle).(default) Otherwise, is P and Q';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // boolean properties
+ PropertyType[ord(TProp.residual)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.VIpolar)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.Ppolar)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.residual)] := ptruint(@obj.IncludeResidual);
+ PropertyOffset[ord(TProp.VIpolar)] := ptruint(@obj.VIpolar);
+ PropertyOffset[ord(TProp.Ppolar)] := ptruint(@obj.Ppolar);
+
+ // integer properties
+ PropertyType[ord(TProp.terminal)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.mode)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.terminal)] := ptruint(@obj.MeteredTerminal);
+ PropertyOffset[ord(TProp.mode)] := ptruint(@obj.Mode);
+
+ // object reference
+ PropertyType[ord(TProp.element)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.element)] := ptruint(@obj.MeteredElement);
+ PropertyOffset2[ord(TProp.element)] := 0;
+ //PropertyFlags[ord(TProp.element)] := [TPropertyFlag.CheckForVar]; // not required for general cktelements
+
+ // enum action
+ PropertyType[ord(TProp.Action)] := TPropertyType.StringEnumActionProperty;
+ PropertyOffset[ord(TProp.Action)] := ptruint(@DoAction);
+ PropertyOffset2[ord(TProp.Action)] := PtrInt(ActionEnum);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ inherited DefineProperties;
end;
-{--------------------------------------------------------------------------}
-function TDSSMonitor.NewObject(const ObjName: String): Integer;
+function TDSSMonitor.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new Monitor and add it to Monitor class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TMonitorObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-{--------------------------------------------------------------------------}
-function TDSSMonitor.Edit: Integer;
+function TDSSMonitor.BeginEdit(ptr: Pointer; SetActive_: Boolean): Pointer;
var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
- recalc: Integer;
-
+ Obj: TObj;
begin
+ Obj := TObj(inherited BeginEdit(ptr, SetActive_));
+ Obj.recalc := 0;
+ Result := Obj;
+end;
- // continue parsing with contents of Parser
- DSS.ActiveMonitorObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveMonitorObj;
-
- Result := 0;
- recalc := 0;
-
- with DSS.ActiveMonitorObj do
+function TDSSMonitor.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
- inc(recalc);
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 661);
- 1:
- begin
- ElementName := ConstructElemName(DSS, lowercase(param)); // subtitute @var values if any
- PropertyValue[1] := ElementName;
- end;
- 2:
- MeteredTerminal := Parser.IntValue;
- 3:
- Mode := Parser.IntValue;
- 4:
- begin
- param := lowercase(param);
- case param[1] of
- 's':
- Save;
- 'c', 'r':
- ResetIt;
- 't':
- TakeSample;
- 'p':
- begin
- PostProcess;
- dec(recalc)
- end
- end;
- end; // buffer
- 5:
- IncludeResidual := InterpretYesNo(Param);
- 6:
- VIpolar := InterpretYesNo(Param);
- 7:
- Ppolar := InterpretYesNo(Param);
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveMonitorObj, ParamPointer - NumPropsthisClass)
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
- if recalc > 0 then
+ if (NumChanges + recalc) > 0 then
RecalcElementData;
+ Exclude(Flags, Flg.EditionActive);
end;
-
+ Result := True;
end;
-{--------------------------------------------------------------------------}
procedure TDSSMonitor.ResetAll; // Force all monitors in the circuit to reset
-
var
Mon: TMonitorObj;
-
begin
Mon := ActiveCircuit.Monitors.First;
while Mon <> NIL do
@@ -444,15 +373,12 @@ procedure TDSSMonitor.ResetAll; // Force all monitors in the circuit to reset
Mon.ResetIt;
Mon := ActiveCircuit.Monitors.Next;
end;
-
end;
-{--------------------------------------------------------------------------}
procedure TDSSMonitor.SampleAll; // Force all monitors in the circuit to take a sample
-
var
Mon: TMonitorObj;
-// sample all monitors except mode 5 monitors
+ // sample all monitors except mode 5 monitors
begin
Mon := ActiveCircuit.Monitors.First;
while Mon <> NIL do
@@ -464,12 +390,10 @@ procedure TDSSMonitor.SampleAll; // Force all monitors in the circuit to take a
end;
end;
-{--------------------------------------------------------------------------}
procedure TDSSMonitor.SampleAllMode5; // Force all mode=5 monitors in the circuit to take a sample
-
var
Mon: TMonitorObj;
-// sample all Mode 5 monitors except monitors
+ // sample all Mode 5 monitors except monitors
begin
Mon := ActiveCircuit.Monitors.First;
while Mon <> NIL do
@@ -481,7 +405,6 @@ procedure TDSSMonitor.SampleAllMode5; // Force all mode=5 monitors in the circu
end;
end;
-{--------------------------------------------------------------------------}
procedure TDSSMonitor.PostProcessAll;
var
Mon: TMonitorObj;
@@ -495,9 +418,7 @@ procedure TDSSMonitor.PostProcessAll;
end;
end;
-{--------------------------------------------------------------------------}
procedure TDSSMonitor.SaveAll; // Force all monitors in the circuit to save their buffers to disk
-
var
Mon: TMonitorObj;
@@ -511,58 +432,35 @@ procedure TDSSMonitor.SaveAll; // Force all monitors in the circuit to save
end;
end;
-{--------------------------------------------------------------------------}
-function TDSSMonitor.MakeLike(const MonitorName: String): Integer;
+procedure TMonitorObj.MakeLike(OtherPtr: Pointer);
var
- OtherMonitor: TMonitorObj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this Monitor name in the present collection}
- OtherMonitor := Find(MonitorName);
- if OtherMonitor <> NIL then
- with DSS.ActiveMonitorObj do
- begin
-
- NPhases := OtherMonitor.Fnphases;
- NConds := OtherMonitor.Fnconds; // Force Reallocation of terminal stuff
-
- Buffersize := OtherMonitor.Buffersize;
- ElementName := OtherMonitor.ElementName;
- MeteredElement := OtherMonitor.MeteredElement; // Pointer to target circuit element
- MeteredTerminal := OtherMonitor.MeteredTerminal;
- Mode := OtherMonitor.Mode;
- IncludeResidual := OtherMonitor.IncludeResidual;
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherMonitor.PropertyValue[i];
-
- BaseFrequency := OtherMonitor.BaseFrequency;
-
- end
- else
- DoSimpleMsg('Error in Monitor MakeLike: "' + MonitorName + '" Not Found.', 662);
-
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
+
+ Buffersize := Other.Buffersize;
+ MeteredElement := Other.MeteredElement; // Pointer to target circuit element
+ MeteredTerminal := Other.MeteredTerminal;
+ Mode := Other.Mode;
+ IncludeResidual := Other.IncludeResidual;
+
+ BaseFrequency := Other.BaseFrequency;
end;
-{==========================================================================}
-{ TMonitorObj }
-{==========================================================================}
-
-
-{--------------------------------------------------------------------------}
constructor TMonitorObj.Create(ParClass: TDSSClass; const MonitorName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(MonitorName);
+ Name := AnsiLowerCase(MonitorName);
- Nphases := 3; // Directly set conds and phases
+ FNphases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 1; // this forces allocation of terminals and conductors
// in base class
- {Current Buffer has to be big enough to hold all terminals}
+ // Current Buffer has to be big enough to hold all terminals
CurrentBuffer := NIL;
VoltageBuffer := NIL;
StateBuffer := NIL;
@@ -584,8 +482,7 @@ constructor TMonitorObj.Create(ParClass: TDSSClass; const MonitorName: String);
MonBuffer := AllocMem(Sizeof(MonBuffer^[1]) * BufferSize);
BufPtr := 0;
- ElementName := TDSSCktElement(ActiveCircuit.CktElements.Get(1)).Name; // Default to first circuit element (source)
- MeteredElement := NIL;
+ MeteredElement := TDSSCktElement(ActiveCircuit.CktElements.Get(1)); // Default to first circuit element (source)
Bufferfile := '';
MonitorStream := TMemoryStream.Create; // Create memory stream
@@ -603,16 +500,12 @@ constructor TMonitorObj.Create(ParClass: TDSSClass; const MonitorName: String);
IsProcessed := FALSE;
DSSObjType := ParClass.DSSClassType; //MON_ELEMENT;
-
- InitPropertyValues(0);
-
end;
destructor TMonitorObj.Destroy;
begin
MonitorStream.Free;
Header.Free;
- ElementName := '';
Bufferfile := '';
ReAllocMem(MonBuffer, 0);
ReAllocMem(StateBuffer, 0);
@@ -627,12 +520,9 @@ destructor TMonitorObj.Destroy;
inherited Destroy;
end;
-
-{--------------------------------------------------------------------------}
procedure ConvertBlanks(var s: String);
var
BlankPos: Integer;
-
begin
{ Convert spaces to Underscores }
BlankPos := Pos(' ', S);
@@ -643,78 +533,68 @@ procedure ConvertBlanks(var s: String);
end;
end;
-{--------------------------------------------------------------------------}
procedure TMonitorObj.RecalcElementData;
-
-var
- DevIndex: Integer;
-
begin
ValidMonitor := FALSE;
- Devindex := GetCktElementIndex(ElementName); // Global function
- if DevIndex > 0 then
- begin // Monitored element must already exist
- MeteredElement := ActiveCircuit.CktElements.Get(DevIndex);
+ if MeteredElement <> NIL then
+ begin // Monitored element must already exist
case (Mode and MODEMASK) of
2, 8, 10:
- begin // Must be transformer
+ begin // Must be transformer
if (MeteredElement.DSSObjType and CLASSMASK) <> XFMR_ELEMENT then
if (MeteredElement.DSSObjType and CLASSMASK) <> AUTOTRANS_ELEMENT then
begin
- DoSimpleMsg(MeteredElement.Name + ' is not a transformer!', 663);
+ DoSimpleMsg('%s is not a transformer!', [MeteredElement.Name], 663);
Exit;
end;
end;
3:
- begin // Must be PCElement
+ begin // Must be PCElement
if (MeteredElement.DSSObjType and BASECLASSMASK) <> PC_ELEMENT then
begin
- DoSimpleMsg(MeteredElement.Name + ' must be a power conversion element (Load or Generator)!', 664);
+ DoSimpleMsg('%s must be a power conversion element (Load or Generator)!', [MeteredElement.Name], 664);
Exit;
end;
end;
6:
- begin // Checking Caps Tap
+ begin // Checking Caps Tap
if (MeteredElement.DSSObjType and CLASSMASK) <> CAP_ELEMENT then
begin
- DoSimpleMsg(MeteredElement.Name + ' is not a capacitor!', 2016001);
+ DoSimpleMsg('%s is not a capacitor!', [MeteredElement.Name], 2016001);
Exit;
end;
end;
7:
- begin // Checking if the element is a storage device
+ begin // Checking if the element is a storage device
if ((MeteredElement.DSSObjType and CLASSMASK) <> STORAGE_ELEMENT) then
begin
- DoSimpleMsg(MeteredElement.Name + ' is not a storage device!', 2016002);
+ DoSimpleMsg('%s is not a storage device!', [MeteredElement.Name], 2016002);
Exit;
end;
end;
-
-
end;
if MeteredTerminal > MeteredElement.Nterms then
begin
- DoErrorMsg('Monitor: "' + Name + '"',
- 'Terminal no. "' + '" does not exist.',
- 'Respecify terminal no.', 665);
+ DoErrorMsg(
+ Format(_('Monitor: "%s"'), [Name]),
+ Format(_('Terminal no. "%d" does not exist.'), [MeteredTerminal]),
+ _('Respecify terminal no.'), 665);
end
else
begin
- Nphases := MeteredElement.NPhases;
+ FNphases := MeteredElement.NPhases;
Nconds := MeteredElement.NConds;
- // Sets name of i-th terminal's connected bus in monitor's buslist
- // This value will be used to set the NodeRef array (see TakeSample)
+ // Sets name of i-th terminal's connected bus in monitor's buslist
+ // This value will be used to set the NodeRef array (see TakeSample)
Setbus(1, MeteredElement.GetBus(MeteredTerminal));
- // Make a name for the Buffer File
+ // Make a name for the Buffer File
BufferFile := {ActiveCircuit.CurrentDirectory + }
DSS.CircuitName_ + 'Mon_' + Name + '.mon';
- // removed 10/19/99 ConvertBlanks(BufferFile); // turn blanks into '_'
-
- {Allocate Buffers}
+ // Allocate Buffers
case (Mode and MODEMASK) of
3:
begin
@@ -733,20 +613,20 @@ procedure TMonitorObj.RecalcElementData;
begin
if (MeteredElement.DSSObjType and CLASSMASK) = AUTOTRANS_ELEMENT then
with TAutoTransObj(MeteredElement) do
- NumTransformerCurrents := 2 * NumberOfWindings * nphases
+ NumTransformerCurrents := 2 * NumWindings * nphases
else
with TTransfObj(MeteredElement) do
- NumTransformerCurrents := 2 * NumberOfWindings * nphases;
+ NumTransformerCurrents := 2 * NumWindings * nphases;
ReallocMem(WdgCurrentsBuffer, Sizeof(Complex) * NumTransformerCurrents);
end;
10:
begin
if (MeteredElement.DSSObjType and CLASSMASK) = AUTOTRANS_ELEMENT then
with TAutoTransObj(MeteredElement) do
- NumWindingVoltages := NumberOfWindings * nphases
+ NumWindingVoltages := NumWindings * nphases
else
with TTransfObj(MeteredElement) do
- NumWindingVoltages := NumberOfWindings * nphases;
+ NumWindingVoltages := NumWindings * nphases;
ReallocMem(WdgVoltagesBuffer, Sizeof(Complex) * NumWindingVoltages); // total all phases, all windings
ReallocMem(PhsVoltagesBuffer, Sizeof(Complex) * nphases);
end;
@@ -765,29 +645,26 @@ procedure TMonitorObj.RecalcElementData;
ReallocMem(VoltageBuffer, SizeOf(VoltageBuffer^[1]) * MeteredElement.NConds);
end;
-
-
ClearMonitorStream;
ValidMonitor := TRUE;
-
end;
-
end
else
begin
- MeteredElement := NIL; // element not found
- DoErrorMsg('Monitor: "' + Self.Name + '"', 'Circuit Element "' + ElementName + '" Not Found.',
- ' Element must be defined previously.', 666);
+ // element not found/set
+ DoErrorMsg(Format(_('Monitor: "%s"'), [Self.Name]),
+ _('Circuit Element is not set.'),
+ _('Element must be defined previously.'), 666);
end;
end;
-procedure TMonitorObj.MakePosSequence;
+procedure TMonitorObj.MakePosSequence();
begin
if MeteredElement <> NIL then
begin
Setbus(1, MeteredElement.GetBus(MeteredTerminal));
- Nphases := MeteredElement.NPhases;
+ FNphases := MeteredElement.NPhases;
Nconds := MeteredElement.Nconds;
case (Mode and MODEMASK) of
3:
@@ -813,17 +690,14 @@ procedure TMonitorObj.MakePosSequence;
inherited;
end;
-
-{--------------------------------------------------------------------------}
procedure TMonitorObj.CalcYPrim;
begin
+ // A Monitor is a zero current source; Yprim is always zero.
- {A Monitor is a zero current source; Yprim is always zero.}
// leave YPrims as nil and they will be ignored
// Yprim is zeroed when created. Leave it as is.
end;
-{--------------------------------------------------------------------------}
procedure TMonitorObj.ClearMonitorStream;
var
PhaseLoc: Array of Integer;
@@ -856,12 +730,12 @@ procedure TMonitorObj.ClearMonitorStream;
2:
begin
- RecordSize := 1; // Transformer Taps
+ RecordSize := 1; // Transformer Taps
Header.Add('Tap (pu)');
end;
3:
begin
- RecordSize := NumStateVars; // Statevariabes
+ RecordSize := NumStateVars; // Statevariabes
for i := 1 to NumStateVars do
Header.Add(TpcElement(MeteredElement).VariableName(i));
end;
@@ -912,7 +786,7 @@ procedure TMonitorObj.ClearMonitorStream;
begin
RecordSize := NumTransformerCurrents; // Transformer Winding Currents
for i := 1 to Nphases do
- for j := 1 to NumberOfWindings do
+ for j := 1 to NumWindings do
begin
Header.Add(Format('P%dW%d', [i, j]));
Header.Add('Deg');
@@ -923,7 +797,7 @@ procedure TMonitorObj.ClearMonitorStream;
begin
RecordSize := NumTransformerCurrents; // Transformer Winding Currents
for i := 1 to Nphases do
- for j := 1 to NumberOfWindings do
+ for j := 1 to NumWindings do
begin
Header.Add(Format('P%dW%d', [i, j]));
Header.Add('Deg');
@@ -941,9 +815,9 @@ procedure TMonitorObj.ClearMonitorStream;
if (MeteredElement.DSSObjType and CLASSMASK) = AUTOTRANS_ELEMENT then
with TAutoTransObj(MeteredElement) do
begin
- RecordSize := 2 * NumberOfWindings * Nphases; // Transformer Winding woltages
+ RecordSize := 2 * NumWindings * Nphases; // Transformer Winding woltages
for i := 1 to Nphases do
- for j := 1 to NumberOfWindings do
+ for j := 1 to NumWindings do
begin
Header.Add(Format('P%dW%d', [i, j]));
Header.Add('Deg');
@@ -952,9 +826,9 @@ procedure TMonitorObj.ClearMonitorStream;
else
with TTransfObj(MeteredElement) do
begin
- RecordSize := 2 * NumberOfWindings * Nphases; // Transformer Winding woltages
+ RecordSize := 2 * NumWindings * Nphases; // Transformer Winding woltages
for i := 1 to Nphases do
- for j := 1 to NumberOfWindings do
+ for j := 1 to NumWindings do
begin
Header.Add(Format('P%dW%d', [i, j]));
Header.Add('Deg');
@@ -1218,13 +1092,13 @@ procedure TMonitorObj.ClearMonitorStream;
end;
end;
end;
- end; {CASE}
+ end; // CASE
- // RecordSize is the number of singles in the sample (after the hour and sec)
+ // RecordSize is the number of singles in the sample (after the hour and sec)
- // Write ID so we know it is a DSS Monitor file and which version in case we
- // change it down the road
+ // Write ID so we know it is a DSS Monitor file and which version in case we
+ // change it down the road
with MonitorStream do
begin
@@ -1237,43 +1111,36 @@ procedure TMonitorObj.ClearMonitorStream;
Write(EMPTY_LEGACY_HEADER, Sizeof(TLegacyMonitorStrBuffer));
end;
-{ So the file now looks like: (update 05-18-2021)
- FileSignature (4 bytes) 32-bit Integers
- FileVersion (4)
- RecordSize (4)
- Mode (4)
- String (256) - > this is empty now
-
- hr (4) all singles
- Sec (4)
- Sample (4*RecordSize)
- ...
-
- }
+ // So the file now looks like: (update 05-18-2021)
+ // FileSignature (4 bytes) 32-bit Integers
+ // FileVersion (4)
+ // RecordSize (4)
+ // Mode (4)
+ // String (256) - > this is empty now
+ //
+ // hr (4) all singles
+ // Sec (4)
+ // Sample (4*RecordSize)
+ // ...
except
On E: Exception do
- DoErrorMsg('Cannot open Monitor file.',
+ DoErrorMsg(_('Cannot open Monitor file.'),
E.Message,
- 'Monitor: "' + Name + '"', 670)
+ Format(_('Monitor: "%s"'), [Name]), 670)
end;
end;
-
-{--------------------------------------------------------------------------}
procedure TMonitorObj.OpenMonitorStream;
begin
-
if not IsFileOpen then
begin
MonitorStream.Seek(0, soFromEnd); // Positioned at End of Stream
IsFileOpen := TRUE;
end;
-
end;
-{--------------------------------------------------------------------------}
procedure TMonitorObj.CloseMonitorStream;
begin
try
@@ -1285,19 +1152,15 @@ procedure TMonitorObj.CloseMonitorStream;
end;
except
On E: Exception do
- DoErrorMsg('Cannot close Monitor stream.',
+ DoErrorMsg(_('Cannot close Monitor stream.'),
E.Message,
- 'Monitor: "' + Name + '"', 671)
+ Format(_('Monitor: "%s"'), [Name]), 671)
end;
end;
-{--------------------------------------------------------------------------}
procedure TMonitorObj.Save;
-
// Saves present buffer to monitor file, resets bufferptrs and continues
-
begin
-
if not IsFileOpen then
OpenMonitorStream; // Position to end of stream
@@ -1305,18 +1168,14 @@ procedure TMonitorObj.Save;
MonitorStream.Write(MonBuffer^, SizeOF(MonBuffer^[1]) * BufPtr);
BufPtr := 0; // reset Buffer for next
-
end;
-
-{--------------------------------------------------------------------------}
procedure TMonitorObj.ResetIt;
begin
BufPtr := 0;
ClearMonitorStream;
end;
-{--------------------------------------------------------------------------}
procedure TMonitorObj.PostProcess;
begin
if IsProcessed = FALSE then
@@ -1327,7 +1186,6 @@ procedure TMonitorObj.PostProcess;
IsProcessed := TRUE;
end;
-{--------------------------------------------------------------------------}
procedure TMonitorObj.TakeSample;
var
dHour: Double;
@@ -1348,7 +1206,6 @@ procedure TMonitorObj.TakeSample;
begin
-
if not (ValidMonitor and Enabled) then
Exit;
@@ -1377,7 +1234,6 @@ procedure TMonitorObj.TakeSample;
0, 1: // Voltage, current. Powers
begin
-
// MeteredElement.GetCurrents(CurrentBuffer);
// To save some time, call ComputeITerminal
MeteredElement.ComputeIterminal; // only does calc if needed
@@ -1393,7 +1249,7 @@ procedure TMonitorObj.TakeSample;
end;
except
On E: Exception do
- DoSimpleMsg(E.Message + CRLF + 'NodeRef is invalid. Try solving a snapshot or direct before solving in a mode that takes a monitor sample.', 672);
+ DoSimpleMsg(E.Message + CRLF + _('NodeRef is invalid. Try solving a snapshot or direct before solving in a mode that takes a monitor sample.'), 672);
end;
end;
@@ -1425,7 +1281,7 @@ procedure TMonitorObj.TakeSample;
end;
except
On E: Exception do
- DoSimpleMsg(E.Message + CRLF + 'NodeRef is invalid. Try solving a snapshot or direct before solving in a mode that takes a monitor sample.', 672);
+ DoSimpleMsg(E.Message + CRLF + _('NodeRef is invalid. Try solving a snapshot or direct before solving in a mode that takes a monitor sample.'), 672);
end;
end;
@@ -1504,7 +1360,7 @@ procedure TMonitorObj.TakeSample;
// Put every other Current into buffer
// Current magnitude is same in each end
k := 1;
- for i := 1 to Nphases * NumberOfWindings do
+ for i := 1 to Nphases * NumWindings do
begin
AddDblsToBuffer(pDoubleArray(@WdgCurrentsBuffer^[k].re), 2); // Add Mag, Angle
k := k + 2;
@@ -1518,7 +1374,7 @@ procedure TMonitorObj.TakeSample;
// Put every other Current into buffer
// Current magnitude is same in each end
k := 1;
- for i := 1 to Nphases * NumberOfWindings do
+ for i := 1 to Nphases * NumWindings do
begin
AddDblsToBuffer(pDoubleArray(@WdgCurrentsBuffer^[k].re), 2); // Add Mag, Angle
k := k + 2;
@@ -1542,11 +1398,11 @@ procedure TMonitorObj.TakeSample;
if (MeteredElement.DSSObjType and CLASSMASK) = AUTOTRANS_ELEMENT then
with TAutoTransObj(MeteredElement) do
begin
- for i := 1 to NumberOfWindings do
+ for i := 1 to NumWindings do
begin
GetAutoWindingVoltages(i, PhsVoltagesBuffer);
for j := 1 to nphases do
- WdgVoltagesBuffer^[i + (j - 1) * NumberofWindings] := PhsVoltagesBuffer^[j];
+ WdgVoltagesBuffer^[i + (j - 1) * NumWindings] := PhsVoltagesBuffer^[j];
end;
ConvertComplexArrayToPolar(WdgVoltagesBuffer, NumWindingVoltages);
{Put winding Voltages into Monitor}
@@ -1556,11 +1412,11 @@ procedure TMonitorObj.TakeSample;
else
with TTransfobj(MeteredElement) do
begin
- for i := 1 to NumberOfWindings do
+ for i := 1 to NumWindings do
begin
GetWindingVoltages(i, PhsVoltagesBuffer);
for j := 1 to nphases do
- WdgVoltagesBuffer^[i + (j - 1) * NumberofWindings] := PhsVoltagesBuffer^[j];
+ WdgVoltagesBuffer^[i + (j - 1) * NumWindings] := PhsVoltagesBuffer^[j];
end;
ConvertComplexArrayToPolar(WdgVoltagesBuffer, NumWindingVoltages);
{Put winding Voltages into Monitor}
@@ -1614,7 +1470,7 @@ procedure TMonitorObj.TakeSample;
// Calculates the LL voltages
for i := 1 to NPhases do
- VoltageBuffer^[i] := csub(VoltageBuffer^[i], VoltageBuffer^[i + 1]);
+ VoltageBuffer^[i] := VoltageBuffer^[i] - VoltageBuffer^[i + 1];
ConvertComplexArrayToPolar(VoltageBuffer, Yorder);
@@ -1730,19 +1586,19 @@ procedure TMonitorObj.TakeSample;
begin
Sum := cZero;
for i := 1 to Fnphases do
- Caccum(Sum, VoltageBuffer^[i]);
+ Sum += VoltageBuffer^[i];
AddDblsToBuffer(pDoubleArray(@Sum.re), 2);
end
else
begin // Average the phase magnitudes and sum angles
Sum := cZero;
for i := 1 to Fnphases do
- Caccum(Sum, VoltageBuffer^[i]);
+ Sum += VoltageBuffer^[i];
Sum.re := Sum.re / FnPhases;
AddDblsToBuffer(pDoubleArray(@Sum.re), 2);
Sum := cZero;
for i := 1 to Fnphases do
- Caccum(Sum, CurrentBuffer^[Offset + i]); // Corrected 3-11-13
+ Sum += CurrentBuffer^[Offset + i]; // Corrected 3-11-13
Sum.re := Sum.re / FnPhases;
AddDblsToBuffer(pDoubleArray(@Sum.re), 2);
end;
@@ -1797,7 +1653,6 @@ procedure TMonitorObj.TakeSample;
end;
end;
-{--------------------------------------------------------------------------}
procedure TMonitorObj.AddDblsToBuffer(Dbl: pDoubleArray; Ndoubles: Integer);
var
@@ -1808,7 +1663,6 @@ procedure TMonitorObj.AddDblsToBuffer(Dbl: pDoubleArray; Ndoubles: Integer);
AddDblToBuffer(Dbl^[i]);
end;
-{--------------------------------------------------------------------------}
procedure TMonitorObj.AddDblToBuffer(const Dbl: Double);
begin
@@ -1917,7 +1771,6 @@ procedure TMonitorObj.DoFlickerCalculations;
end;
end;
-{--------------------------------------------------------------------------}
procedure TMonitorObj.TranslateToCSV(Show: Boolean);
var
CSVName: String;
@@ -1948,20 +1801,29 @@ procedure TMonitorObj.TranslateToCSV(Show: Boolean);
try
{$IFDEF DSS_CAPI_PM}
+ if PMParent.ConcatenateReports then
+ // We may need to wait other threads before using the file
+ PMParent.ConcatenateReportsLock.Acquire();
+
if PMParent.ConcatenateReports and (PMParent <> DSS) then
begin
- F := TFileStream.Create(CSVName, fmReadWrite);
+ F := TBufferedFileStream.Create(CSVName, fmOpenReadWrite);
F.Seek(0, soFromEnd);
end
else
{$ENDIF}
- F := TFileStream.Create(CSVName, fmCreate);
+ F := TBufferedFileStream.Create(CSVName, fmCreate);
except
On E: Exception do
begin
- DoSimpleMsg('Error opening CSVFile "' + CSVName + '" for writing' + CRLF + E.Message, 672);
- Exit
+ DoSimpleMsg('Error opening CSVFile "%s" for writing: %s', [CSVName, E.Message], 672);
+{$IFDEF DSS_CAPI_PM}
+ if PMParent.ConcatenateReports then
+ // We may need to wait other threads before using the file
+ PMParent.ConcatenateReportsLock.Release();
+{$ENDIF}
+ Exit;
end;
end;
@@ -1983,7 +1845,6 @@ procedure TMonitorObj.TranslateToCSV(Show: Boolean);
try
try
-
while not (MonitorStream.Position >= MonitorStream.Size) do
begin
with MonitorStream do
@@ -2004,21 +1865,21 @@ procedure TMonitorObj.TranslateToCSV(Show: Boolean);
end;
FSWriteln(F);
end;
-
except
-
On E: Exception do
begin
- DoSimpleMsg('Error Writing CSVFile "' + CSVName + '" ' + CRLF + E.Message, 673);
+ DoSimpleMsg('Error Writing CSVFile "%s": %s', [CSVName, E.Message], 673);
end;
-
end;
finally
-
CloseMonitorStream;
FreeAndNil(F);
-
+{$IFDEF DSS_CAPI_PM}
+ if PMParent.ConcatenateReports then
+ // We may need to wait other threads before using the file
+ PMParent.ConcatenateReportsLock.Release();
+{$ENDIF}
end;
if Show then
@@ -2027,24 +1888,17 @@ procedure TMonitorObj.TranslateToCSV(Show: Boolean);
DSS.GlobalResult := CSVName;
end;
-{--------------------------------------------------------------------------}
procedure TMonitorObj.GetCurrents(Curr: pComplexArray); //Get present value of terminal Curr for reports
var
i: Integer;
begin
-
-{
- Revised 12-7-99 to return Zero current instead of Monitored element current because
- it was messing up Newton iteration.
-}
-
+// Revised 12-7-99 to return Zero current instead of Monitored element current because
+// it was messing up Newton iteration.
for i := 1 to Fnconds do
Curr^[i] := CZERO;
-
end;
-{--------------------------------------------------------------------------}
-procedure TMonitorObj.DumpProperties(F: TFileStream; Complete: Boolean);
+procedure TMonitorObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i, k: Integer;
@@ -2083,22 +1937,6 @@ procedure TMonitorObj.DumpProperties(F: TFileStream; Complete: Boolean);
end;
FSWriteln(F);
end;
-
-end;
-
-procedure TMonitorObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := ''; //'element';
- PropertyValue[2] := '1'; //'terminal';
- PropertyValue[3] := '0'; //'mode';
- PropertyValue[4] := ''; // 'action'; // buffer=clear|save|take|process
- PropertyValue[5] := 'NO';
- PropertyValue[6] := 'YES';
- PropertyValue[7] := 'YES';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
end;
function TMonitorObj.Get_FileName: String;
@@ -2120,4 +1958,7 @@ function TMonitorObj.Get_FileName: String;
initialization
FillChar(EMPTY_LEGACY_HEADER, SizeOf(EMPTY_LEGACY_HEADER), 0);
+ PropInfo := NIL;
+finalization
+ ActionEnum.Free;
end.
diff --git a/src/Meters/ReduceAlgs.pas b/src/Meters/ReduceAlgs.pas
index 3fde09bed..72fe63279 100644
--- a/src/Meters/ReduceAlgs.pas
+++ b/src/Meters/ReduceAlgs.pas
@@ -7,9 +7,9 @@
----------------------------------------------------------
}
-{Reduction Algorithms}
+// Reduction Algorithms
-{Primarily called from EnergyMeter}
+// Primarily called from EnergyMeter
interface
@@ -21,15 +21,12 @@ interface
procedure DoReduceDefault(DSS: TDSSContext; var BranchList: TCktTree);
procedure DoReduceShortLines(DSS: TDSSContext; var BranchList: TCktTree);
procedure DoReduceDangling(DSS: TDSSContext; var BranchList: TCktTree);
- {procedure DoReduceTapEnds(var BranchList:TCktTree);}
procedure DoBreakLoops(DSS: TDSSContext; var BranchList: TCktTree);
procedure DoMergeParallelLines(DSS: TDSSContext; var BranchList: TCktTree);
procedure DoReduceSwitches(DSS: TDSSContext; var Branchlist: TCktTree);
procedure DoRemoveAll_1ph_Laterals(DSS: TDSSContext; var Branchlist: TCktTree);
-
procedure DoRemoveBranches(DSS: TDSSContext; var BranchList: TCktTree; FirstPDElement: TPDElement; KeepLoad: Boolean; const EditStr: String);
-
implementation
uses
@@ -38,8 +35,7 @@ implementation
DSSGlobals,
DSSClassDefs,
Load,
- uComplex,
- ParserDel,
+ UComplex, DSSUcomplex,
CktElement,
sysutils,
ExecHelper,
@@ -51,11 +47,9 @@ implementation
PARALLELMERGE: Boolean = FALSE;
procedure DoMergeParallelLines(DSS: TDSSContext; var BranchList: TCktTree);
-{Merge all lines in this zone that are marked in parallel}
-
+// Merge all lines in this zone that are marked in parallel
var
LineElement: TLineObj;
-
begin
if BranchList <> NIL then
begin
@@ -65,7 +59,7 @@ procedure DoMergeParallelLines(DSS: TDSSContext; var BranchList: TCktTree);
begin
if BranchList.PresentBranch.IsParallel then
begin
- {There will always be two lines in parallel. The first operation will disable the second}
+ // There will always be two lines in parallel. The first operation will disable the second
if LineElement.Enabled then
LineElement.MergeWith(TLineObj(BranchList.PresentBranch.LoopLineObj), PARALLELMERGE); // Guaranteed to be a line
end;
@@ -75,11 +69,9 @@ procedure DoMergeParallelLines(DSS: TDSSContext; var BranchList: TCktTree);
end;
procedure DoBreakLoops(DSS: TDSSContext; var BranchList: TCktTree);
-
-{Break loops}
+// Break loops
var
LineElement: TLineObj;
-
begin
if BranchList <> NIL then
begin
@@ -89,7 +81,7 @@ procedure DoBreakLoops(DSS: TDSSContext; var BranchList: TCktTree);
begin
if BranchList.PresentBranch.IsLoopedHere then
begin
- {There will always be two lines in the loop. The first operation will disable the second}
+ // There will always be two lines in the loop. The first operation will disable the second
if LineElement.Enabled then
TLineObj(BranchList.PresentBranch.LoopLineObj).Enabled := FALSE; // Disable the other
end;
@@ -98,22 +90,6 @@ procedure DoBreakLoops(DSS: TDSSContext; var BranchList: TCktTree);
end;
end;
-
-{
-
-procedure DoReduceTapEnds(DSS: TDSSContext; var BranchList:TCktTree);
-(*Var
- pLineElem1, pLineElem2:TLineObj;
- ToBusRef:Integer;
- AngleTest:Double;
- ParentNode:TCktTreeNode;
-*)
-begin
-
-end;
-}
-
-
procedure DoReduceDangling(DSS: TDSSContext; var BranchList: TCktTree);
var
pLineElem1: TDSSCktElement;
@@ -121,19 +97,16 @@ procedure DoReduceDangling(DSS: TDSSContext; var BranchList: TCktTree);
begin
if BranchList <> NIL then
begin
- {Let's throw away all dangling end branches}
+ // Let's throw away all dangling end branches
BranchList.First;
pLineElem1 := BranchList.GoForward; // Always keep the first element
while pLineElem1 <> NIL do
begin
-
if IsLineElement(pLineElem1) then
with BranchList.PresentBranch do
begin
-
- {If it is at the end of a section and has no load,cap, reactor,
- or coordinate, just throw it away}
+ // If it is at the end of a section and has no load,cap, reactor, or coordinate, just throw it away
if IsDangling then
begin
ToBusRef := ToBusReference; // only access this property once!
@@ -141,12 +114,11 @@ procedure DoReduceDangling(DSS: TDSSContext; var BranchList: TCktTree);
with DSS.ActiveCircuit.Buses^[ToBusRef] do
if not (Keep) then
pLineElem1.Enabled := FALSE;
- end; {IF}
- end; {If-With}
+ end;
+ end;
pLineElem1 := BranchList.GoForward;
end;
end;
-
end;
function IsShortLine(DSS: TDSSContext; const Elem: TDSSCktElement): Boolean;
@@ -156,15 +128,15 @@ function IsShortLine(DSS: TDSSContext; const Elem: TDSSCktElement): Boolean;
begin
LineElement := TLineObj(Elem);
- {Get Positive Sequence or equivalent from matrix}
+ // Get Positive Sequence or equivalent from matrix
if LineElement.SymComponentsModel then
with LineElement do
Ztest := Cabs(Cmplx(R1, X1)) * Len
- else {Get impedance from Z matrix} {Zs - Zm ... approximates Z1}
+ else // Get impedance from Z matrix Zs - Zm ... approximates Z1
with LineElement do
begin
if NPhases > 1 then
- Ztest := Cabs(Csub(Z.Getelement(1, 1), Z.GetElement(1, 2))) * Len
+ Ztest := Cabs(Z.Getelement(1, 1) - Z.GetElement(1, 2)) * Len
else
Ztest := Cabs(Z.Getelement(1, 1)) * Len;
end;
@@ -173,11 +145,10 @@ function IsShortLine(DSS: TDSSContext; const Elem: TDSSCktElement): Boolean;
Result := TRUE
else
Result := FALSE;
-
end;
procedure DoReduceShortLines(DSS: TDSSContext; var BranchList: TCktTree);
-{Eliminate short lines with impedance < Zmag and merge with lines on either side}
+// Eliminate short lines with impedance < Zmag and merge with lines on either side
var
LineElement1, LineElement2: TLineObj;
ShuntElement: TDSSCktElement;
@@ -186,18 +157,21 @@ procedure DoReduceShortLines(DSS: TDSSContext; var BranchList: TCktTree);
begin
if BranchList <> NIL then
- begin {eliminate really short lines}
- {First, flag all elements that need to be merged}
+ begin // eliminate really short lines
+ // First, flag all elements that need to be merged
LineElement1 := BranchList.First;
LineElement1 := BranchList.GoForward; // Always keep the first element
while LineElement1 <> NIL do
begin
if IsLineElement(LineElement1) then
begin
- LineElement1.Flag := IsShortLine(DSS, LineElement1); {Too small: Mark for merge with something}
- end; {IF}
+ if IsShortLine(DSS, LineElement1) then // Too small: Mark for merge with something
+ Include(LineElement1.Flags, flg.Flag)
+ else
+ Exclude(LineElement1.Flags, flg.Flag);
+ end;
LineElement1 := BranchList.GoForward; // traverse the whole meter zone (circuit tree)
- end; {WHILE}
+ end;
LineElement1 := BranchList.First;
LineElement1 := BranchList.GoForward; // Always keep the first element in the Tree
@@ -205,18 +179,18 @@ procedure DoReduceShortLines(DSS: TDSSContext; var BranchList: TCktTree);
begin
if LineElement1.enabled then // else skip
- if not LineElement1.HasControl then
- if not LineElement1.IsMonitored then // Skip if controlled element or control is monitoring ,,,
+ if not (Flg.HasControl in LineElement1.Flags) then
+ if not (Flg.IsMonitored in LineElement1.Flags) then // Skip if controlled element or control is monitoring ,,,
- if LineElement1.Flag then // too short; Try to merge this element out
+ if flg.Flag in LineElement1.Flags then // too short; Try to merge this element out
begin
with BranchList do
begin
- // {****} WriteDLLDebugFile(Format('Processing Line.%s Bus1=%s Bus2=%s',[Uppercase(LineElement1.Name), LineElement1.GetBus(1), LineElement1.GetBus(2)]));
- if (PresentBranch.NumChildBranches = 0) and (PresentBranch.NumShuntObjects = 0) then
+ if (PresentBranch.NumChildBranches = 0) and (PresentBranch.NumShuntObjects = 0) and
+ (not DSS.ActiveCircuit.Buses[PresentBranch.ToBusReference].Keep) then
LineElement1.Enabled := FALSE // just discard it
else
- if (PresentBranch.NumChildBranches = 0) {****OR (PresentBranch.NumChildBranches>1)**} then {Merge with Parent and move shunt elements to TO node on parent branch}
+ if (PresentBranch.NumChildBranches = 0) then //Merge with Parent and move shunt elements to TO node on parent branch
begin
ParentNode := PresentBranch.ParentBranch;
if ParentNode <> NIL then
@@ -224,8 +198,8 @@ procedure DoReduceShortLines(DSS: TDSSContext; var BranchList: TCktTree);
if ParentNode.NumChildBranches = 1 then // only works for in-line
if not DSS.ActiveCircuit.Buses^[PresentBranch.ToBusReference].Keep then
begin // Check Keeplist
- {Let's consider merging}
- {First Check for any Capacitors. Skip if any}
+ // Let's consider merging
+ // First Check for any Capacitors. Skip if any
MergeOK := TRUE;
if ParentNode.NumShuntObjects > 0 then
begin
@@ -239,7 +213,7 @@ procedure DoReduceShortLines(DSS: TDSSContext; var BranchList: TCktTree);
Break; // outta loop
end;
ShuntElement := PresentBranch.NextShuntObject;
- end; {While}
+ end;
end;
if MergeOK then
@@ -248,35 +222,32 @@ procedure DoReduceShortLines(DSS: TDSSContext; var BranchList: TCktTree);
if LineElement2.enabled then // Check to make sure it hasn't been merged out
if IsLineElement(LineElement2) then
if LineElement2.MergeWith(LineElement1, SERIESMERGE) then
- begin {Move any loads to ToBus Reference of parent branch}
- // {****} WriteDLLDebugFile(Format('TOP Loop: Eliminating Line %s and merging into Line %s ',[Uppercase(LineElement1.Name), Uppercase(LineElement2.Name) ]));
+ begin // Move any loads to ToBus Reference of parent branch
if ParentNode.NumShuntObjects > 0 then
begin
- {Redefine bus connection for PC elements hanging on the bus that is eliminated}
+ // Redefine bus connection for PC elements hanging on the bus that is eliminated
ShuntElement := ParentNode.FirstShuntObject;
while ShuntElement <> NIL do
begin
DSS.Parser.CmdString := 'bus1="' + DSS.ActiveCircuit.BusList.NameOfIndex(PresentBranch.ToBusReference) + GetNodeString(ShuntElement.GetBus(1)) + '"';
- // {****} WriteDLLDebugFile(Format('Moving Shunt.%s from %s to %s ',[ShuntElement.Name, ShuntElement.GetBus(1), DSS.Parser.CmdString ]));
- ShuntElement.Edit;
+ ShuntElement.Edit(DSS.Parser);
ShuntElement := ParentNode.NextShuntObject;
- end; {While}
- end; {IF}
- //+++ LineElement1 := BranchList.GoForward; // skip to next branch since we eliminated a bus
+ end;
+ end;
end;
end;
- end; {IF}
- end; {IF ParentNode}
+ end; // IF
+ end; // IF ParentNode
end
else
- if (PresentBranch.NumChildBranches = 1) then {Merge with child}
+ if (PresentBranch.NumChildBranches = 1) then // Merge with child
begin
if not DSS.ActiveCircuit.Buses^[PresentBranch.ToBusReference].Keep then // check keeplist
begin
- {Let's consider merging}
- {First Check for any Capacitors. Skip if any}
+ // Let's consider merging
+ // First Check for any Capacitors. Skip if any
MergeOK := TRUE;
if PresentBranch.NumShuntObjects > 0 then
begin
@@ -290,7 +261,7 @@ procedure DoReduceShortLines(DSS: TDSSContext; var BranchList: TCktTree);
Break; // outta loop
end;
ShuntElement := PresentBranch.NextShuntObject;
- end; {While}
+ end;
end;
if MergeOK then
@@ -300,24 +271,23 @@ procedure DoReduceShortLines(DSS: TDSSContext; var BranchList: TCktTree);
if IsLineElement(LineElement2) then
if LineElement2.MergeWith(LineElement1, SERIESMERGE) then
begin
- // {****} WriteDLLDebugFile(Format('BOT Loop: Eliminating Line %s and merging into Line %s ',[Uppercase(LineElement1.Name), Uppercase(LineElement2.Name)]));
if PresentBranch.NumShuntObjects > 0 then
begin
- {Redefine bus connection to upline bus}
+ // Redefine bus connection to upline bus
ShuntElement := PresentBranch.FirstShuntObject;
while ShuntElement <> NIL do
begin
+ //TODO: remove parser usage
DSS.Parser.CmdString := 'bus1="' + DSS.ActiveCircuit.BusList.NameOfIndex(PresentBranch.FromBusReference) + GetNodeString(ShuntElement.GetBus(1)) + '"';
- // {****} WriteDLLDebugFile(Format('Moving Shunt.%s from %s to %s ',[ShuntElement.Name, ShuntElement.GetBus(1), DSS.Parser.CmdString ]));
- ShuntElement.Edit;
+ ShuntElement.Edit(DSS.Parser);
ShuntElement := PresentBranch.NextShuntObject;
- end; {While}
- end; {IF}
+ end;
+ end;
LineElement1 := BranchList.GoForward; // skip to next branch since we eliminated a bus
end;
end;
- end; {IF not}
- end; {ELSE}
+ end; // IF not
+ end; // ELSE
end;
end;
LineElement1 := BranchList.GoForward;
@@ -333,28 +303,23 @@ procedure DoReduceShortLines(DSS: TDSSContext; var BranchList: TCktTree);
end;
procedure DoReduceSwitches(DSS: TDSSContext; var Branchlist: TCktTree);
-
-{Merge switches in with lines or delete if dangling}
-
+// Merge switches in with lines or delete if dangling
var
LineElement1, LineElement2: TLineObj;
begin
-
if BranchList <> NIL then
begin
-
LineElement1 := BranchList.First;
LineElement1 := BranchList.GoForward; // Always keep the first element
while LineElement1 <> NIL do
begin
-
if LineElement1.Enabled then // maybe we threw it away already
if IsLineElement(LineElement1) then
if LineElement1.IsSwitch then
with BranchList.PresentBranch do
- {see if eligble for merging}
+ // see if eligible for merging
case NumChildBranches of
- 0: {Throw away if dangling}
+ 0: // Throw away if dangling
if NumShuntObjects = 0 then
LineElement1.Enabled := FALSE;
@@ -362,19 +327,18 @@ procedure DoReduceSwitches(DSS: TDSSContext; var Branchlist: TCktTree);
if NumShuntObjects = 0 then
if not DSS.ActiveCircuit.Buses^[ToBusReference].Keep then
begin
- {Let's consider merging}
+ // Let's consider merging
LineElement2 := FirstChildBranch.CktObject;
if IsLineElement(LineElement2) then
if not LineElement2.IsSwitch then
LineElement2.MergeWith(LineElement1, SERIESMERGE){Series Merge}
end;
- else {Nada}
+ else //Nada
end;
LineElement1 := BranchList.GoForward;
end;
end;
-
end;
procedure DoReduceDefault(DSS: TDSSContext; var BranchList: TCktTree);
@@ -384,27 +348,24 @@ procedure DoReduceDefault(DSS: TDSSContext; var BranchList: TCktTree);
begin
if BranchList <> NIL then
begin
-
- {Now merge remaining lines}
-
+ // Now merge remaining lines
LineElement1 := BranchList.First;
LineElement1 := BranchList.GoForward; // Always keep the first element
while LineElement1 <> NIL do
begin
-
if IsLineElement(LineElement1) then
if not LineElement1.IsSwitch then // Exceptions
- if not LineElement1.HasControl then
- if not LineElement1.IsMonitored then
+ if not (Flg.HasControl in LineElement1.Flags) then
+ if not (Flg.IsMonitored in LineElement1.Flags) then
if LineElement1.Enabled then // maybe we threw it away already
with BranchList do
begin
- {see if eligble for merging}
+ // see if eligible for merging
if PresentBranch.NumChildBranches = 1 then
if PresentBranch.NumShuntObjects = 0 then
if not DSS.ActiveCircuit.Buses^[PresentBranch.ToBusReference].Keep then
begin
- {Let's consider merging}
+ // Let's consider merging
LineElement2 := PresentBranch.FirstChildBranch.CktObject;
if IsLineElement(LineElement2) then
@@ -417,7 +378,6 @@ procedure DoReduceDefault(DSS: TDSSContext; var BranchList: TCktTree);
LineElement1 := BranchList.GoForward;
end;
end;
-
end;
procedure DoRemoveBranches(DSS: TDSSContext; var BranchList: TCktTree; FirstPDElement: TPDElement; KeepLoad: Boolean; const EditStr: String);
@@ -431,10 +391,8 @@ procedure DoRemoveBranches(DSS: TDSSContext; var BranchList: TCktTree; FirstPDEl
LoadBus: TDSSBus;
LoadBasekV: Double;
StartLevel: Integer;
-
begin
-
-// Position BranchList at "FirstPDElement"
+ // Position BranchList at "FirstPDElement"
PDElem := BranchList.First;
while (PDElem <> FirstPDElement) and (PDElem <> NIL) do
PDElem := BranchList.GoForward;
@@ -443,19 +401,18 @@ procedure DoRemoveBranches(DSS: TDSSContext; var BranchList: TCktTree; FirstPDEl
if PDElem = NIL then
begin
- DoSimpleMsg(DSS, Format('%s.%s Not Found (Remove Command).', [FirstPDElement.ParentClass.Name, FirstPDElement.Name]), 5432100);
+ DoSimpleMsg(DSS, '%s not found (Remove Command).', [FirstPDElement.FullName], 5432100);
end
else
begin
-
- { If KeepLoad, create a new Load object at upstream bus (from bus).}
+ // If KeepLoad, create a new Load object at upstream bus (from bus).
if KeepLoad then
with BranchList.PresentBranch do
begin
BusName := FirstPDElement.GetBus(FromTerminal);
- TotalkVA := CDivreal(PDelem.Power[FromTerminal], 1000.0);
+ TotalkVA := PDelem.Power[FromTerminal] / 1000.0;
NewLoadName := Format('Eq_%s_%s', [FirstPDElement.Name, StripExtension(BusName)]);
- {Pick up the kV Base for the From bus}
+ // Pick up the kV Base for the From bus
LoadBus := DSS.ActiveCircuit.Buses^[FromBusReference];
if Loadbus.kVBase > 0.0 then
LoadBasekV := LoadBus.kVBase
@@ -466,25 +423,20 @@ procedure DoRemoveBranches(DSS: TDSSContext; var BranchList: TCktTree; FirstPDEl
end;
if FirstPDElement.NPhases > 1 then
LoadBasekV := LoadBasekV * Sqrt3;
- {Load up DSS.Parser with definition of equivalent load}
+ // Load up DSS.Parser with definition of equivalent load
DSS.Parser.CmdString := Format(' phases=%d Bus1=%s kW=%g kvar=%g kV=%g %s', [FirstPDElement.NPhases, Busname, TotalkVA.re, TotalkVA.im, LoadBasekV, EditStr]);
DSS.DSSExecutive.AddObject('load', NewLoadName); // Add new load to circuit
end;
- {Disable all elements in the tree downline from the start element}
-
- // {****} WriteDLLDebugFile(Format('StartLevel = %d ',[ StartLevel]));
-
+ // Disable all elements in the tree downline from the start element
while PDElem <> NIL do
begin
- // {****} WriteDLLDebugFile(Format('Disabling cktelement %d %s.%s',[ BranchList.Level, PDelem.ParentClass.Name, PDElem.Name ]));
with BranchList do
begin
pShunt := PresentBranch.FirstShuntObject;
while pShunt <> NIL do
begin
pShunt.Enabled := FALSE;
- // {****} WriteDLLDebugFile(Format('Disabling shunt element %s.%s',[ pShunt.ParentClass.Name, pShunt.Name ]));
pShunt := PresentBranch.NextShuntObject;
end;
end;
@@ -495,9 +447,7 @@ procedure DoRemoveBranches(DSS: TDSSContext; var BranchList: TCktTree; FirstPDEl
// Check to see if we are back where we started. If so, stop.
if BranchList.Level <= StartLevel then
PDElem := NIL;
-
end;
-
end;
with DSS.ActiveCircuit do
@@ -509,53 +459,43 @@ procedure DoRemoveBranches(DSS: TDSSContext; var BranchList: TCktTree; FirstPDEl
end;
procedure DoRemoveAll_1ph_Laterals(DSS: TDSSContext; var Branchlist: TCktTree);
-{Remove all 1-phase laterals in Branchlist and lump total load back to main feeder}
-{
- This removes all elements on all 1ph laterals and moves the net load back to the main feeder tap point
- Does not
-}
-
+// Remove all 1-phase laterals in Branchlist and lump total load back to main feeder
+// This removes all elements on all 1ph laterals and moves the net load back to the main feeder tap point does not
var
PDelem: TPDElement;
BusName: String;
pShunt: TDSSCktElement;
HeadBus: TDSSBus;
- HeadBasekV: Double;
+ HeadBasekV: Double = 1.0;
StartLevel: Integer;
pBus: TDSSBus;
// strNodes: String;
-
-
begin
- {
- Just march down the feeder until we encounter a 1-phase PD element
- }
- // Position BranchList at "beginning"
+ // Just march down the feeder until we encounter a 1-phase PD element
+ // Position BranchList at "beginning"
PDElem := BranchList.First;
while PDElem <> NIL do
begin
-
if PDElem.nphases = 1 then // ELIMINATE THIS LATERAL
begin
- {Check to see if this is a 1-phase switch or other branch in the middle of a 3-phase branch and go on}
- {If the To bus has more than 1 phase, keep this branch else lump the load at the From node}
+ // Check to see if this is a 1-phase switch or other branch in the middle of a 3-phase branch and go on
+ // If the To bus has more than 1 phase, keep this branch else lump the load at the From node
pBus := DSS.ActiveCircuit.Buses^[Branchlist.PresentBranch.ToBusReference]; //To Bus
if pBus.NumNodesThisBus = 1 then // Eliminate the lateral starting with this branch
begin
-
- { If KeepLoad (ReduceLateralsKeepLoad), create a new Load object at upstream bus (from bus).}
+ // If KeepLoad (ReduceLateralsKeepLoad), create a new Load object at upstream bus (from bus).
if DSS.ActiveCircuit.ReduceLateralsKeepLoad then
with BranchList do
begin
BusName := PDElem.GetBus(PresentBranch.FromTerminal);
- // Make sure there is a node reference .. default to 1
+ // Make sure there is a node reference .. default to 1
if Pos('.', BusName) = 0 then
BusName := BusName + '.1';
- {Pick up the kV Base for the From bus}
+ // Pick up the kV Base for the From bus
HeadBus := DSS.ActiveCircuit.Buses^[PresentBranch.FromBusReference];
if (HeadBus.kVBase > 0.0) then
HeadBasekV := HeadBus.kVBase // Use defined voltage base
@@ -566,25 +506,20 @@ procedure DoRemoveAll_1ph_Laterals(DSS: TDSSContext; var Branchlist: TCktTree);
end;
end;
- {
-
- Disable all PDelements in the tree downline from the beginning of the 1-phase lateral
- Move 1-phase shunts to Headbus
-
- }
+ // Disable all PDelements in the tree downline from the beginning of the 1-phase lateral
+ // Move 1-phase shunts to Headbus
StartLevel := BranchList.level; // record level of first 1-phase branch in this lateral
while PDElem <> NIL do
begin
- // {****} WriteDLLDebugFile(Format('Disabling cktelement %d %s.%s',[ BranchList.Level, PDelem.ParentClass.Name, PDElem.Name ]));
- { Get rid of loads and other shunt elements connected to this branch }
+ // Get rid of loads and other shunt elements connected to this branch
with BranchList do
begin
pShunt := PresentBranch.FirstShuntObject;
while pShunt <> NIL do
begin
DSS.Parser.CmdString := Format('Bus1=%s kV=%.6g ', [Busname, HeadBasekV]);
- pShunt.Edit;
+ pShunt.Edit(DSS.Parser);
pShunt := PresentBranch.NextShuntObject;
end;
end;
@@ -592,7 +527,7 @@ procedure DoRemoveAll_1ph_Laterals(DSS: TDSSContext; var Branchlist: TCktTree);
PDElem.Enabled := FALSE;
PDElem := BranchList.GoForward;
- // Check to see if we are back where we started. If so, stop with this lateral and get on to the next.
+ // Check to see if we are back where we started. If so, stop with this lateral and get on to the next.
if PDElem <> NIL then
if (BranchList.Level <= StartLevel) then
Break;
@@ -607,9 +542,6 @@ procedure DoRemoveAll_1ph_Laterals(DSS: TDSSContext; var Branchlist: TCktTree);
else
PDElem := BranchList.GoForward;
-
- // {****} If PDElem<>Nil then WriteDLLDebugFile(Format('Going on.. cktelement %d %s.%s phases=%d',[ BranchList.Level, PDelem.ParentClass.Name, PDElem.Name, PDelem.NPhases ]));
-
end;
with DSS.ActiveCircuit do
@@ -618,8 +550,6 @@ procedure DoRemoveAll_1ph_Laterals(DSS: TDSSContext; var Branchlist: TCktTree);
DoResetMeterZones; // without eliminated devices
Solution.SystemYChanged := TRUE; // force rebuild of Y
end;
-
-
end;
end.
diff --git a/src/Meters/Sensor.pas b/src/Meters/Sensor.pas
index 098c60077..a9c67bca5 100644
--- a/src/Meters/Sensor.pas
+++ b/src/Meters/Sensor.pas
@@ -7,16 +7,8 @@
----------------------------------------------------------
}
-{
- Change Log
- 8-24-2007 Created from Monitor Object
- Sept-Oct 2008 Modified for new load allocation and state estimator algorithms
-}
-
-{
- Sensor compares voltages and currents. Power quantities are converted to current quantities
- based on rated kVBase, or actual voltage if voltage measurement specified.
-}
+// Sensor compares voltages and currents. Power quantities are converted to current quantities
+// based on rated kVBase, or actual voltage if voltage measurement specified.
interface
@@ -26,36 +18,47 @@ interface
Meterelement,
DSSClass,
Arraydef,
- ucomplex,
+ UComplex, DSSUcomplex,
utilities,
Classes;
type
+{$SCOPEDENUMS ON}
+ TSensorProp = (
+ INVALID = 0,
+ element = 1,
+ terminal = 2,
+ kvbase = 3,
+ clear = 4,
+ kVs = 5,
+ currents = 6,
+ kWs = 7,
+ kvars = 8,
+ conn = 9, // Sensor connection
+ Deltadirection = 10, // +/- 1
+ pctError = 11, // %Error of sensor
+ Weight = 12 // for WLS calc
+ // action = 13 // unused
+ );
+{$SCOPEDENUMS OFF}
-{==============================================================================}
TSensor = class(TMeterClass)
- PRIVATE
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const SensorName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
procedure ResetAll; OVERRIDE;
procedure SampleAll; OVERRIDE; // Force all Sensors to take a sample
procedure SaveAll; OVERRIDE; // Force all Sensors to save their buffers to disk
procedure SetHasSensorFlag;
-
end;
-{==============================================================================}
-
TSensorObj = class(TMeterElement)
PRIVATE
ValidSensor: Boolean;
@@ -64,23 +67,19 @@ TSensorObj = class(TMeterElement)
kVBase: Double; // value specified
Vbase: Double; // in volts
- FConn: Integer;
-
Vspecified,
Ispecified,
Pspecified,
Qspecified: Boolean;
- ClearSpecified: Boolean;
+ ClearSpecified: LongBool;
FDeltaDirection: Integer;
- procedure Set_Conn(const Value: Integer);
- procedure Set_Action(const Value: String);
+ //procedure Set_Action(const Value: String);
procedure ZeroSensorArrays;
procedure AllocateSensorObjArrays;
- procedure RecalcVbase;
+
function RotatePhases(const j: Integer): Integer;
- function LimitToPlusMinusOne(const i: Integer): Integer;
procedure ClearSensor;
procedure UpdateCurrentVector;
function Get_WLSCurrentError: Double;
@@ -90,11 +89,15 @@ TSensorObj = class(TMeterElement)
pctError,
Weight: Double;
+ FConn: Integer;
+ procedure RecalcVbase;
constructor Create(ParClass: TDSSClass; const SensorName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model, reset nphases
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model, reset nphases
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE; // Always Zero for a Sensor
procedure TakeSample; OVERRIDE; // Go add a sample to the buffer
@@ -102,13 +105,10 @@ TSensorObj = class(TMeterElement)
procedure Save; // Saves present buffer to file
procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present value of terminal Curr
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
{Properties to interpret input to the sensor}
- property Conn: Integer READ Fconn WRITE Set_Conn; // Connection code
- property Action: String WRITE Set_Action;
+ // property Action: String WRITE Set_Action;
property WLSCurrentError: Double READ Get_WLSCurrentError;
property WLSVoltageError: Double READ Get_WLSVoltageError;
@@ -119,12 +119,9 @@ TSensorObj = class(TMeterElement)
property SensorQ: pDoubleArray READ SensorKVAR;
end;
-{==============================================================================}
-
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Circuit,
@@ -141,223 +138,155 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TSensorObj;
+ TProp = TSensorProp;
const
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
- NumPropsThisClass = 13;
-
-{==============================================================================}
-
-constructor TSensor.Create(dssContext: TDSSContext); // Creates superstructure for all Sensor objects
+constructor TSensor.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
-
- Class_name := 'Sensor';
- DSSClassType := DSSClassType + SENSOR_ELEMENT;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, SENSOR_ELEMENT, 'Sensor');
end;
-{==============================================================================}
-
destructor TSensor.Destroy;
-
begin
inherited Destroy;
end;
-{==============================================================================}
-
procedure TSensor.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
-
- PropertyName[1] := 'element';
- PropertyName[2] := 'terminal';
- PropertyName[3] := 'kvbase';
- PropertyName[4] := 'clear';
- PropertyName[5] := 'kVs';
- PropertyName[6] := 'currents';
- PropertyName[7] := 'kWs';
- PropertyName[8] := 'kvars';
- PropertyName[9] := 'conn'; // Sensor connection
- PropertyName[10] := 'Deltadirection'; // +/- 1
- PropertyName[11] := '%Error'; // %Error of sensor
- PropertyName[12] := 'Weight'; // for WLS calc
- PropertyName[13] := 'action';
-
- PropertyHelp[1] := 'Name (Full Object name) of element to which the Sensor is connected.';
- PropertyHelp[2] := 'Number of the terminal of the circuit element to which the Sensor is connected. ' +
- '1 or 2, typically. Default is 1.';
- PropertyHelp[3] := 'Voltage base for the sensor, in kV. If connected to a 2- or 3-phase terminal, ' + CRLF +
- 'specify L-L voltage. For 1-phase devices specify L-N or actual 1-phase voltage. ' +
- 'Like many other DSS devices, default is 12.47kV.';
- PropertyHelp[4] := '{ Yes | No }. Clear=Yes clears sensor values. Should be issued before putting in a new set of measurements.';
- PropertyHelp[5] := 'Array of Voltages (kV) measured by the voltage sensor. For Delta-connected ' +
- 'sensors, Line-Line voltages are expected. For Wye, Line-Neutral are expected.';
- PropertyHelp[6] := 'Array of Currents (amps) measured by the current sensor. Specify this or power quantities; not both.';
- PropertyHelp[7] := 'Array of Active power (kW) measurements at the sensor. Is converted into Currents along with q=[...]' + CRLF +
- 'Will override any currents=[...] specification.';
- PropertyHelp[8] := 'Array of Reactive power (kvar) measurements at the sensor. Is converted into Currents along with p=[...]';
- PropertyHelp[9] := 'Voltage sensor Connection: { wye | delta | LN | LL }. Default is wye. Applies to voltage measurement only. ' + CRLF +
- 'Currents are always assumed to be line currents.' + CRLF +
- 'If wye or LN, voltage is assumed measured line-neutral; otherwise, line-line.';
- PropertyHelp[10] := '{1 or -1} Default is 1: 1-2, 2-3, 3-1. For reverse rotation, enter -1. Any positive or negative entry will suffice.';
- PropertyHelp[11] := 'Assumed percent error in the measurement. Default is 1.';
- PropertyHelp[12] := 'Weighting factor: Default is 1.';
- PropertyHelp[13] := 'NOT IMPLEMENTED.Action options: ' + CRLF + 'SQERROR: Show square error of the present value of the monitored terminal ' + CRLF +
- 'quantity vs the sensor value. Actual values - convert to per unit in calling program. ' + CRLF +
- 'Value reported in result window/result variable.';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+ // double arrays/vectors
+ PropertyType[ord(TProp.kVs)] := TPropertyType.DoubleVArrayProperty;
+ PropertyOffset[ord(TProp.kVs)] := ptruint(@obj.SensorVoltage);
+ PropertyOffset2[ord(TProp.kVs)] := ptruint(@obj.Fnphases);
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
+ PropertyType[ord(TProp.currents)] := TPropertyType.DoubleVArrayProperty;
+ PropertyOffset[ord(TProp.currents)] := ptruint(@obj.SensorCurrent);
+ PropertyOffset2[ord(TProp.currents)] := ptruint(@obj.Fnphases);
-end;
+ PropertyType[ord(TProp.kWs)] := TPropertyType.DoubleVArrayProperty;
+ PropertyOffset[ord(TProp.kWs)] := ptruint(@obj.SensorkW);
+ PropertyOffset2[ord(TProp.kWs)] := ptruint(@obj.Fnphases);
-{==============================================================================}
+ PropertyType[ord(TProp.kvars)] := TPropertyType.DoubleVArrayProperty;
+ PropertyOffset[ord(TProp.kvars)] := ptruint(@obj.Sensorkvar);
+ PropertyOffset2[ord(TProp.kvars)] := ptruint(@obj.Fnphases);
-function TSensor.NewObject(const ObjName: String): Integer;
-begin
- // Make a new Sensor and add it to Sensor class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TSensorObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
-end;
+ // enum properties
+ PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.conn)] := ptruint(@obj.FConn);
+ PropertyOffset2[ord(TProp.conn)] := PtrInt(DSS.ConnectionEnum);
-{==============================================================================}
+ // object reference
+ PropertyType[ord(TProp.element)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.element)] := ptruint(@obj.MeteredElement);
+ PropertyOffset2[ord(TProp.element)] := 0;
+ //PropertyFlags[ord(TProp.element)] := [TPropertyFlag.CheckForVar]; // not required for general cktelements
-function TSensor.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
- DoRecalcElementData: Boolean;
+ // integer properties
+ PropertyType[ord(TProp.terminal)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.terminal)] := ptruint(@obj.MeteredTerminal);
-begin
+ PropertyType[ord(TProp.DeltaDirection)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.DeltaDirection)] := ptruint(@obj.FDeltaDirection);
- // continue parsing with contents of Parser
- DSS.ActiveSensorObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveSensorObj;
+ // boolean properties
+ PropertyType[ord(TProp.clear)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.clear)] := ptruint(@obj.ClearSpecified);
- Result := 0;
- DoRecalcElementData := FALSE;
+ // double properties (default type)
+ PropertyOffset[ord(TProp.kvbase)] := ptruint(@obj.kVBase);
+ PropertyOffset[ord(TProp.pctError)] := ptruint(@obj.pctError);
+ PropertyOffset[ord(TProp.Weight)] := ptruint(@obj.Weight);
- with DSS.ActiveSensorObj do
- begin
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
+end;
+
+function TSensor.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
+begin
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
+end;
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
+procedure TSensorObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
+begin
+ case Idx of
+ 1..2:
begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 661);
- 1:
- ElementName := lowercase(param);
- 2:
- MeteredTerminal := Parser.IntValue;
- 3:
- kVBase := Parser.DblValue;
- 4:
- ClearSpecified := InterpretYesNo(Param);
- 5:
- Parser.ParseAsVector(Fnphases, SensorVoltage); // Inits to zero
- 6:
- Parser.ParseAsVector(Fnphases, SensorCurrent); // Inits to zero
- 7:
- begin
- Parser.ParseAsVector(Fnphases, SensorkW);
- Pspecified := TRUE;
- UpdateCurrentVector;
- end;
- 8:
- begin
- Parser.ParseAsVector(Fnphases, Sensorkvar);
- Qspecified := TRUE;
- UpdateCurrentVector;
- end;
- 9:
- Conn := InterpretConnection(Param);
- 10:
- FDeltaDirection := LimitToPlusMinusOne(Parser.IntValue);
- 11:
- pctError := Parser.dblValue;
- 12:
- Weight := Parser.dblValue;
- 13:
- Action := Param; // Put sq error in Global Result
+ Include(Flags, Flg.NeedsRecalc);
+ MeteredElementChanged := TRUE;
+ end;
+ 3:
+ Include(Flags, Flg.NeedsRecalc);
+
+ // Do not recalc element data for setting of sensor quantities
+ 4:
+ if ClearSpecified then
+ ClearSensor;
+ 5:
+ Vspecified := TRUE;
+ 6:
+ Ispecified := TRUE;
+ 7:
+ begin
+ Pspecified := TRUE;
+ UpdateCurrentVector;
+ end;
+ 8:
+ begin
+ Qspecified := TRUE;
+ UpdateCurrentVector;
+ end;
+ 9:
+ begin
+ RecalcVbase;
+ Include(Flags, Flg.NeedsRecalc);
+ end;
+ 10:
+ begin
+ if FDeltaDirection >= 0 then
+ FDeltaDirection := 1
else
- // Inherited parameters
- ClassEdit(DSS.ActiveSensorObj, ParamPointer - NumPropsthisClass)
- end;
-
- case ParamPointer of
- 1..2:
- begin
- DoRecalcElementData := TRUE;
- MeteredElementChanged := TRUE;
- end;
- 3:
- DoRecalcElementData := TRUE;
-
- {Do not recalc element data for setting of sensor quantities}
- 4:
- if ClearSpecified then
- ClearSensor;
- 5:
- Vspecified := TRUE;
- 6:
- Ispecified := TRUE;
- 7:
- Pspecified := TRUE;
- 8:
- Qspecified := TRUE;
-
- 9:
- DoRecalcElementData := TRUE;
- 10:
- DoRecalcElementData := TRUE;
- end;
+ FDeltaDirection := -1;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ Include(Flags, Flg.NeedsRecalc);
end;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
- if DoRecalcElementData then
+function TSensor.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
+ begin
+ if Flg.NeedsRecalc in Flags then
RecalcElementData;
+ Exclude(Flags, Flg.EditionActive);
end;
-
end;
-{==============================================================================}
-
-procedure TSensor.ResetAll; // Force all Sensors in the circuit to reset
-
+procedure TSensor.ResetAll; // Force all Sensors in the circuit to reset
var
pSensor: TSensorObj;
-
begin
-
pSensor := ActiveCircuit.Sensors.First;
while pSensor <> NIL do
begin
@@ -365,19 +294,12 @@ procedure TSensor.ResetAll; // Force all Sensors in the circuit to reset
pSensor.ResetIt;
pSensor := ActiveCircuit.Sensors.Next;
end;
-
end;
-{==============================================================================}
-
-procedure TSensor.SampleAll; // Force all Sensors in the circuit to take a sample
-
+procedure TSensor.SampleAll; // Force all Sensors in the circuit to take a sample
var
pSensor: TSensorObj;
-
begin
-
-
pSensor := ActiveCircuit.Sensors.First;
while pSensor <> NIL do
begin
@@ -385,53 +307,35 @@ procedure TSensor.SampleAll; // Force all Sensors in the circuit to take a samp
pSensor.TakeSample;
pSensor := ActiveCircuit.Sensors.Next;
end;
-
end;
-{==============================================================================}
-
-procedure TSensor.SaveAll; // Force all Sensors in the circuit to save their buffers to disk
-
-//VAR
-// Mon:TSensorObj;
-
+procedure TSensor.SaveAll; // Force all Sensors in the circuit to save their buffers to disk
begin
-{
- Mon := ActiveCircuit.Sensors.First;
- WHILE Mon<>Nil DO
- Begin
- If Mon.Enabled Then Mon.Save;
- Mon := ActiveCircuit.Sensors.Next;
- End;
-}
end;
-{==============================================================================}
-
procedure TSensor.SetHasSensorFlag;
// Set the HasSensorObj Flag for all cktElement;
var
i: Integer;
ThisSensor: TSensorObj;
CktElem: TDSSCktElement;
-
begin
- {Initialize all to FALSE}
+ // Initialize all to FALSE
with ActiveCircuit do
begin
CktElem := PDElements.First;
while CktElem <> NIL do
begin
- CktElem.HasSensorObj := FALSE;
+ Exclude(CktElem.Flags, Flg.HasSensorObj);
CktElem := PDElements.Next;
- end; {WHILE}
+ end;
CktElem := PCElements.First;
while CktElem <> NIL do
begin
- CktElem.HasSensorObj := FALSE;
+ Exclude(CktElem.Flags, Flg.HasSensorObj);
CktElem := PCElements.Next;
- end; {WHILE}
- end; {WITH}
+ end;
+ end;
for i := 1 to ActiveCircuit.Sensors.Count do
begin
@@ -439,65 +343,35 @@ procedure TSensor.SetHasSensorFlag;
with ThisSensor do
if MeteredElement <> NIL then
begin
- MeteredElement.HasSensorObj := TRUE;
+ Include(MeteredElement.Flags, Flg.HasSensorObj);
if MeteredElement is TPCElement then
TPCElement(MeteredElement).SensorObj := ThisSensor
else
TPDElement(MeteredElement).SensorObj := ThisSensor;
end;
- end; {FOR}
-
+ end;
end;
-{==============================================================================}
-
-function TSensor.MakeLike(const SensorName: String): Integer;
+procedure TSensorObj.MakeLike(OtherPtr: Pointer);
var
- OtherSensor: TSensorObj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this Sensor name in the present collection}
- OtherSensor := Find(SensorName);
- if OtherSensor <> NIL then
- with DSS.ActiveSensorObj do
- begin
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
- NPhases := OtherSensor.Fnphases;
- NConds := OtherSensor.Fnconds; // Force Reallocation of terminal stuff
-
- ElementName := OtherSensor.ElementName;
- MeteredElement := OtherSensor.MeteredElement; // Pointer to target circuit element
- MeteredTerminal := OtherSensor.MeteredTerminal;
-{==========================================================================}
-
-
-{==========================================================================}
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherSensor.PropertyValue[i];
-
- BaseFrequency := OtherSensor.BaseFrequency;
-
- end
- else
- DoSimpleMsg('Error in Sensor MakeLike: "' + SensorName + '" Not Found.', 662);
+ MeteredElement := Other.MeteredElement; // Pointer to target circuit element
+ MeteredTerminal := Other.MeteredTerminal;
+ BaseFrequency := Other.BaseFrequency;
end;
-{==========================================================================}
-{ TSensorObj }
-{==========================================================================}
-
-
-{==============================================================================}
-
constructor TSensorObj.Create(ParClass: TDSSClass; const SensorName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(SensorName);
+ Name := AnsiLowerCase(SensorName);
- Nphases := 3; // Directly set conds and phases
+ FNphases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 1; // this forces allocation of terminals and conductors
// in base class
@@ -509,56 +383,42 @@ constructor TSensorObj.Create(ParClass: TDSSClass; const SensorName: String);
Weight := 1.0;
pctError := 1.0;
- Conn := 0; // Wye
+ FConn := 0; // Wye
+ RecalcVbase();
ClearSensor;
DSSObjType := ParClass.DSSClassType; //SENSOR_ELEMENT;
-
- InitPropertyValues(0);
-
- // RecalcElementData;
-
+ // RecalcElementData;
end;
-{==============================================================================}
-
destructor TSensorObj.Destroy;
begin
- ElementName := '';
ReAllocMem(SensorkW, 0);
ReAllocMem(Sensorkvar, 0);
inherited Destroy;
end;
-{==============================================================================}
-
procedure TSensorObj.RecalcElementData;
-
-var
- DevIndex: Integer;
-
begin
+ Exclude(Flags, Flg.NeedsRecalc);
ValidSensor := FALSE;
- Devindex := GetCktElementIndex(ElementName); // Global function
- if DevIndex > 0 then
+ if MeteredElement <> NIL then
begin // Sensored element must already exist
- MeteredElement := ActiveCircuit.CktElements.Get(DevIndex);
-
if MeteredTerminal > MeteredElement.Nterms then
begin
- DoErrorMsg('Sensor: "' + Name + '"',
- 'Terminal no. "' + '" does not exist.',
- 'Respecify terminal no.', 665);
+ DoErrorMsg(Format(_('Sensor: "%s"'), [Name]),
+ Format(_('Terminal no. "%d" does not exist.'), [MeteredTerminal]),
+ _('Respecify terminal no.'), 665);
end
else
begin
- Nphases := MeteredElement.NPhases;
+ FNphases := MeteredElement.NPhases;
Nconds := MeteredElement.NConds;
- // Sets name of i-th terminal's connected bus in Sensor's buslist
- // This value will be used to set the NodeRef array (see TakeSample)
+ // Sets name of i-th terminal's connected bus in Sensor's buslist
+ // This value will be used to set the NodeRef array (see TakeSample)
Setbus(1, MeteredElement.GetBus(MeteredTerminal));
ClearSensor;
@@ -569,22 +429,22 @@ procedure TSensorObj.RecalcElementData;
ZeroSensorArrays;
RecalcVbase;
end;
-
- end
- else
- begin
- MeteredElement := NIL; // element not found
- DoErrorMsg('Sensor: "' + Self.Name + '"', 'Circuit Element "' + ElementName + '" Not Found.',
- ' Element must be defined previously.', 666);
+ Exit;
end;
+
+ // element not found/set
+ DoErrorMsg(
+ Format(_('Sensor: "%s"'), [Self.Name]),
+ _('Circuit Element is not set.'),
+ _('Element must be defined previously.'), 666);
end;
-procedure TSensorObj.MakePosSequence;
+procedure TSensorObj.MakePosSequence();
begin
if MeteredElement <> NIL then
begin
Setbus(1, MeteredElement.GetBus(MeteredTerminal));
- Nphases := MeteredElement.NPhases;
+ FNphases := MeteredElement.NPhases;
Nconds := MeteredElement.Nconds;
ClearSensor;
ValidSensor := TRUE;
@@ -595,8 +455,6 @@ procedure TSensorObj.MakePosSequence;
inherited;
end;
-{==============================================================================}
-
procedure TSensorObj.RecalcVbase;
begin
case Fconn of
@@ -611,8 +469,6 @@ procedure TSensorObj.RecalcVbase;
end;
end;
-{==============================================================================}
-
procedure TSensorObj.CalcYPrim;
begin
// leave YPrims as nil and they will be ignored
@@ -620,19 +476,12 @@ procedure TSensorObj.CalcYPrim;
// IF YPrim=nil THEN YPrim := TcMatrix.CreateMatrix(Yorder);
end;
-{==============================================================================}
-
procedure TSensorObj.ResetIt;
-
{What does it mean to reset a sensor?}
begin
-
ClearSensor;
-
end;
-{==============================================================================}
-
function TSensorObj.RotatePhases(const j: Integer): Integer;
// For Delta connections or Line-Line voltages
begin
@@ -653,8 +502,6 @@ function TSensorObj.RotatePhases(const j: Integer): Integer;
end;
-{==============================================================================}
-
procedure TSensorObj.TakeSample;
var
i: Integer;
@@ -667,39 +514,31 @@ procedure TSensorObj.TakeSample;
case Fconn of
1:
for i := 1 to Fnphases do
- CalculatedVoltage^[i] := Csub(VTerminal^[i], VTerminal^[RotatePhases(i)]);
+ CalculatedVoltage^[i] := VTerminal^[i] - VTerminal^[RotatePhases(i)];
else
for i := 1 to Fnphases do
CalculatedVoltage^[i] := VTerminal^[i];
end;
-
- {NOTE: CalculatedVoltage is complex}
-
+ // NOTE: CalculatedVoltage is complex
end;
-{==============================================================================}
-
procedure TSensorObj.GetCurrents(Curr: pComplexArray); //Get present value of terminal Curr for reports
var
i: Integer;
begin
-{
- Return array of zero
-}
+ // Return array of zero
for i := 1 to Fnconds do
Curr^[i] := CZERO;
end;
-{==============================================================================}
-
procedure TSensorObj.UpdateCurrentVector;
-{Updates the currentvector when P and Q are defined
- as the input vectors for the sensor}
+// Updates the currentvector when P and Q are defined
+// as the input vectors for the sensor
var
kVA: Double;
i: Integer;
begin
-{Convert P and Q specification to Currents}
+ // Convert P and Q specification to Currents
if Pspecified then
begin // compute currents assuming vbase
if Qspecified then
@@ -722,19 +561,14 @@ procedure TSensorObj.UpdateCurrentVector;
end;
function TSensorObj.Get_WLSCurrentError: Double;
-{
- Return the WLS Error for Currents
- Get Square error and weight it
-
-}
-
+// Return the WLS Error for Currents
+// Get Square error and weight it
var
kVA: Double;
i: Integer;
begin
-
Result := 0.0;
-{Convert P and Q specification to Currents}
+ // Convert P and Q specification to Currents
if Pspecified then
begin // compute currents assuming vbase
if Qspecified then
@@ -762,11 +596,8 @@ function TSensorObj.Get_WLSCurrentError: Double;
end;
Result := Result * Weight;
-
end;
-{==============================================================================}
-
function TSensorObj.Get_WLSVoltageError: Double;
// Get Square error and weight it
var
@@ -781,31 +612,6 @@ function TSensorObj.Get_WLSVoltageError: Double;
Result := Result * Weight;
end;
-{==============================================================================}
-
-procedure TSensorObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
- if Complete then
- begin
- FSWriteln(F);
- end;
-
-end;
-
-{==============================================================================}
-
procedure TSensorObj.ClearSensor;
begin
Vspecified := FALSE;
@@ -815,8 +621,6 @@ procedure TSensorObj.ClearSensor;
ClearSpecified := FALSE;
end;
-{==============================================================================}
-
procedure TSensorObj.AllocateSensorObjArrays;
begin
ReAllocMem(SensorkW, Sizeof(SensorkW^[1]) * Fnphases);
@@ -824,8 +628,6 @@ procedure TSensorObj.AllocateSensorObjArrays;
AllocateSensorArrays;
end;
-{==============================================================================}
-
procedure TSensorObj.ZeroSensorArrays;
var
i: Integer;
@@ -839,69 +641,8 @@ procedure TSensorObj.ZeroSensorArrays;
end;
end;
-{==============================================================================}
-
-procedure TSensorObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := ''; //'element';
- PropertyValue[2] := '1'; //'terminal';
- PropertyValue[3] := '12.47'; //'kVBase';
- PropertyValue[4] := 'No'; // Must be set to yes to clear before setting quantities
- PropertyValue[5] := '[7.2, 7.2, 7.2]';
- PropertyValue[6] := '[0.0, 0.0, 0.0]'; // currents
- PropertyValue[7] := '[0.0, 0.0, 0.0]'; // P kW
- PropertyValue[8] := '[0.0, 0.0, 0.0]'; // Q kvar
- PropertyValue[9] := 'wye';
- PropertyValue[10] := '1';
- PropertyValue[11] := '1'; // %Error
- PropertyValue[12] := '1'; // %Error
- PropertyValue[13] := ''; // Action
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-
-{==============================================================================}
-
-function TSensorObj.LimitToPlusMinusOne(const i: Integer): Integer;
-begin
- if i >= 0 then
- Result := 1
- else
- Result := -1;
-end;
-
-{==============================================================================}
-
procedure TSensorObj.Save;
begin
-
-end;
-
-
-{==============================================================================}
-
-procedure TSensorObj.Set_Conn(const Value: Integer);
-{Interpret the Connection}
-begin
- Fconn := Value;
- RecalcVbase;
end;
-{==============================================================================}
-
-procedure TSensorObj.Set_Action(const Value: String);
-{Interpret Action Property}
-begin
-
-end;
-
-{==============================================================================}
-
-initialization
- //WriteDLLDebugFile('Sensor');
-
-end.
+end.
\ No newline at end of file
diff --git a/src/Meters/VLNodeVars.pas b/src/Meters/VLNodeVars.pas
deleted file mode 100644
index 9896653ef..000000000
--- a/src/Meters/VLNodeVars.pas
+++ /dev/null
@@ -1,109 +0,0 @@
-unit VLNodeVars;
-
-{
- ----------------------------------------------------------
- Copyright (c) 2017, Electric Power Research Institute, Inc.
- Added by Ying.
- ----------------------------------------------------------
-
- Definition of Fmonitor (virtue leader) Public Data Record
-}
-
-interface
-
-uses
- UComplex;
- //PointerList;
-
-type
- pNodeVar = ^TVLNodeVars;
- {Fmonitor public data/state variable structure}
-
- //value ot save communication delay
- //TDelays = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}packed{$ENDIF} record
- Tdlys = array [0..99] of Double; //max 99
-
- //end;
- TVLNodeVars = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}packed{$ENDIF} record
- //properties for Node
- vl_strBusName: String;
- vl_strMeasuredName: String;
- vl_terminalNum: Integer;
- vl_ndphases: Integer;
- vl_basevolt: Double;
- vl_nodeType_phase: array [1..3] of Smallint; // set by TFMonitorObj.Init_nodeFM :
- //for each phase
- //1, dg under it; 2, no dg there
- //if a dg is connected, it is 1;
-
- vl_V, vl_V1, vl_V2, vl_V3: Double;
-
- Bus_Idx: Integer; // has to be updated before being used
- // it is related to YMatrix
- Yii: array [0..3] of complex; //each phase 123 - ABC, 0 - pos seq
- Yij: array [0..3] of complex; //each phase 123 - ABC, 0 - pos seq
-
- // complex voltage
- vl_V_c, vl_V_1c, vl_V_2c, vl_V_3c: complex;
- //Properties for DG
- vl_strName_dg: String;
- //vl_terminalNum : integer;
- vl_ndphases_dg: Integer; // set by TFMonitorObj.Init_nodeFM: 1,3
- vl_phase_num_dg: Integer; // set by TFMonitorObj.Init_nodeFM, 123--abc 0- this node has 3-phase
- //if vl_nodeType=1, and vl_ndphases=1,phase_num =1,2,3
- //SmallIntArray = Array[1..100] of SmallInt;
- //vl_strBusName : string;
- vl_CC_switch_dg: Boolean;// cooperate control switch. true, cooperate control is on
- vl_PF_flag_dg: Integer;//1, real power control is on
- vl_QV_flag_dg: Integer;//1, volt/var control is on
- vl_volt_thrd_dg: Double;
- //vl_phase_select
- vl_Alpha_dg,
- vl_Alpha1_dg, vl_Alpha2_dg, vl_Alpha3_dg,
- vl_Alpha_dgn,
- vl_Gradient_dg, vl_Gradient1_dg, vl_Gradient2_dg, vl_Gradient3_dg: Double;
-
- // communication array for alpha and others can be improved
- vl_smpl_dg: array [1..6] of Tdlys; //1: alpha; 2: alphaP; 3, bus voltage 0 seq. ; 4,5,6: bus voltage ABC
-
- //
- vl_SmplCnt: Integer; //sample count for this agent
- vl_crnt_smp_time: Double; //time for current sample at this agent
-
- vl_AlphaP_dg,
- vl_AlphaP1_dg, vl_AlphaP2_dg, vl_AlphaP3_dg: Double;
- vl_GradientP_dg, vl_GradientP1_dg, vl_GradientP2_dg, vl_GradientP3_dg: Double;
- vl_Pmax_dg, vl_Qmax_dg,
- vl_Pmax_phase_dg, vl_Qmax_phase_dg: Double;
- vl_V_base_dg,
- vl_V_ref_dg, vl_V_ref1_dg, vl_V_ref2_dg, vl_V_ref3_dg: Double;// nominal value with respect to p.u. 1 //must be set by initialization
- vl_kcq_dg: Double; // the step size gain of agent i //has to be defined befor used
- vl_p_DG, vl_p_DG1, vl_p_DG2, vl_p_DG3: Double;
- vl_kcd_dg: Double; // the step size gain of agent i //has to be defined befor used
- vl_kc_ul_dg: Double; // the cooperative gain for agent i
- vl_q_DG, vl_q_DG1, vl_q_DG2, vl_q_DG3: Double;
- //Properties for Loads
- ldType: Integer; //-1: noload; 0: one 3phase or 2phase load; 1, 2, 3: 1,2 or 3 single loads;
- ldIdx, ldIdx1, ldIdx2, ldIdx3: Integer;
- vl_Q_Di: Double; //all load reactive power except DG
- vl_Q_Di1: Double; //
- vl_Q_Di2: Double; //
- vl_Q_Di3: Double; //
- vl_P_Di: Double; //all load reactive power except DG
- vl_P_Di1: Double; //
- vl_P_Di2: Double; //
- vl_P_Di3: Double; //
- // vl_NodeRef : integer;// for global use
-
- // attack and defense
- d_atk, z_dfs: Double;
- z_dfsn: Double;
- d_atk0: Double;
- end;
- pNodeArray = ^NodeArray;
- NodeArray = array [1..33] of TVLNodeVars;
-
-
-implementation
-
-end.
diff --git a/src/Meters/fMonitor.pas b/src/Meters/fMonitor.pas
deleted file mode 100644
index 1560a6fec..000000000
--- a/src/Meters/fMonitor.pas
+++ /dev/null
@@ -1,3267 +0,0 @@
-unit fMonitor;
-
-{
- ----------------------------------------------------------
- Copyright (c) 2008-2019, Electric Power Research Institute, Inc.
- All rights reserved.
- ----------------------------------------------------------
-}
-
-{
- Change Log
- By Ying @UCF 10/27/2017
-
-}
-
-interface
-
-USES
- Command, MeterClass, Meterelement, DSSClass, Arraydef, ucomplex,
- utilities, Classes, DSSPointerList,
- VLNodeVars,LD_fm_infos;
-TYPE
- TFMonitorStrBuffer = Array[1..256] of AnsiChar;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
- {This has to be named TDSSMonitor because Delphi has a TMonitor Class and the compiler will get confused}
- TDSSFMonitor = class(TMeterClass)
- private
-
- protected
- Procedure DefineProperties;
- Function MakeLike(const MonitorName:String):Integer; Override;
- public
-
-
- constructor Create(dssContext: TDSSContext);
- destructor Destroy; override;
-
- Function Edit:Integer; override; // uses global parser
- Function NewObject(const ObjName:String):Integer; override;
-
- Procedure ResetAll; Override;
- Procedure SampleAll; Override; // Force all monitors to take a sample
- //Procedure SampleAllMode5; // Sample just Mode 5 monitors
- Procedure SaveAll; Override; // Force all monitors to save their buffers to disk
- {update FM leader information}
- Procedure update_sys_ld_info; //all FMs
-
- end;
-
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
- TFMonitorObj = class(TMeterElement)
- private
- //pTVLeaderVars : TDSSPointerList; //save all nodes information
- //VL_node : TVLnodeVars;// leader node of this cluster
- nodes : integer; //nodes of this cluster \\default nodes := 33;
- pCommMatrix : pSmallIntArray;// communication matrix of this cluster
- P_trans_ref : double; // Power Ref on metered elemet, if mode =1 real power of this cluster will be used
- p_mode : integer;
- tempCplx : complex;
-
- BufferSize :Integer;
- Hour :Integer;
- Sec :Double; // last time entered in the buffer
- MonBuffer :pSingleArray;
- Bufptr :Integer; // point to present (last) element in buffer must be incremented to add
-
- CurrentBuffer :pComplexArray;
- VoltageBuffer :pComplexArray;
-
- NumStateVars :Integer;
- StateBuffer :pDoubleArray;
-
- FlickerBuffer :pComplexArray; // store phase voltages in polar form
- // then convert to re=flicker level, update every time step
- // and im=Pst, update every 10 minutes
- SolutionBuffer :pDoubleArray;
-
- IncludeResidual :Boolean;
- VIpolar :Boolean;
- Ppolar :Boolean;
-
- FileSignature :Integer;
- FileVersion :Integer;
-
- BaseFrequency :Double;
- {-------------}
- F_Value_one : double;//test;
- //Voltages
- F_Value_one_V : pDoubleArray; //Measured voltage by each FMonitor(p.u.)
- F_Value_one_S : pComplexArray; //Measured apparent power for each phase by each Fmonitor
- Fvalue_P : Double; //This variable is used to store total measure three-phase active power of any Fmonitor
- Fvalue_Q : Double; //This variable is used to store total measure three-phase reactive power of any Fmonitor
-
- F_P_one, F_Q_one :double;//measured power
- P_ref_one :double; //the ref Power for this point
-
- Node_num : Integer; // Node number within the cluster
- V_Sensor : Integer; // Voltage sensor enable variable
- P_Sensor : Integer; // Power sensor enable variable
- Cluster_num : integer; // the group number for this
- Total_Clusters : Integer; //Total Number of the Groups in a circuit
-
- //communication time
- T_intvl_smpl : double; //Sampling interval.
- MaxLocalMem : integer; //Max number of local memory, no large than 99
- Smpl_stps : integer; // T_Comm/ ActiveCircuit.Solution.Dynavars.h.
- pCommDelayMatrix : pDoubleArray; //
- pCommDelaySteps : pSmallIntArray;// Communication delay step matrix of this cluster
-
- //delay to uppper level
- up_dly : double; //in seconds
- nup_dlys : integer; //nup_dlys := up_dly / t_intvl_smpl;
- virtual_Ld_Nd : integer;// denotes which node talks to upper level
- // default by 1;
-
- Bus_code :Integer;
- NodeNum : Integer;
- Node_Ref : Integer;
- {------------}
- BufferFile :String; // Name of file for catching buffer overflow
-
- IsFileOpen :Boolean;
- ValidMonitor :Boolean;
- IsProcessed :Boolean;
-
- //Procedure AddDblsToBuffer(Dbl:pDoubleArray; Ndoubles:Integer);
- //Procedure AddDblToBuffer(const Dbl:Double);
-
- //Procedure DoFlickerCalculations; // call from CloseMonitorStream
- Procedure Set_nodes_for_fm(intNodes : integer);//initiate the structure of this FMon
- Procedure Set_CommVector( strParam: string);
- Procedure Set_volt_lmt_clstr( strParam: string);
-
- Procedure Set_CommDelayVector( strParam: string);
- Procedure ResetDelaySteps(iNodeNum: integer);
-
- Procedure Set_ElemTable_line( strParam: string);
- Procedure Init_nodeFM(iNodeNum : integer);
- //Procedure push_voltage;
- Procedure Get_PDElem_terminal_voltage(nd_num_in_cluster: integer ;devName: string; Tern_num: integer); //
- procedure Calc_Alpha_for_PDNode(NodeNum :Integer);
- Procedure update_all_nodes_info;
- Function AvgPmax : double;
- Function AvgQmax : double;
- Procedure Get_PQ_DI(i_NodeNum : integer);
- Function Calc_Grdt_for_Alpha(NodeNuminClstr, phase_num,ActorID:Integer):Double;
- Function Calc_Grdt_for_Alpha_vivj(NodeNuminClstr, phase_num,ActorID : Integer):Double;
-
- Function Calc_GP_AlphaP(NodeNuminClstr, phase_num, ActorID:Integer):Double;
- Function Get_power_trans :Double;
- public
- pNodeFMs : pNodeArray;
-
- Mode :Integer;
- //MonitorStream :TMemoryStream;
- SampleCount :Integer; // This is the number of samples taken
- {-- overview information about this cluster--}
- ld_fm_info : array [0..3] of TLD_fm_infos;
-
- constructor Create(ParClass:TDSSClass; const MonitorName:String);
- destructor Destroy; override;
-
- PROCEDURE MakePosSequence; Override; // Make a positive Sequence Model, reset nphases
- Procedure RecalcElementData; Override;
- Procedure CalcYPrim; Override; // Always Zero for a monitor
- //Procedure TakeSample; Override; // Go add a sample to the buffer
- {}
- //Procedure GetFvalue;
- //Function PhaseDetect(Bus_name:string):Integer;
- {}
- Procedure ResetIt;
- Procedure Save; // Saves present buffer to file
- //Procedure PostProcess; // calculates Pst or other post-processing
- //
-
- Function Calc_sum_dij_Alphaj(NodeNumofDG, phase_num,ActorID:Integer):Double;
- //unified voltage (1-V_dg)^2
- Function Calc_Alpha_M2(NodeNumofDG, phase_num:Integer; dbNodeRef: integer; Bii,beta,Volt_Trhd: double):Double;
- //minimize loss
- Function Calc_Alpha_L(NodeNumofDG, phase_num:Integer; dbNodeRef: integer; Bii,beta,Volt_Trhd: double):Double;
- Function Calc_Alpha_L_vivj(NodeNumofDG, phase_num:Integer; dbNodeRef: integer; Bii,beta,Volt_Trhd: double):Double;
- Function Calc_Alpha_LnM2(NodeNumofDG, phase_num:Integer; dbNodeRef: integer; Bii,beta,Volt_Trhd: double):Double;
-
- Function Calc_AlphaP(NodeNuminClstr, phase_num, ActorID:Integer):Double;
- Function Get_P_mode( ActorID : Integer):integer;
-
- //Zero seq.
- Function Calc_fm_ul_0(NodeNumofDG, phase_num:Integer; dbNodeRef: integer; Bii,beta,Volt_Trhd: double): double;
- Function Calc_fm_us_0(NodeNumofDG, phase_num:Integer; dbNodeRef: integer; Bii,beta,Volt_Trhd: double): double;
- Procedure Agnt_smpl(NodeNumofDG, phase_num,ActorID:Integer); // sample data of this node at each t_intvl_smpl
- Procedure Init_delay_array(NodeNumofDG, ActorID:Integer);
- {For real power control-dynamic simu}
- Function Calc_ul_P(NodeNuminClstr, phase_num:Integer):Double;
- Function Calc_Gradient_ct_P(NodeNuminClstr, phase_num, ActorID:Integer):Double; // curtailment
- {--}
- Procedure update_ld_info( ActorID: integer); //all nodes in the cluster
- Procedure update_ld_dly( ActorID: integer); // all nodes in this cluster with delay
- // Procedure TranslateToCSV(Show:Boolean);
-
- Procedure GetCurrents(Curr: pComplexArray); Override; // Get present value of terminal Curr
- PROCEDURE InitPropertyValues(ArrayOffset:Integer); Override;
- Procedure DumpProperties(F: TFileStream; Complete:Boolean);Override;
- function Get_FileName: String;
-
- // Property CSVFileName:String Read Get_FileName;
- end;
-
-
-{--------------------------------------------------------------------------}
-implementation
-
-USES
-
- ParserDel, DSSClassDefs, DSSGlobals, Circuit, CktTree, CktElement,Transformer, PCElement, PDElement,
- Sysutils, ucmatrix, showresults, mathUtil , TOPExport, Dynamics, PstCalc,
- {} Terminal, Generic5OrderMach, strutils,
- Capacitor,
- Load,
- DSSHelper,
- DSSObjectHelper,
- TypInfo;
-
-CONST
- SEQUENCEMASK = 17;
- MAGNITUDEMASK = 32;
- POSSEQONLYMASK = 64;
- MODEMASK = 15;
-
- NumPropsThisClass = 24;//22;//21;//20;//17; //12;// 9; //8;//7;//add P_ref_one
- NumSolutionVars = 12;
-
-VAR
- StrBuffer:TFMonitorStrBuffer;
-
-{--------------------------------------------------------------------------}
-constructor TDSSFMonitor.Create(dssContext: TDSSContext); // Creates superstructure for all Monitor objects
-
-Begin
- Inherited Create(dssContext);
-
- Class_name := 'FMonitor';
- DSSClassType := DSSClassType + FMON_ELEMENT;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
-End;
-
-{--------------------------------------------------------------------------}
-destructor TDSSFMonitor.Destroy;
-
-Begin
- Inherited Destroy;
-End;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Procedure TDSSFMonitor.DefineProperties;
-Begin
-
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
-
- PropertyName[1] := 'element';
- PropertyName[2] := 'terminal';
- PropertyName[3] := 'mode';
- PropertyName[4] := 'action'; // buffer=clear|save
- PropertyName[5] := 'residual'; // buffer=clear|save
- PropertyName[6] := 'VIPolar'; // V I in mag and angle rather then re and im
- PropertyName[7] := 'PPolar'; // Power in power PF rather then power and vars
- PropertyName[8] := 'P_trans_ref';
- PropertyName[9] := 'V_Sensor';
- PropertyName[10] := 'P_Sensor';
- PropertyName[11] := 'Node_num';
- PropertyName[12] := 'Cluster_num';
- PropertyName[13] := 'Total_Clusters';
- PropertyName[14] := 'Nodes';
- PropertyName[15] := 'CommVector';
- PropertyName[16] := 'ElemTableLine';
- PropertyName[17] := 'P_Mode'; //real power control mode
- PropertyName[18] := 'CommDelayVector';
- PropertyName[19] := 'T_intvl_smpl'; //real power control mode
- PropertyName[20] := 'MaxLocalMem';
- PropertyName[21] := 'Volt_limits_pu';// set limits for this cluster {0,1.05, 0.95}
- PropertyName[22] := 'b_Curt_Ctrl';// set P curtailment on/off
- PropertyName[23] := 'up_dly';// delay time to communicate to upper level
- PropertyName[24] := 'virtual_ld_node';// delay time to communicate to upper level
-
- PropertyHelp[1] := 'Name (Full Object name) of element to which the monitor is connected.';
- PropertyHelp[2] := 'Number of the terminal of the circuit element to which the monitor is connected. '+
- '1 or 2, typically. For monitoring states, attach monitor to terminal 1.';
- PropertyHelp[3] := 'Bitmask integer designating the values the monitor is to capture: '+CRLF+
- '0 = Voltages and currents' + CRLF+
- '1 = Powers'+CRLF+
- '2 = Tap Position (Transformers only)'+CRLF+
- '3 = State Variables (PCElements only)' +CRLF+
- '4 = Flicker level and severity index (Pst) for voltages. No adders apply.' +CRLF+
- ' Flicker level at simulation time step, Pst at 10-minute time step.' +CRLF+
- '5 = Solution variables (Iterations, etc).' +CRLF+CRLF+
- 'Normally, these would be actual phasor quantities from solution.' + CRLF+
- '6 = Capacitor Switching (Capacitors only)'+CRLF+
- 'Combine with adders below to achieve other results for terminal quantities:' + CRLF+
- '+16 = Sequence quantities' + CRLF+
- '+32 = Magnitude only' + CRLF+
- '+64 = Positive sequence only or avg of all phases' + CRLF+
- CRLF +
- 'Mix adder to obtain desired results. For example:' + CRLF+
- 'Mode=112 will save positive sequence voltage and current magnitudes only' + CRLF+
- 'Mode=48 will save all sequence voltages and currents, but magnitude only.';
- PropertyHelp[4] := '{Clear | Save | Take | Process}' + CRLF +
- '(C)lears or (S)aves current buffer.' + CRLF +
- '(T)ake action takes a sample.'+ CRLF +
- '(P)rocesses the data taken so far (e.g. Pst for mode 4).' + CRLF + CRLF +
- 'Note that monitors are automatically reset (cleared) when the Set Mode= command is issued. '+
- 'Otherwise, the user must explicitly reset all monitors (reset monitors command) or individual ' +
- 'monitors with the Clear action.';
- PropertyHelp[5] := '{Yes/True | No/False} Default = No. Include Residual cbannel (sum of all phases) for voltage and current. ' +
- 'Does not apply to sequence quantity modes or power modes.';
- PropertyHelp[6] := '{Yes/True | No/False} Default = YES. Report voltage and current in polar form (Mag/Angle). (default) Otherwise, it will be real and imaginary.';
- PropertyHelp[7] := '{Yes/True | No/False} Default = YES. Report power in Apparent power, S, in polar form (Mag/Angle).(default) Otherwise, is P and Q';
- PropertyHelp[8] := 'P_trans_ref: P ref value for metered element(unit kW)';
- PropertyHelp[9] := 'V_Sensor'+ CRLF+
- 'Enable voltage sensor';
- PropertyHelp[10] := 'P_Sensor'+ CRLF+
- 'Enable power sensor';
- PropertyHelp[11] := 'Node_num' + CRLF+
- 'Assign a node number within a cluster';
- PropertyHelp[12] := 'Cluster_num';
- PropertyHelp[13] := 'Total_Clusters.' + CRLF+
- 'Define the total number of groups in a circuit' + CRLF+
- 'Just use for the first defined FMonitor';
- PropertyHelp[14] := 'Nodes connected to this FMonitor. Example:(Nodes=33)';
- PropertyHelp[15] := 'CommVector of this FMonitor. ' + CRLF+
- 'The first entry of this vector is the number of '+ CRLF+
- 'Example:(CommVector={2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})'+ CRLF+
- 'The example show node #2 can communicate to node #1,#2,#3';
- PropertyHelp[16] := 'ElemTableLine of the each node within this cluster. ' + CRLF+
- 'The first entry of this vector is the number of node within cluster '+ CRLF+
- 'The second entry of this vector is element name '+ CRLF+
- 'The third entry of this vector is terminal number '+ CRLF+
- 'The fourth entry of this vector is voltage sensor '+ CRLF+
- 'Example:(ElemTable={2,Line.1,1,1})'+ CRLF+
- 'The example show node #2 Element';
- PropertyHelp[17] := '0 = real Power controlled by each p_ref on each DG' + CRLF+
- '1 = real Power on MeteredElem controlled by DGs according to P_trans_ref'+CRLF+
- '2 = Not defined'+CRLF+
- '3 = Not defined' ;
-
- PropertyHelp[18] := 'CommDelayVector of this FMonitor. ' + CRLF+
- 'The first entry of this vector is the number of the node.'+ CRLF+
- 'Example:(CommVector={2,t1,0,t2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})'+ CRLF+
- 'The example show node #2 can communicate to node #1 and #3 with time delay t1 and t2 seperately';
- PropertyHelp[19] := 'T_intvl_smpl: '+ CRLF+
- 'The imformation of each agent will be sampled at each T_comm time. Unit is second.'
- + CRLF+'T_intvl_smpl is also the minimal communication time between neighbor nodes.'
- + CRLF+'If T_intvl_smpl=0.0, no delay for the communication is enabled in the simulation.';
- PropertyHelp[20] := 'MaxLocalMem: the max number of local memory siza. No larger than 99';
- PropertyHelp[21] := 'Volt_limits_pu: exmaple "Volt_limits_pu={a0,a1, a2}"'
- + CRLF+'a0: the phase number, 0 means pos. seq; a1: upper voltage limit of this cluster, usually 1.05;'
- + CRLF+'a2: upper voltage limit of this cluster, usually 0.95';// set limits for this cluster {0,1.05, 0.95}
- PropertyHelp[22] := 'b_Curt_Ctrl:set P curtailment on/off;'// set P curtailment on/off
- + CRLF+ 'b_Curt_Ctrl=True: P curtailment will be implemented according to the system voltage (default);'
- + CRLF+ 'b_Curt_Ctrl=False: P curtailment will not be implemented.';
- PropertyHelp[23] := 'up_dly: delay time to upper level. For example: "up_dly := 0.05"'
- + CRLF+ 'It can be used to simulate the time delay between clusters';
- PropertyName[24] := 'virtual_ld_node: which node talks to upper level. virtual_ld_node=1';
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
-End;
-
-{--------------------------------------------------------------------------}
-Function TDSSFMonitor.NewObject(const ObjName:String):Integer;
-Begin
- // Make a new Monitor and add it to Monitor class list
- With ActiveCircuit Do
- Begin
- ActiveCktElement := TFMonitorObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- End;
-End;
-{--------------------------------------------------------------------------}
-Function TDSSFMonitor.Edit:Integer;
-VAR
- ParamPointer:Integer;
- ParamName:String;
- Param:String;
- recalc: integer;
- i : integer;
-Begin
-
- // continue parsing with contents of Parser
- // continue parsing with contents of Parser
- ActiveFMonitorObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := ActiveFMonitorObj;
-
- Result := 0;
- recalc:=0;
-
- WITH ActiveFMonitorObj DO Begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- WHILE Length(Param)>0 DO Begin
- IF Length(ParamName) = 0 THEN Inc(ParamPointer)
- ELSE ParamPointer := CommandList.GetCommand(ParamName);
-
- If (ParamPointer>0) and (ParamPointer<=NumProperties) Then PropertyValue[ParamPointer]:= Param;
- inc (recalc);
-
- CASE ParamPointer OF
- 0: DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name +'.'+ Name + '"', 661);
- 1: Begin
- ElementName := ConstructElemName(lowercase(param)); // subtitute @var values if any
- PropertyValue[1] := ElementName;
- End;
- 2: MeteredTerminal := Parser.IntValue;
- 3: Mode := Parser.IntValue;
- 4: Begin
- param := lowercase(param);
- Case param[1] of
- 's':Save;
- 'c','r':ResetIt;
- 't': TakeSample;
- //'p': begin PostProcess; dec(recalc) end
- End;
- End; // buffer
- 5: IncludeResidual := InterpretYesNo(Param);
- 6: VIpolar := InterpretYesNo(Param);
- 7: Ppolar := InterpretYesNo(Param);
- 8: p_trans_ref := 1000 * Parser.dblValue;//kW for ref, unit of p_trans_ref is 'W'
- 9: V_Sensor := Parser.IntValue;//Voltage Sensor: Binary
- 10: P_Sensor := Parser.IntValue;//Power sensor : Binary
- //11: Node_num := Parser.IntValue;//Node number : integer
- 12: Cluster_num := Parser.IntValue;//group number: integer
- 13: Total_Clusters := Parser.IntValue;//Total number of the groups: integer
- 14: Set_nodes_for_fm(Parser.IntValue);//Nodes. Innitiate the structure
- 15: Set_CommVector(Param);//
- 16: Set_ElemTable_line(Param);//
- 17: p_mode := Parser.IntValue;
- 18: Set_CommDelayVector(Param);//
- 19: begin
- T_intvl_smpl := Parser.dblValue; //
- for i := 1 to nodes do
- ResetDelaySteps(i);
- end ;
- 20: MaxLocalMem := Parser.IntValue;
- 21: Set_volt_lmt_clstr(Param);
- 22: ld_fm_info[0].b_curt_ctrl := InterpretYesNo(Param); //curtailment
- 23: begin
- up_dly := Parser.dblValue;
- if t_intvl_smpl<>0.0 then
- begin
- if frac (up_dly /t_intvl_smpl)<>0.0 then
- nUp_dlys := trunc(up_dly /t_intvl_smpl)
- else nUp_dlys := trunc(up_dly /t_intvl_smpl)+1;
- end
- else nUp_dlys :=0;
- end;
- 24: virtual_Ld_Nd := Parser.IntValue;
- ELSE
- // Inherited parameters
- ClassEdit( ActiveFMonitorObj, ParamPointer - NumPropsthisClass)
- End;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- End;
-
- if recalc > 0 then RecalcElementData;
- End;
-
-End;
-
-{--------------------------------------------------------------------------}
-Procedure TDSSFMonitor.ResetAll; // Force all monitors in the circuit to reset
-
-VAR
- Mon:TFMonitorObj;
-
-Begin
- Mon := ActiveCircuit.FMonitors.First;
- WHILE Mon<>Nil DO
- Begin
- If Mon.enabled Then Mon.ResetIt;
- Mon := ActiveCircuit.FMonitors.Next;
- End;
-
-End;
-
-{--------------------------------------------------------------------------}
-Procedure TDSSFMonitor.SampleAll; // Force all monitors in the circuit to take a sample
-
-VAR
- Mon:TFMonitorObj;
-// sample all monitors except mode 5 monitors
-Begin
- Mon := ActiveCircuit.FMonitors.First;
- WHILE Mon<>Nil DO Begin
- If Mon.enabled Then
- If Mon.Mode <> 5 then Mon.TakeSample;
- Mon := ActiveCircuit.FMonitors.Next;
- End;
- //ProcessFvalue;
-End;
-//This function is used to measure total net power of a cluster
-Procedure TDSSFMonitor.update_sys_ld_info; //all FMs
-VAR
- FMon:TFMonitorObj;
- vtemp, dv_lwst : double;
-// sample all monitors except mode 5 monitors
-Begin
- ActiveCircuit.Solution.LD_FM[0].volt_hghst := -999999;
- ActiveCircuit.Solution.LD_FM[0].volt_lwst := 9999999;
- FMon := ActiveCircuit.FMonitors.First;
- WHILE FMon<>Nil DO Begin
- //update all agents information:
- //synchronous: voltage to agents
- //asynchronous: aphga, ahphaP, highest/lowest voltage
- If FMon.enabled Then
- //FMon.update_ld_info;
- FMon.update_ld_dly; //with delay
- //
- {Update cluster info to center}
- if ActiveCircuit.Solution.LD_FM[0].volt_hghst < FMon.ld_fm_info[0].volt_hghst then
- begin
- ActiveCircuit.Solution.LD_FM[0].volt_hghst := FMon.ld_fm_info[0].volt_hghst;
- ActiveCircuit.Solution.LD_FM[0].ndnum_hghst := FMon.ld_fm_info[0].ndnum_hghst;
- ActiveCircuit.Solution.LD_FM[0].clstr_num_hghst := FMon.Cluster_num;
- ActiveCircuit.Solution.LD_FM[0].volt_hgh_lmt := fmon.ld_fm_info[0].volt_hgh_lmt;
- ActiveCircuit.Solution.LD_FM[0].b_ctrl_hghst := FMon.ld_fm_info[0].b_ctrl_hghst;
- end;
- if ActiveCircuit.Solution.LD_FM[0].volt_lwst > FMon.ld_fm_info[0].volt_lwst then
- begin
- ActiveCircuit.Solution.LD_FM[0].volt_lwst := FMon.ld_fm_info[0].volt_lwst;
- ActiveCircuit.Solution.LD_FM[0].ndnum_lwst := FMon.ld_fm_info[0].ndnum_lwst;
- ActiveCircuit.Solution.LD_FM[0].clstr_num_lwst := FMon.Cluster_num;
- ActiveCircuit.Solution.LD_FM[0].volt_lw_lmt := fmon.ld_fm_info[0].volt_lw_lmt;
- ActiveCircuit.Solution.LD_FM[0].b_ctrl_lwst := FMon.ld_fm_info[0].b_ctrl_lwst;
- end;
-
- {---- curtailment ----- bCurtl := t/f for overall system ------}
- //curtailment is needed or not
- if FMon.ld_fm_info[0].volt_hghst > 1.0 then
- FMon.ld_fm_info[0].b_ctrl_hghst := true //need curtailment
- else FMon.ld_fm_info[0].b_ctrl_hghst := false;
- {---- each cluster may have their own ---}
- FMon := ActiveCircuit.FMonitors.Next;
- End;
- //curtailment is needed or not
- vtemp := ( activecircuit.Solution.LD_FM[0].volt_hghst - activecircuit.Solution.LD_FM[0].volt_lwst );//p.u.
- dv_lwst := activecircuit.Solution.LD_FM[0].volt_lwst - ActiveCircuit.Solution.LD_FM[0].volt_lw_lmt;//0.95; // must greater than 0.0
- if (dv_lwst<0.0) then
- begin
- activecircuit.Solution.bCurtl := true; //curtailment
- end
- else
- begin
- activecircuit.Solution.bCurtl := false;//dont need curtailment
- end;
-
-End;
-
-{--------------------------------------------------------------------------}
-{
-Procedure TDSSFMonitor.PostProcessAll;
-VAR
- Mon:TFMonitorObj;
-Begin
- Mon := ActiveCircuit.FMonitors.First;
- WHILE Mon<>Nil DO Begin
- If Mon.Enabled Then Mon.PostProcess;
- Mon := ActiveCircuit.FMonitors.Next;
- End;
-End;
-}
-{--------------------------------------------------------------------------}
-Procedure TDSSFMonitor.SaveAll; // Force all monitors in the circuit to save their buffers to disk
-
-VAR
- Mon:TFMonitorObj;
-
-Begin
- Mon := ActiveCircuit.FMonitors.First;
- WHILE Mon<>Nil DO Begin
- If Mon.Enabled Then Mon.Save;
- Mon := ActiveCircuit.FMonitors.Next;
- End;
-End;
-
-{--------------------------------------------------------------------------}
-Function TDSSFMonitor.MakeLike(const MonitorName:String):Integer;
-VAR
- OtherMonitor:TFMonitorObj;
- i:Integer;
-Begin
- Result := 0;
- {See if we can find this Monitor name in the present collection}
- OtherMonitor := Find(MonitorName);
- IF OtherMonitor<>Nil THEN
- WITH ActiveFMonitorObj DO Begin
-
- NPhases := OtherMonitor.Fnphases;
- NConds := OtherMonitor.Fnconds; // Force Reallocation of terminal stuff
-
- Buffersize := OtherMonitor.Buffersize;
- ElementName:= OtherMonitor.ElementName;
- MeteredElement:= OtherMonitor.MeteredElement; // Pointer to target circuit element
- MeteredTerminal:= OtherMonitor.MeteredTerminal;
- Mode := OtherMonitor.Mode;
- IncludeResidual := OtherMonitor.IncludeResidual;
-
- For i := 1 to ParentClass.NumProperties Do PropertyValue[i] := OtherMonitor.PropertyValue[i];
-
- BaseFrequency:= OtherMonitor.BaseFrequency;
-
- End
- ELSE DoSimpleMsg('Error in Monitor MakeLike: "' + MonitorName + '" Not Found.', 662);
-
-End;
-
-{==========================================================================}
-{ TFMonitorObj }
-{==========================================================================}
-
-
-
-{--------------------------------------------------------------------------}
-constructor TFMonitorObj.Create(ParClass:TDSSClass; const MonitorName:String);
-var
- i : integer;
-Begin
- Inherited Create(ParClass);
- Name := LowerCase(MonitorName);
-
- Nphases := 3; // Directly set conds and phases
- Fnconds := 3;
- Nterms := 1; // this forces allocation of terminals and conductors
- // in base class
-
- pNodeFMs := nil;
- //pNodeFMs := AllocMem(Sizeof(pNodeFMs^[1] )* 1);
-
- {Current Buffer has to be big enough to hold all terminals}
- CurrentBuffer := Nil;
- VoltageBuffer := Nil;
- StateBuffer := Nil;
- FlickerBuffer := Nil;
- SolutionBuffer:= Nil;
-
- Basefrequency := 60.0;
- Hour := 0;
- Sec := 0.0;
-
- Mode := 0; // Standard Mode: V & I, complex values
-
- BufferSize := 1024; // Makes a 4K buffer
- MonBuffer := AllocMem(Sizeof(MonBuffer^[1]) * BufferSize);
- BufPtr := 0;
-
- ElementName := TDSSCktElement(ActiveCircuit.CktElements.Get(1)).Name; // Default to first circuit element (source)
- MeteredElement := nil;
- Bufferfile := '';
-
- //MonitorStream := TMemoryStream.Create; // Create memory stream
-
- IsFileOpen := FALSE;
- MeteredTerminal := 1;
- IncludeResidual := FALSE;
- VIPolar := TRUE;
- Ppolar := TRUE;
- FileSignature := 43756;
- FileVersion := 1;
- SampleCount := 0;
- IsProcessed := FALSE;
-
- DSSObjType := ParClass.DSSClassType; //MON_ELEMENT;
-
- InitPropertyValues(0);
- Nodes := 33;//default Nodes in one cluster
-
- ReAllocMem(pCommMatrix, Nodes *Nodes * sizeof(pCommMatrix^[1]));
- {}
- ReAllocMem(f_Value_one_V, 999 * sizeof(f_Value_one_V^[1]));
- ReAllocMem(F_Value_one_S, 999 * sizeof(F_Value_one_S^[1]));
- {}
- T_intvl_smpl := 0;
- MaxLocalMem := 10;
- ReAllocMem(pCommDelayMatrix, Nodes *Nodes * sizeof(pCommDelayMatrix^[1]));
- ReAllocMem(pCommDelaySteps, Nodes *Nodes * sizeof(pCommDelaySteps^[1]));
-
- {leader information}
- for i := 0 to 3 do
- begin
-
- ld_fm_info[i].ndnum_hghst := 0;
- ld_fm_info[i].b_ctrl_hghst := false; //small number that can never be true
- ld_fm_info[i].volt_hghst := -1.0;
- ld_fm_info[i].volt_hgh_lmt := 1.05;
- ld_fm_info[i].Pinjec_hghst := 0.0;
- ld_fm_info[i].ndnum_lwst := 0;
- ld_fm_info[i].b_ctrl_lwst := false;
- ld_fm_info[i].volt_lw_lmt := 0.95;
- ld_fm_info[i].volt_lwst := 9999999999.0; //large nunber can never be true
- ld_fm_info[i].Pinjec_lwst := 0.0;
- ld_fm_info[i].volt_avg := 0.0;
- ld_fm_info[i].total_pg := 0.0;
- ld_fm_info[i].total_pl := 0.0;
- ld_fm_info[i].b_Curt_Ctrl := true;
- end;
- virtual_Ld_Nd := 1;
- nUp_dlys := 0;
- //bCurtl_Clstr := false;
- {end of leader initialization}
-End;
-
-destructor TFMonitorObj.Destroy;
-Begin
- //MonitorStream.Free;
- ElementName := '';
- Bufferfile := '';
- ReAllocMem(MonBuffer,0);
- ReAllocMem(StateBuffer,0);
- ReAllocMem(CurrentBuffer,0);
- ReAllocMem(VoltageBuffer,0);
- ReAllocMem(FlickerBuffer,0);
- ReAllocMem(SolutionBuffer,0);
- {}
- if Assigned(f_Value_one_V) then ReallocMem(f_Value_one_V, 0);
- if Assigned(F_Value_one_S) then ReallocMem(F_Value_one_S, 0);
- {}
- ReAllocMem(pNodeFMs,0);
- ReAllocMem(pCommMatrix,0);
- ReAllocMem(pCommDelayMatrix, 0);
- ReAllocMem(pCommDelaySteps, 0);
- Inherited Destroy;
-End;
-
-
-{--------------------------------------------------------------------------}
-Procedure ConvertBlanks(Var s:String);
-VAR
- BlankPos:Integer;
-
-Begin
- { Convert spaces to Underscores }
- BlankPos := Pos(' ', S);
- WHILE BlankPos>0 DO Begin
- S[BlankPos] := '_';
- BlankPos := Pos(' ', S);
- End;
-End;
-
-{--------------------------------------------------------------------------}
-Procedure TFMonitorObj.RecalcElementData;
-
-VAR
- DevIndex :Integer;
- i : integer;
-Begin
- ValidMonitor := FALSE;
- Devindex := GetCktElementIndex(ElementName); // Global function
- IF DevIndex>0 THEN Begin // Monitored element must already exist
- MeteredElement := ActiveCircuit.CktElements.Get(DevIndex);
- Case (Mode and MODEMASK) of
- 2: Begin // Must be transformer
- If (MeteredElement.DSSObjType And CLASSMASK) <> XFMR_ELEMENT Then Begin
- DoSimpleMsg(MeteredElement.Name + ' is not a transformer!', 663);
- Exit;
- End;
- End;
- 3: Begin // Must be PCElement
- If (MeteredElement.DSSObjType And BASECLASSMASK) <> PC_ELEMENT Then Begin
- DoSimpleMsg(MeteredElement.Name + ' must be a power conversion element (Load or Generator)!', 664);
- Exit;
- End;
- End;
- 6: begin // Checking Caps Tap
- If (MeteredElement.DSSObjType And CLASSMASK) <> CAP_ELEMENT Then Begin
- DoSimpleMsg(MeteredElement.Name + ' is not a capacitor!', 2016001);
- Exit;
- End;
- end;
-
-
-
- End;
-
- IF MeteredTerminal>MeteredElement.Nterms THEN Begin
- DoErrorMsg('FMonitor: "' + Name + '"',
- 'Terminal no. "' +'" does not exist.',
- 'Respecify terminal no.', 665);
- End
- ELSE Begin
- Nphases := MeteredElement.NPhases;
- Nconds := MeteredElement.NConds;
-
- // Sets name of i-th terminal's connected bus in monitor's buslist
- // This value will be used to set the NodeRef array (see TakeSample)
- Setbus(1, MeteredElement.GetBus(MeteredTerminal));
-
-
- Case (Mode and MODEMASK) of
- 3: Begin
- NumStateVars := TPCElement(MeteredElement).Numvariables;
- ReallocMem(StateBuffer, Sizeof(StateBuffer^[1])*NumStatevars);
- End;
- 4: Begin
- ReallocMem(FlickerBuffer, Sizeof(FlickerBuffer^[1])*Nphases);
- End;
- 5: Begin
- ReallocMem(SolutionBuffer, Sizeof(SolutionBuffer^[1])*NumSolutionVars);
- End;
- Else
- ReallocMem(CurrentBuffer, SizeOf(CurrentBuffer^[1])*MeteredElement.Yorder);
- ReallocMem(VoltageBuffer, SizeOf(VoltageBuffer^[1])*MeteredElement.NConds);
- End;
-
- //ClearMonitorStream;
-
- ValidMonitor := TRUE;
-
- End;
-
- End
- ELSE Begin
- MeteredElement := nil; // element not found
- DoErrorMsg('Monitor: "' + Self.Name + '"', 'Circuit Element "'+ ElementName + '" Not Found.',
- ' Element must be defined previously.', 666);
- End;
- {}
- {}
-End;
-
-procedure TFMonitorObj.MakePosSequence;
-begin
- if MeteredElement <> Nil then begin
- Setbus(1, MeteredElement.GetBus(MeteredTerminal));
- Nphases := MeteredElement.NPhases;
- Nconds := MeteredElement.Nconds;
- Case (Mode and MODEMASK) of
- 3: Begin
- NumStateVars := TPCElement(MeteredElement).Numvariables;
- ReallocMem(StateBuffer, Sizeof(StateBuffer^[1])*NumStatevars);
- End;
- 4: Begin
- ReallocMem(FlickerBuffer, Sizeof(FlickerBuffer^[1])*Nphases);
- End;
- 5: Begin
- ReallocMem(SolutionBuffer, Sizeof(SolutionBuffer^[1])*NumSolutionVars);
- End;
- Else
- ReallocMem(CurrentBuffer, SizeOf(CurrentBuffer^[1])*MeteredElement.Yorder);
- ReallocMem(VoltageBuffer, SizeOf(VoltageBuffer^[1])*MeteredElement.NConds);
- End;
- //ClearMonitorStream;
- ValidMonitor := TRUE;
- end;
- Inherited;
-end;
-
-
-{--------------------------------------------------------------------------}
-Procedure TFMonitorObj.CalcYPrim;
-Begin
-
- {A Monitor is a zero current source; Yprim is always zero.}
- // leave YPrims as nil and they will be ignored
- // Yprim is zeroed when created. Leave it as is.
-End;
-
-{--------------------------------------------------------------------------}
-Procedure TFMonitorObj.Save;
-
-// Saves present buffer to monitor file, resets bufferptrs and continues
-
-Begin
-
- //If NOT IsFileOpen THEN OpenMonitorStream; // Position to end of stream
-
- {Write present monitor buffer to monitorstream}
- //MonitorStream.Write(MonBuffer^, SizeOF(MonBuffer^[1]) * BufPtr);
-
- BufPtr := 0; // reset Buffer for next
-
-End;
-{--------------------------------------------------------------------------}
- Procedure TFMonitorObj.Set_nodes_for_fm(intNodes : integer);
-var
- i,j: Integer;
-begin
- //
- Nodes := intNodes;//initalize the size according to nodes
- if pNodeFMs<>nil then ReAllocMem(pNodeFMs,0);
- if pCommMatrix<>nil then ReAllocMem(pCommMatrix,0);
- if pCommDelayMatrix<>nil then ReAllocMem(pCommDelayMatrix,0);
- if pCommDelaySteps<>nil then ReAllocMem(pCommDelaySteps,0);
- //
- pNodeFMs := AllocMem( Sizeof(pNodeFMs^[1] )* intNodes);
- ReAllocMem(pCommMatrix, intNodes *intNodes * sizeof(pCommMatrix^[1]));
- ReAllocMem(pCommDelayMatrix, intNodes *intNodes * sizeof(pCommDelayMatrix^[1]));
- ReAllocMem(pCommDelaySteps, intNodes *intNodes * sizeof(pCommDelaySteps^[1]));
- for i:=1 to nodes do
- for j := 1 to nodes do
- pCommDelayMatrix^[nodes*(i-1)+j] := 0.0;
-
-end;
-{--------------------------------------------------------------------------}
-Procedure TFMonitorObj.Set_volt_lmt_clstr( strParam: string);
-var
- i:Integer;
- Datahgh, datalw:double;
- iPhasenum : integer;
-begin
- AuxParser.CmdString := strParam; // Load up Parser
- AuxParser.NextParam; // the first entry is the No. of iNode
- iPhasenum := AuxParser.IntValue; //node number defined in cluster
- AuxParser.NextParam; // high limit
- Datahgh := AuxParser.DblValue;
- AuxParser.NextParam; // low limit
- Datalw := AuxParser.DblValue;
-
- case iPhaseNum of
- 0: begin
- ld_fm_info[0].volt_hgh_lmt := Datahgh;
- ld_fm_info[0].volt_lw_lmt := Datalw;
- end;
- 1: ;
- 2: ;
- 3: ;
- end;
-end;
-Procedure TFMonitorObj.Set_CommVector( strParam: string);
-VAR
- TempStr,
- DataStr :String;
- i,j,
- iMin, // the min if Nodes or the length of the vector
- iNodeNum : integer;
-Begin
-
- AuxParser.CmdString := strParam; // Load up Parser
- //iMin := min(Nodes, )
- {Loop for no more than the expected number of windings; Ignore omitted values}
-
- AuxParser.NextParam; // the first entry is the No. of iNode
- iNodeNum := AuxParser.IntValue; //node number defined in cluster
- FOR i := 2 to (Nodes + 1) Do Begin
- AuxParser.NextParam; // ignore any parameter name not expecting any
- DataStr := AuxParser.StrValue;
- IF Length(DataStr) > 0 THEN pCommMatrix^[(iNodeNum-1)*Nodes+ i-1] := AuxParser.intValue;
- End;
-
-// Updates the value of the property for future queries
-// Added y Davis 02072019
- TempStr := '';
- for j := 1 to Nodes do
- Begin
- iNodeNum := j;
- TempStr := TempStr + inttostr(iNodeNum) + ',';
- for i := 2 to (Nodes + 1) do
- TempStr := TempStr + inttostr(pCommMatrix^[(iNodeNum-1)*Nodes+ i-1]) + ',';
-
- TempStr := TempStr + '|';
- End;
- ActiveDSSObject.PropertyValue[15] := TempStr;
-
-
-end;
-
-Procedure TFMonitorObj.Set_CommDelayVector( strParam: string);
-VAR
- TEmpStr,
- DataStr :String;
- i, j,
- iNodeNum : integer;
-Begin
-
- AuxParser.CmdString := strParam; // Load up Parser
- //iMin := min(Nodes, )
- {Loop for no more than the expected number of windings; Ignore omitted values}
-
- AuxParser.NextParam; // the first entry is the No. of iNode
- iNodeNum := AuxParser.IntValue; //node number defined in cluster
- FOR i := 2 to (Nodes + 1) Do Begin
- AuxParser.NextParam; // ignore any parameter name not expecting any
- DataStr := AuxParser.StrValue;
- IF Length(DataStr) > 0 THEN pCommDelayMatrix^[(iNodeNum-1)*Nodes+ i-1] := AuxParser.DblValue;
- End;
-
- ResetDelaySteps(iNodeNum); //Use pCommDelayMatrix^ to calculate pCommDelaySteps^
-
-// Updates the value of the property for future queries
-// Added y Davis 02072019
- TempStr := '';
- for j := 1 to Nodes do
- Begin
- iNodeNum := j;
- TempStr := TempStr + inttostr(iNodeNum) + ',';
- for i := 2 to (Nodes + 1) do
- TempStr := TempStr + floattostr(pCommDelayMatrix^[(iNodeNum-1)*Nodes+ i-1]) + ',';
-
- TempStr := TempStr + '|';
- End;
- ActiveDSSObject.PropertyValue[18] := TempStr;
-end;
-{--------------------------------------------------------------------------}
-Procedure TFMonitorObj.Set_ElemTable_line( strParam: string);
-VAR
- TempStr,
- DataStr :String;
- i :Integer;
- iNodeNum : integer;
-Begin
- AuxParser.CmdString := strParam; // Load up Parser
- AuxParser.NextParam; // the first entry is the number of the iNode
- iNodeNum := AuxParser.IntValue; //node number defined in the cluster
- AuxParser.NextParam; // the first entry is the number of the iNode
- pNodeFMs^[iNodeNum].vl_strBusName := AuxParser.strValue; //node number defined in the cluster
- AuxParser.NextParam;
- pNodeFMs^[iNodeNum].vl_strMeasuredName := AuxParser.StrValue; //Element name load into data str
- //
- //pNodeFMs^[iNodeNum].vl_strName_dg := pNodeFMs^[iNodeNum].vl_strMeasuredName;
- //
- AuxParser.NextParam;
- pNodeFMs^[iNodeNum].vl_terminalNum := AuxParser.IntValue; //Terminal number load into data str
- AuxParser.NextParam;
- pNodeFMs^[iNodeNum].vl_V_ref_dg := 1000*AuxParser.dblValue;
- AuxParser.NextParam;
- pNodeFMs^[iNodeNum].vl_kc_ul_dg := AuxParser.dblValue;
- //2.402
- Init_nodeFM(iNodeNum);
-// Updates the value of the property for future queries
-// Added y Davis 02072019
- TempStr := '';
- for i := 1 to iNodeNum do
- Begin
- TempStr := TempStr + inttostr(i) + ',' +
- pNodeFMs^[i].vl_strBusName + ',' +
- pNodeFMs^[i].vl_strMeasuredName + ',' +
- inttostr(pNodeFMs^[i].vl_terminalNum) + ',' +
- floattostr(pNodeFMs^[i].vl_V_ref_dg) + ',' +
- floattostr(pNodeFMs^[iNodeNum].vl_kc_ul_dg) + '|';
- End;
- ActiveDSSObject.PropertyValue[16] := TempStr;
-end;
-Procedure TFMonitorObj.Get_PQ_DI(i_NodeNum : integer);
-var
- Devindex,i, j, num: integer;
- pElement :TDSSCktElement ;
- pLoad: TLoadObj;
- cBuffer:pComplexArray;
-begin
-
- with pNodeFMs^[i_NodeNum] do
- beGIn
- case ldType of
- 0:// one 3 phase or 2 phase load
- begin
- pElement := ActiveCircuit.PCElements.Get(ldidx);
- num := pElement.NPhases;
- cBuffer := Allocmem(sizeof(cBuffer^[1])*num);
- pElement.GetPhasePower(cBuffer);// power
-
- for j := 1 to num do
- begin
- i := pElement.Terminals[0].TermNodeRef[j - 1];
- case activecircuit.MapNodeToBus^[i].NodeNum of
- 1: begin vl_P_Di1 := cBuffer^[1].re; vl_Q_Di1 := cBuffer^[1].im; end;
- 2: begin vl_P_Di1 := cBuffer^[2].re; vl_Q_Di1 := cBuffer^[2].im; end;
- 3: begin vl_P_Di1 := cBuffer^[3].re; vl_Q_Di1 := cBuffer^[3].im; end;
- end;
- end;
- end;
- 1,2,3:
- begin
- pElement := nil;
- if ldidx1>0 then
- begin
- pElement := ActiveCircuit.PCElements.Get(ldidx1);
- num := pElement.NPhases;
- cBuffer := Allocmem(sizeof(cBuffer^[1])*num);
- pElement.GetPhasePower(cBuffer);// power
-
- for j := 1 to num do
- begin
- i := pElement.Terminals[0].TermNodeRef[j - 1];
- case activecircuit.MapNodeToBus^[i].NodeNum of
- 1: begin vl_P_Di1 := cBuffer^[1].re; vl_Q_Di1 := cBuffer^[1].im; end;
- 2: begin vl_P_Di1 := cBuffer^[1].re; vl_Q_Di1 := cBuffer^[1].im; end;
- 3: begin vl_P_Di1 := cBuffer^[1].re; vl_Q_Di1 := cBuffer^[1].im; end;
- end;
- end;
- end;
- if ldidx2>0 then
- begin
- pElement := ActiveCircuit.PCElements.Get(ldidx2);
- num := pElement.NPhases;
- cBuffer := Allocmem(sizeof(cBuffer^[1])*num);
- pElement.GetPhasePower(cBuffer);// power
-
- for j := 1 to num do
- begin
- i := pElement.Terminals[0].TermNodeRef[j - 1];
- case activecircuit.MapNodeToBus^[i].NodeNum of
- 1: begin vl_P_Di2 := cBuffer^[1].re; vl_Q_Di2 := cBuffer^[1].im; end;
- 2: begin vl_P_Di2 := cBuffer^[1].re; vl_Q_Di2 := cBuffer^[1].im; end;
- 3: begin vl_P_Di2 := cBuffer^[1].re; vl_Q_Di2 := cBuffer^[1].im; end;
- end;
- end;
- end;
- if ldidx3>0 then
- begin
- pElement := ActiveCircuit.PCElements.Get(ldidx3);
- num := pElement.NPhases;
- cBuffer := Allocmem(sizeof(cBuffer^[1])*num);
- pElement.GetPhasePower(cBuffer);// power
-
- for j := 1 to num do
- begin
- i := pElement.Terminals[0].TermNodeRef[j - 1];
- case activecircuit.MapNodeToBus^[i].NodeNum of
- 1: begin vl_P_Di3 := cBuffer^[1].re; vl_Q_Di3 := cBuffer^[1].im; end;
- 2: begin vl_P_Di3 := cBuffer^[1].re; vl_Q_Di3 := cBuffer^[2].im; end;
- 3: begin vl_P_Di3 := cBuffer^[1].re; vl_Q_Di3 := cBuffer^[3].im; end;
- end;
- end;
- end;
- end
-// 2:
-// begin
-//
-// end;
-// 3:
-// begin
-//
-// end
- else
- end;
- end;
-end;
-
-Procedure TFMonitorObj.Init_nodeFM(iNodeNum : integer);
-var
- strTemp : string;
- Devindex, i, j: integer;
- PCindex_ld :integer;
- pElem :TDSSCktElement ;
-
- pDG : TGeneric5Obj;
- pLd : TLoadObj;
- //pPDElem : TPDElement;
- lstPC :TAdjArray;
- num : integer;
- ctmp : complex;
- cBuffer:pComplexArray;
-begin
-
- //init all info of this node
- with pNodeFMs^[iNodeNum] do
- beGIn
- //1
- strTemp := lowercase( vl_strBusName);
- Bus_Idx := ActiveCircuit.BusList.Find(strTemp);
-
- Devindex := GetCktElementIndex(vl_strMeasuredName); // Global function
- IF DevIndex>0 THEN Begin // Monitored element must already exist
- pElem := ActiveCircuit.CktElements.Get(DevIndex);
- end;
- vl_ndphases := pElem.NPhases;
- vl_basevolt := ActiveCircuit.Buses[bus_idx].kVBase*1000;
- vl_phase_num_dg := -1; //-1 -- no dg under this nodes;0 --3 phases by default
-
- for j := 1 to 3 do vl_nodeType_phase[j] := 2;// by default not dg
-
- WITH ActiveCircuit
- Do Begin
- pElem := PCElements.First;
- PCindex_ld := PCElements.activeindex;
- WHILE pElem <> nil
- Do Begin
- IF pElem.Enabled THEN
- begin
- if pElem.ActiveTerminal.BusRef = Bus_Idx then
- begin
- if {$IFDEF FPC}ANSIContainsText{$ELSE}ContainsText{$ENDIF}(lowercase(pElem.DSSClassName),'generic5') then
- begin
-
- //vl_nodeType should be define per phase
- vl_ndphases_dg := pElem.NPhases; //1 or 3
- // under 1 bus, there can be either 3 phase DG or 1 phase dg
- //set Cluster defination for DG
- // one 3phase dg
- // 1,2 or 3 1-phase dgs under each phase
- pDG := TGeneric5Obj(pElem);
- num := trunc(pDG.Get_Variable(30)); //ctrl_mode
- case num of
- 1: vl_nodeType_phase[1] := 1;
- 2: vl_nodeType_phase[2] := 1;
- 3: vl_nodeType_phase[3] := 1;
- else for j := 1 to 3 do vl_nodeType_phase[j] := 1; // //ctrl_mode = 4 or 0
- end;
- //pDG
- if (pDG.FMonObj = nil) then // first cluster
- begin
-
- pDG.Set_Variable(28,cluster_num); //28: TPCElement(self).cluster_num := trunc(Value);
- //if cluster_num >= 1 then // assign the virtue leader to this DG
- pDG.FMonObj := self; //FMonObj := ActiveCircuit.Fmonitors.Get(cluster_num); cluster_num can not be used for 'Get'
- pDG.Set_Variable(29,iNodeNum); //29: TPCElement(self).NdNumInCluster := trunc(Value) ;
- pDG.Set_Variable(30,1); //TPCElement(self).nVLeaders := trunc(Value) ;
- end else
- begin // the second virtual leader // which means if the 2nd one will always be the one being overwritten
-
- if (cluster_num <> Round(pDG.get_Variable(28))) then
- begin
- pDG.Set_Variable(31,cluster_num); //28: TPCElement(self).cluster_num2 := trunc(Value);
- //if cluster_num >= 1 then // assign the virtue leader to this DG
- pDG.FMonObj2 := self; //FMonObj := ActiveCircuit.Fmonitors.Get(cluster_num); cluster_num can not be used for 'Get'
- pDG.Set_Variable(32,iNodeNum); //29: TPCElement(self).NdNumInCluster2 := trunc(Value) ;
- pDG.Set_Variable(30,2); // TPCElement(self).nVLeaders := trunc(Value) ;
- end else
- begin
- //
- end;
- end;
- vl_phase_num_dg := 0; //3 phases by default
- if vl_ndphases_dg=1 then
- vl_phase_num_dg := trunc(pDG.Get_Variable(30)); //if vl_nodeType=1, and vl_ndphases=1,phase_num =1,2,3 0- this node has 3 phases
- // 30, ctrl_mode, is the phase number of this Generic5
-
- end;
-
- ldType := -1;
- ldIdx := -1;
- ldIdx1 := -1;
- ldIdx2 := -1;
- ldIdx3 := -1;
-
- if {$IFDEF FPC}ANSIContainsText{$ELSE}ContainsText{$ENDIF}(lowercase(pElem.DSSClassName),'load') then
- begin
-
- num := pElem.NPhases;
- pLd := TLoadObj(pElem);
- //Devindex :=
- if num = 3 then
- begin
- ldIdx := PCindex_ld ;
- ldType := 0;
- end else if num=2 then
-
- begin
- ldIdx := PCindex_ld ;
- ldType := 0;
-
- end else if num=1 then
-
- begin
- i := pElem.Terminals[0].TermNodeRef[0];
- case activecircuit.MapNodeToBus^[i].NodeNum of
- 1: ldIdx1 := PCindex_ld ;
- 2: ldIdx2 := PCindex_ld ;
- 3: ldIdx3 := PCindex_ld ;
- end;
- if ldType<1 then
- ldType := 1
- else if ldType>=1 then
- ldType := ldType + 1;
- if ldType>=3 then
- ldType := 3 ;
- end;
- //
-
-// cBuffer := Allocmem(sizeof(cBuffer^[1])*num);
-// pElem.GetPhasePower(cBuffer);// power
-// for j := 1 to num do
-// begin
-// i := pElem.Terminals[0].TermNodeRef[j - 1];
-// case activecircuit.MapNodeToBus^[i].NodeNum of
-// 1: begin vl_P_Di1 := cBuffer^[1].re; vl_Q_Di1 := cBuffer^[1].im; end;
-// 2: begin vl_P_Di1 := cBuffer^[2].re; vl_Q_Di1 := cBuffer^[2].im; end;
-// 3: begin vl_P_Di1 := cBuffer^[3].re; vl_Q_Di1 := cBuffer^[3].im; end;
-// end;
-// end;
-//
-// Reallocmem(cBuffer,0);
- end;
- end;
- End;
- pElem := PCElements.Next;
- PCindex_ld := PCElements.activeindex;
- End;
- end;
- //lstPC := ActiveCircuit.GetBusAdjacentPCLists;
- //activecircuit.MapNodeToBus
- //num := lstPC.NumShuntObjects;
-
- //2
-
-
- // will be overwritten if this nodeFM is a 1-phase Generic5
- ////// update cluster_num and node number of this DG in this cluster
- {if vl_nodeType=1 then // this only work for DG
- begin
- //set
- pDG := TGeneric5Obj(pElement);
- //pDG
- if pDG.FMonObj = nil then // first cluster
- begin
-
- pDG.Set_Variable(28,cluster_num); //28: TPCElement(self).cluster_num := trunc(Value);
- //if cluster_num >= 1 then // assign the virtue leader to this DG
- pDG.FMonObj := self; //FMonObj := ActiveCircuit.Fmonitors.Get(cluster_num); cluster_num can not be used for 'Get'
- pDG.Set_Variable(29,iNodeNum); //29: TPCElement(self).NdNumInCluster := trunc(Value) ;
- pDG.Set_Variable(30,1); //TPCElement(self).nVLeaders := trunc(Value) ;
- end else
- begin // the second virtual leader // which means if the 2nd one will always be the one being overwritten
-
- pDG.Set_Variable(31,cluster_num); //28: TPCElement(self).cluster_num2 := trunc(Value);
- //if cluster_num >= 1 then // assign the virtue leader to this DG
- pDG.FMonObj2 := self; //FMonObj := ActiveCircuit.Fmonitors.Get(cluster_num); cluster_num can not be used for 'Get'
- pDG.Set_Variable(32,iNodeNum); //29: TPCElement(self).NdNumInCluster2 := trunc(Value) ;
- pDG.Set_Variable(30,2); // TPCElement(self).nVLeaders := trunc(Value) ;
- end;
- if vl_ndphases_dg=1 then
- vl_phase_num_dg := trunc(pDG.Get_Variable(30)); //if vl_nodeType=1, and vl_ndphases=1,phase_num =1,2,3 0- this node has 3 phases
- // 30, ctrl_mode, is the phase number of this Generic5
- end; }
- {if vl_nodeType=2 then // line and tranformer
- begin
- //set
- //pPDelem := TPDElement(pElement);
- //pDG
- end;
- }
- ///
- vl_V_ref1_dg:= vl_V_ref_dg;//:=1000*2.4; 1000*2.4; V_ref2:=1000*2.4; V_ref3 :=1000*2.4;// norminal value with respect to p.u. 1 //must be set by initialization
- vl_V_ref2_dg:= vl_V_ref_dg;
- vl_V_ref3_dg:= vl_V_ref_dg;
- //kcq := 1.0; // the step size gain of agent i //has to be defined befor used
-
- /// other properties if needed
- //ndphases : integer; //how many phases of this device on this node; not those of node
- vl_CC_switch_dg :=false; // cooperate control switch. true, cooperate control is on
- vl_PF_flag_dg :=0 ;//1, real power control is on
- vl_QV_flag_dg :=0;//1, volt/var control is on
- vl_Alpha_dg:=0;
- vl_Alpha1_dg:=0;vl_Alpha2_dg:=0;vl_Alpha3_dg:=0;
- vl_Gradient_dg:=0;vl_Gradient1_dg:=0;vl_Gradient2_dg:=0;vl_Gradient3_dg:=0;
- vl_AlphaP_dg:=0;
- vl_AlphaP1_dg:=0;vl_AlphaP2_dg:=0;vl_AlphaP3_dg:=0;
- vl_GradientP_dg:=0;vl_GradientP1_dg:=0;vl_GradientP2_dg:=0;vl_GradientP3_dg:=0;
- vl_Pmax_dg:=0; vl_Qmax_dg:=0;
- vl_Pmax_phase_dg:=0; vl_Qmax_phase_dg :=0;
- vl_V_base_dg:=1000*2.4;
- vl_V:=1000*2.4;vl_V1:=1000*2.4;vl_V2:=1000*2.4;vl_V3:=1000*2.4;
- vl_Q_Di := 0.0;
- vl_Q_Di1 := 0.0;
- vl_Q_Di2 := 0.0;
- vl_Q_Di3 := 0.0;
- vl_P_Di := 0.0;
- vl_P_Di1 := 0.0;
- vl_P_Di2 := 0.0;
- vl_P_Di3 := 0.0;
- vl_smplCnt := 0;
- vl_crnt_smp_time := 0.0;
- end;
-end;
-Procedure TFMonitorObj.Get_PDElem_terminal_voltage(nd_num_in_cluster: integer ;devName: string; Tern_num: integer );
-var
- tempTerminal : TPowerTerminal;
- i ,Devindex, j: integer;
- tempElement :TDSSCktElement ;
- //VAR
- //pElem:TDSSCktElement;
- phase_num:Integer ;
- vabs : double;
- //V012 :TSymCompArray5;
- V012 : Array[0..2] of Complex;
- VaVbVc :Array[1..3] of Complex;
-begin
- Devindex := GetCktElementIndex(devName); // Global function
- IF DevIndex>0 THEN Begin // Monitored element must already exist
- tempElement := ActiveCircuit.CktElements.Get(DevIndex);
- end;
- //
- tempTerminal := tempElement.Terminals[Tern_num - 1];
- for j:=1 to tempElement.NPhases do// how many phases of this element
- begin
- i := tempTerminal.TermNodeRef[j - 1]; // global node number
- phase_num := ActiveCircuit.MapNodeToBus^[i].NodeNum;
- vabs := cabs (activecircuit.Solution.NodeV^[i]);
- if phase_num=1 then // phase A
- begin
- pnodefms^[nd_num_in_cluster].vl_V1 := vabs ;
- pnodefms^[nd_num_in_cluster].vl_V_1c := activecircuit.Solution.NodeV^[i];
- end
- else if phase_num=2 then //phase B
- begin
- pnodefms^[nd_num_in_cluster].vl_V2 := vabs;
- pnodefms^[nd_num_in_cluster].vl_V_2c := activecircuit.Solution.NodeV^[i];
- end
- else if phase_num=3 then //phase c
- begin
- pnodefms^[nd_num_in_cluster].vl_V3 := vabs;
- pnodefms^[nd_num_in_cluster].vl_V_3c := activecircuit.Solution.NodeV^[i];
- end;
- end;
- if tempElement.NPhases=3 then
- begin
- VaVbVc[1] := pnodefms^[nd_num_in_cluster].vl_V_1c;//phase A
- VaVbVc[2] := pnodefms^[nd_num_in_cluster].vl_V_2c;
- VaVbVc[3] := pnodefms^[nd_num_in_cluster].vl_V_3c;
- Phase2SymComp(@VaVbVc, @V012); // Convert abc voltages to 012
- pnodefms^[nd_num_in_cluster].vl_V := cabs(V012[1]); //pos. seq. Voltage
- end;
-
-end;
-
-Procedure TFMonitorObj.update_all_nodes_info; //PD nodes
-var
- i : integer;
-begin
- for i := 1 to nodes do
- begin
- with pnodeFMs^[i] do
- begin
- //if vl_nodeType = 1 then // generic dg
- //begin
- // do nothing; because the info is updated from the other end (sent by DGs)
- //end
- //else if vl_nodeType = 2 then // PD elements: lines, xformers, etc..
- //begin
- //Update_PD_Node_Info(i,vl_strMeasuredName,vl_terminalNum);
- Get_PDElem_terminal_voltage(i,vl_strMeasuredName,vl_terminalNum,ActorID ) ;
- //Calc_Alpha_for_PDNode(i ); // calc all alpha, alpha1, alpha2, alph3 of all pdelement nodes
- //end;
- end;
- end;
-
-end;
-{--------------------------------------------------------------------------}
-Procedure TFMonitorObj.ResetDelaySteps(iNodeNum: integer);
-Var
- j,tmp : integer;
-begin
- //calc delay array
- for j := 1 to nodes do
- begin
- if (T_intvl_smpl=0.0) or (pCommDelayMatrix^[(iNodeNum-1)*nodes+j]=0.0) then
- pCommDelaySteps^[(iNodeNum-1)*nodes+j] := 0
- else
- begin
- tmp := trunc(pCommDelayMatrix^[(iNodeNum-1)*nodes+j] /T_intvl_smpl);
- if frac(pCommDelayMatrix^[(iNodeNum-1)*nodes+j] /T_intvl_smpl)=0.0 then
- pCommDelaySteps^[(iNodeNum-1)*nodes+j] := tmp
- else
- pCommDelaySteps^[(iNodeNum-1)*nodes+j] := tmp + 1;
- //How many delays for communication
- end;
- end
-end;
-
-Procedure TFMonitorObj.ResetIt;
-Var
- iTmp : integer;
-Begin
- BufPtr := 0;
- //ClearMonitorStream;
- if ActiveCircuit.Solution.DynaVars.SolutionMode = DYNAMICMODE then
- begin
- //calc Delay_stps for sampling
- if T_intvl_smpl = 0.0 then
- Smpl_stps := 0 //No delay.
- else
- begin
- iTmp := Trunc(T_intvl_smpl / ActiveCircuit.Solution.Dynavars.h);
- if frac(T_intvl_smpl / ActiveCircuit.Solution.Dynavars.h)=0.0 then
- Smpl_stps := iTmp
- else
- Smpl_stps := iTmp +1 ;// uper
- end;
- for iTmp := 1 to Nodes do
- begin
- pnodeFMs^[iTmp].vl_smplCnt := 0;
- pNodeFMs^[iTmp].vl_crnt_smp_time := ActiveCircuit.Solution.DynaVars.intHour*3600+ActiveCircuit.Solution.DynaVars.t ;
- Init_delay_array(iTmp); // in DYNAMICMODE, init alpha array
- end;
-
- end;
-End;
-
-Procedure TFMonitorObj.GetCurrents(Curr: pComplexArray); //Get present value of terminal Curr for reports
-VAR
- i:Integer;
-Begin
-
-{
- Revised 12-7-99 to return Zero current instead of Monitored element current because
- it was messing up Newton iteration.
-}
-
- For i := 1 to Fnconds Do Curr^[i] := CZERO;
-
-End;
-
-{--------------------------------------------------------------------------}
-Procedure TFMonitorObj.DumpProperties(F: TFileStream; Complete:Boolean);
-
-VAR
- i, k:Integer;
-
-Begin
- Inherited DumpProperties(F,Complete);
-
- With ParentClass Do
- For i := 1 to NumProperties Do
- Begin
- FSWriteln(F,'~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- End;
-
-
- If Complete Then Begin
- FSWriteln(F);
- FSWriteln(F,'// BufferSize=',BufferSize:0);
- FSWriteln(F,'// Hour=',Hour:0);
- FSWriteln(F,'// Sec=',Sec:0);
- FSWriteln(F,'// BaseFrequency=',BaseFrequency:0:1);
- FSWriteln(F,'// Bufptr=',BufPtr:0);
- FSWriteln(F,'// Buffer=');
- k:=0;
- FOR i := 1 to BufPtr DO Begin
- FSWrite(F, MonBuffer^[i]:0:1,', ');
- Inc(k);
- IF k=(2 + Fnconds*4) THEN Begin
- FSWriteln(F);
- k:=0;
- End;
- End;
- FSWriteln(F);
- End;
-
-End;
-
-procedure TFMonitorObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := ''; //'element';
- PropertyValue[2] := '1'; //'terminal';
- PropertyValue[3] := '0'; //'mode';
- PropertyValue[4] := ''; // 'action'; // buffer=clear|save|take|process
- PropertyValue[5] := 'NO';
- PropertyValue[6] := 'YES';
- PropertyValue[7] := 'YES';
- {}
- PropertyValue[8] := '0';//ref power
- PropertyValue[9] := '0';// default voltage sensor value
- PropertyValue[10] := '0';// default power sensor value
- PropertyValue[11] := '1';// default node number value
- PropertyValue[12] := '1';// default by group 1
- PropertyValue[13] := '1';// default number of groups
- {}
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-
-{--------------------------------------------------------------------------}
-
-
-{----------------------------------------------------------------------}
-
-//
-procedure TFMonitorObj.Calc_Alpha_for_PDNode(NodeNum:Integer);
-//must be called after self gradient calc
-// self gradient calc is in 'Update _pd_node_info'
-var
- j,phase_num : integer;
- sum_Sij_j : double;
- TempAlpha : double;
-begin
- //if phase_num=0 then
- // result := 0.0 //alpha
- //else if phase_num=1 then
- // result:= 0.0 //alpha1
- // else if phase_num=2 then
- // result := 0.0 //alpha2
- // else if phase_num=3 then
- // result := 0.0; //alpha3
- //update nodes information before calculating alpha_i
- //update_all_nodes_info;
-
- // calclate alpha
- with pnodeFMs^[NodeNum] do
- for phase_num:= 1 to vl_ndphases_dg do
- case phase_num of
- 1: begin //phase A
- TempAlpha := 0.0;//init as zero
- sum_Sij_j := 0.0;
- for j := 1 to Nodes do
- //for j := 1 to NodeNumofDG-1 do
- begin
- //if pnodeFMs^[j].vl_phase_num=phase_num then
- //begin
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNum-1)*nodes +j]*pnodeFMs^[j].vl_Alpha1_dg ;
- sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNum-1)*nodes +j];
- //end;
- end;
- //for j := NodeNumofDG + 1 to Nodes do
- //begin
- // Alpha1 := Alpha1 + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].Alpha1 ;
- // sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNumofDG-1)*nodes +j];
- //end;
- vl_Alpha1_dg := TempAlpha / sum_Sij_j;
- vl_Alpha1_dg := vl_Alpha1_dg - vl_kcq_dg* vl_gradient1_dg;
- //result := Alpha1;
- end;
- 2: begin //phase B
- TempAlpha := 0.0;//init as zero
- sum_Sij_j := 0.0;
- for j := 1 to Nodes do
- //for j := 1 to NodeNumofDG-1 do
- begin
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNum-1)*nodes +j]*pnodeFMs^[j].vl_Alpha2_dg ;
- sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNum-1)*nodes +j];
- end;
- //for j := NodeNumofDG + 1 to Nodes do
- //begin
- // Alpha2 := Alpha2 + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].Alpha2 ;
- // sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNumofDG-1)*nodes +j];
- //end;
- vl_Alpha2_dg := TempAlpha / sum_Sij_j;
- vl_Alpha2_dg := vl_Alpha2_dg - vl_kcq_dg* vl_gradient2_dg;
- //result := Alpha2;
- end;
- 3: begin //phase C
- TempAlpha := 0.0;//init as zero
- sum_Sij_j := 0.0;
- for j := 1 to Nodes do
- //for j := 1 to NodeNumofDG-1 do
- begin
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNum-1)*nodes +j]*pnodeFMs^[j].vl_Alpha3_dg ;
- sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNum-1)*nodes +j];
- end;
- //for j := NodeNumofDG + 1 to Nodes do
- //begin
- // Alpha3 := Alpha3 + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].Alpha3 ;
- // sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNumofDG-1)*nodes +j];
- //end;
- vl_Alpha3_dg := TempAlpha / sum_Sij_j;
- vl_Alpha3_dg := vl_Alpha3_dg - vl_kcq_dg* vl_gradient3_dg;
- //result := Alpha3;
- end;
- 0: begin //pos seq value
- TempAlpha := 0.0;//init as zero
- sum_Sij_j := 0.0;
- for j := 1 to Nodes do
- //for j := 1 to NodeNumofDG-1 do
- begin
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNum-1)*nodes +j]*pnodeFMs^[j].vl_Alpha_dg ;
- sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNum-1)*nodes +j];
- end;
- //for j := NodeNumofDG + 1 to Nodes do
- //begin
- // Alpha := Alpha + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].Alpha ;
- // sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNumofDG-1)*nodes +j];
- //end;
- vl_Alpha_dg := TempAlpha / sum_Sij_j;
- vl_Alpha_dg := vl_Alpha_dg - vl_kcq_dg* vl_gradient_dg;
- //result := Alpha;
- end;
- else
- end;
-end;
-{------------------------}
-//////////////////////////
-Function TFMonitorObj.Get_P_mode( ActorID : Integer):integer;
-begin
- result := p_mode;
-end;
-// AlphaP Gradient or Pref
-Function TFMonitorObj.get_power_trans:Double; // NodeNuminClstr: node number in cluster
-var
- i,j,k: integer;
- pTerminal : TPowerTerminal;
- Curr: pComplexArray;
-begin
- //tempCplx := MeteredElement.power[MeteredTerminal];
- TPDElement(MeteredElement).GetCurrents(MeteredElement.Iterminal); //Curr
- pTerminal := MeteredElement.Terminals[MeteredTerminal - 1];
- tempCplx := CZERO;
- k := (MeteredTerminal -1)*MeteredElement.NConds;
- for j:=1 to MeteredElement.NConds do// how many conds of this element
- begin
- i := pTerminal.TermNodeRef[j - 1]; // global node number
- CACCUM( tempCplx , cmul(activecircuit.Solution.NodeV^[i], Conjg(MeteredElement.Iterminal[k+j])));//power
- end;
- result := tempCplx.re ;
-end;
-//calculate the gradient for alpha i
-Function TFMonitorObj.Calc_Grdt_for_Alpha(NodeNuminClstr, phase_num,ActorID : Integer):Double;
-var
- Vtemp, ctmp : complex;
- tmp : double;
- Gij, Bij, Gii, Bii : double;
- Devindex, i, j, k, jTempTerminal: integer;
- pElem :TDSSCktElement ;
- nodeRefi : integer;// ref number of this node
- nodeRefj : integer;// ref number of the upper node
- den : double;
-begin
- //pNodeFMs^[NodeNuminClstr].vl_strMeasuredName;//element followed by this bus
- Devindex := GetCktElementIndex(pNodeFMs^[NodeNuminClstr].vl_strMeasuredName);
- IF DevIndex>0 THEN Begin // Monitored element must already exist
- pElem := ActiveCircuit.CktElements.Get(DevIndex);
- end;
- if pElem <> nil //want to get voltages from the other side of the device
- then Begin
- With ActiveCircuit.solution Do
- For i := 1 to pElem.Yorder Do
- pElem.Vterminal^[i] := NodeV^[pElem.NodeRef^[i]];
- End
- else result := 0.0 ;
- //k is the terminal number of this end
- k := pNodeFMs^[NodeNuminClstr].vl_terminalNum;
- //this is the other end jTempTerminal
- if k=1 then jTempTerminal := 2 else jTempTerminal := 1;
- //k := (iTempTerminal -1)*MeteredElement.NConds;
- //
- //find the voltage of this phase on this terminal
- for i:=1 to pElem.NPhases do// how many conds of this element
- begin
- j := pElem.Terminals[jTempTerminal - 1].TermNodeRef[i - 1];
- if activecircuit.MapNodeToBus^[j].NodeNum = phase_num then
- begin
- nodeRefj := j; // node ref of the other end of this element and this phase
- vTemp := activecircuit.Solution.NodeV^[nodeRefj];
- nodeRefi := pElem.Terminals[k - 1].TermNodeRef[i - 1]; // node ref of this node
- end;
- end;
- if phase_num=0 then // cannot deal with pos seq
- begin
-
- end;
- //
- ctmp := activecircuit.Solution.Get_Yij(nodeRefi,nodeRefj) ;
- Gij := ctmp.re;
- Bij := ctmp.im;
- ctmp := activecircuit.Solution.Get_Yij(nodeRefi,nodeRefi) ;
- //ctmp := activecircuit.Solution.NodeYii^[nodeRefi];
- Gii := ctmp.re;
- Bii := ctmp.im;
-
- with pNodeFMs^[NodeNuminClstr] do
- case phase_num of //pos seq
- 0: begin
- result := vl_gradient_dg; // can not deal with that
- end;
- 1: begin
- {den := vl_Q_DG1 - vl_Q_Di1- vl_V1*vl_V1* Bii; // pos ctrl: Bii use the first one
- tmp := vl_V1-cabs(vTemp);
-
- vl_gradient1_dg := vl_V1 * tmp;
-
- if abs(den)0 THEN Begin // Monitored element must already exist
- pElem := ActiveCircuit.CktElements.Get(DevIndex);
- end;
- if pElem <> nil //want to get voltages from the other side of the device
- then Begin
- With ActiveCircuit.solution Do
- For i := 1 to pElem.Yorder Do
- pElem.Vterminal^[i] := NodeV^[pElem.NodeRef^[i]];
- End
- else result := 0.0 ;
- //k is the terminal number of this end
- k := pNodeFMs^[NodeNuminClstr].vl_terminalNum;
- //this is the other end jTempTerminal
- if k=1 then jTempTerminal := 2 else jTempTerminal := 1;
- //k := (iTempTerminal -1)*MeteredElement.NConds;
- //
- //find the voltage of this phase on this terminal
- for i:=1 to pElem.NPhases do// how many conds of this element
- begin
- j := pElem.Terminals[jTempTerminal - 1].TermNodeRef[i - 1];
- if activecircuit.MapNodeToBus^[j].NodeNum = phase_num then
- begin
- nodeRefj := j; // node ref of the other end of this element and this phase
- vTemp := activecircuit.Solution.NodeV^[nodeRefj];
- nodeRefi := pElem.Terminals[k - 1].TermNodeRef[i - 1]; // node ref of this node
- end;
- end;
- if phase_num=0 then // cannot deal with pos seq
- begin
-
- end;
- //
- ctmp := activecircuit.Solution.Get_Yij(nodeRefi,nodeRefj) ;
- Gij := ctmp.re;
- Bij := ctmp.im;
- ctmp := activecircuit.Solution.Get_Yij(nodeRefi,nodeRefi) ;
- //ctmp := activecircuit.Solution.NodeYii^[nodeRefi];
- Gii := ctmp.re;
- Bii := ctmp.im;
-
- with pNodeFMs^[NodeNuminClstr] do
- case phase_num of //pos seq
- 0: begin
- result := vl_gradient_dg; // can not deal with that
- end;
- 1: begin
- den := vl_Q_DG1 - vl_Q_Di1- vl_V1*vl_V1* Bii; // pos ctrl: Bii use the first one
- tmp := vl_V1-cabs(vTemp);
-
- vl_gradient1_dg := vl_V1 * tmp;
-
- if abs(den)0 then
- // PGtemp := -(p_trans_ref - ptemp )/Activecircuit.solution.t;//pNodeFMs^[NodeNuminClstr].vl_V_ref_dg // pu value?
- //else
- //if pNodeFMs^[NodeNuminClstr].vl_Pmax_dg<>0.0 then
- //PGtemp := -(p_trans_ref - ptemp )//-(p_trans_ref - ptemp )/pNodeFMs^[NodeNuminClstr].vl_Pmax_dg // should use the total load
- //else PGtemp := 0.0 ;
- PGtemp := -(p_trans_ref - ptemp );
- if PGtemp>0 then PGtemp := 1
- else if PGtemp<0 then PGtemp := -1
- else PGtemp := 0.0;
- case phase_num of //pos seq
- 0: begin
- result := PGtemp; //
- end;
- 1: begin
- result := PGtemp; //
- end;
- 2: begin
- result := PGtemp; //
- end;
- 3: begin
- result := PGtemp; //
- end;
- end;
-end;
-//////////////////////////
-{-------------------------}
-Function TFMonitorObj.Calc_AlphaP(NodeNuminClstr, phase_num,ActorID:Integer):Double; // NodeNuminClstr: node number in cluster
-var
- j : integer;
- den_dij,TempAlpha : Double;
-
-begin
- //alphaP = sum (alphaP) + Beta * Gp
-
- with pNodeFMs^[NodeNuminClstr] do
- begin
- case phase_num of //pos seq
- 0: begin
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) then //only 3 phase nodes
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNuminClstr-1)*Nodes+ j];
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNuminClstr-1)*nodes +j]*pnodeFMs^[j].vl_AlphaP_dg;
- //end;
- end;
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
- end;
- vl_gradient_dg := Calc_GP_AlphaP(NodeNuminClstr,phase_num);
- vl_alphaP_dg :=TempAlpha + vl_kcd_dg * vl_gradient_dg/activecircuit.Solution.Iteration;
- if vl_alphaP_dg >1 then vl_alphaP_dg := 1;
- if vl_alphaP_dg <-1 then vl_alphaP_dg := -1;
- result := vl_alphaP_dg;
- end;
- 1: begin
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only count dgs with 3 phases or 1 phase that is same number
- or (pnodeFMs^[j].vl_phase_num_dg = phase_num) then
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNuminClstr-1)*Nodes+ j] ;
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNuminClstr-1)*nodes +j]*pnodeFMs^[j].vl_AlphaP1_dg;
- //end;
- end;
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
- end;
- vl_gradientP1_dg := Calc_GP_AlphaP(NodeNuminClstr,phase_num);
- vl_alphaP1_dg :=TempAlpha + vl_kcd_dg * vl_gradientP1_dg/activecircuit.Solution.Iteration;
- if vl_alphaP1_dg >1 then vl_alphaP1_dg := 1;
- if vl_alphaP1_dg <-1 then vl_alphaP1_dg := -1;
- result := vl_alphaP1_dg;
- end;
- 2: begin
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only count dgs with 3 phases or 1 phase that is same number
- or (pnodeFMs^[j].vl_phase_num_dg = phase_num) then
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNuminClstr-1)*Nodes+ j];
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNuminClstr-1)*nodes +j]*pnodeFMs^[j].vl_AlphaP2_dg;
- //end;
- end;
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
- end;
- vl_gradientP2_dg := Calc_GP_AlphaP(NodeNuminClstr,phase_num);
- vl_alphaP2_dg :=TempAlpha + vl_kcd_dg * vl_gradientP2_dg/activecircuit.Solution.Iteration;
- if vl_alphaP2_dg >1 then vl_alphaP2_dg := 1;
- if vl_alphaP2_dg <-1 then vl_alphaP2_dg := -1;
- result := vl_alphaP2_dg;
- end;
- 3: begin
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- //for i := 1 to nodes do den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ i]; //4.is this correct
-
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only count dgs with 3 phases or 1 phase that is same number
- or (pnodeFMs^[j].vl_phase_num_dg = phase_num) then
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNuminClstr-1)*Nodes+ j];
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNuminClstr-1)*nodes +j]*pnodeFMs^[j].vl_AlphaP3_dg;
- //end;
- end;
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
- end;
- vl_gradientP3_dg := Calc_GP_AlphaP(NodeNuminClstr,phase_num);
- vl_alphaP3_dg :=TempAlpha + vl_kcd_dg * vl_gradientP3_dg/activecircuit.Solution.Iteration;
- if vl_alphaP3_dg >1 then vl_alphaP3_dg := 1;
- if vl_alphaP3_dg <-1 then vl_alphaP3_dg := -1;
- result := vl_alphaP3_dg;
- end;
- end;
- end;
-end;
-{----------------------------------------------------------------------------------}
-//only work for Generic5 nodefm
-Function TFMonitorObj.Calc_Alpha_M2(NodeNumofDG, phase_num:Integer; dbNodeRef: integer; Bii,beta,Volt_Trhd: double):Double;
-//NodeNumofDG = NodeNuminClstr
-var
- i,j : integer;
- sum_Sij_j, den : double;
- alpha:double;
- den_dij,dii,TempAlpha : Double;
- tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7: double;
-
-begin
- //update_all_nodes_info;
- //needs to update Q_Di here(Find a way)
- {
-
- 1.need beta or kcq here
- 2. is Bii calculated for each phases
- 3.what is phase 0?
- 4.in the function look at communication vector
-
- }
-
- //j := ActiveCircuit.Solution.Iteration;
-
- update_all_nodes_info; // update voltages on all buses
-
- with pNodeFMs^[NodeNumofDG] do
- begin
- case phase_num of //pos seq
- 0: begin
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
-
-
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) then //only 3 phase nodes
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j];
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha_dg;
- //end;
- end;
-
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
-
- //2.calculate gradient----------------
- //Bii := ActiveCircuit.Solution.NodeYii^[dbNodeRef].im;
- //Load.ActiveLoadObj.kvarBase;
- den := vl_Q_DG - vl_Q_Di- vl_V*vl_V* Bii; // pos ctrl: Bii use the first one
- if abs(den)1 then alpha := 1;
- if alpha <-1 then alpha := -1;
- vl_Alpha_dg := alpha;
- result := alpha;
- end;
- 1: begin
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- //for i := 1 to nodes do den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ i]; //4.is this correct
- //tmp1 := Nodes;
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only count dgs with 3 phases or 1 phase that is same number
- //or (pnodeFMs^[j].vl_phase_num_dg = phase_num) then
- or (pnodeFMs^[j].vl_nodeType_phase[1] = 1) then //this phase has DG
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j] ;
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha1_dg;
- //end;
- end;
-
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
- //2.calculate gradient----------------
- //Bii := ActiveCircuit.Solution.NodeYii^[dbNodeRef].im;
- den := vl_Q_DG1 - vl_Q_Di1- vl_V1*vl_V1* Bii; // pos ctrl: Bii use the first one
- if abs(den)1 then vl_alpha1_dg := 1;
- if vl_alpha1_dg <-1 then vl_alpha1_dg := -1;
- result := vl_alpha1_dg;
- end;
- 2: begin
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- //for i := 1 to nodes do den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ i]; //4.is this correct
-
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only count dgs with 3 phases or 1 phase that is same number
- //or (pnodeFMs^[j].vl_phase_num_dg = phase_num) then
- or (pnodeFMs^[j].vl_nodeType_phase[2] = 1) then //this phase has DG
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j];
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha2_dg;
- //end;
- end;
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
-
- //2.calculate gradient----------------
- //Bii := ActiveCircuit.Solution.NodeYii^[dbNodeRef].im;
- den := vl_Q_DG2 - vl_Q_Di2- vl_V2*vl_V2* Bii; // pos ctrl: Bii use the first one
- if abs(den)1 then vl_alpha2_dg := 1;
- if vl_alpha2_dg <-1 then vl_alpha2_dg := -1;
- result := vl_alpha2_dg;
- end;
- 3: begin
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- //for i := 1 to nodes do den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ i]; //4.is this correct
-
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only count dgs with 3 phases or 1 phase that is same number
- //or (pnodeFMs^[j].vl_phase_num_dg = phase_num) then
- or (pnodeFMs^[j].vl_nodeType_phase[3] = 1) then //this phase has DG
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j];
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha3_dg;
- //end;
- end;
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
-
- //2.calculate gradient----------------
- //Bii := ActiveCircuit.Solution.NodeYii^[dbNodeRef].im;
- den := vl_Q_DG3 - vl_Q_Di3- vl_V3*vl_V3* Bii; // pos ctrl: Bii use the first one
- if abs(den)1 then vl_alpha3_dg := 1;
- if vl_alpha3_dg <-1 then vl_alpha3_dg := -1;
- result := vl_alpha3_dg;
- end;
- end;
- end;
- //result := 0;
-end;
-//will be call in Generic5
-//calculate subgradient for DG 'NodeNumofDG' phase 'phase_num'
-
-Function TFMonitorObj.Calc_Alpha_LnM2(NodeNumofDG, phase_num:Integer; dbNodeRef: integer; Bii,beta,Volt_Trhd: double):Double;
-Var
- Lambda0, Lambda, tmp, tmp1 : double;
-begin
- Lambda0 := 0.1;
- Lambda := 1.0;
- tmp := 0.0;
- //tmp := Calc_Alpha_L(NodeNumofDG, phase_num, dbNodeRef, Bii,beta,Volt_Trhd) ;
- tmp1 := Calc_Alpha_M2(NodeNumofDG, phase_num, dbNodeRef, Bii,beta,Volt_Trhd) ;
- result := (1-Lambda) *tmp + Lambda * tmp1;
- //result := Calc_Alpha_L_vivj(NodeNumofDG, phase_num, dbNodeRef, Bii,beta,Volt_Trhd) ;
-end;
-Function TFMonitorObj.Calc_Alpha_L_vivj(NodeNumofDG, phase_num:Integer; dbNodeRef: integer; Bii,beta,Volt_Trhd: double ):Double;
-Var
- i,j : integer;
- sum_Sij_j, den : double;
- alpha:double;
- den_dij,dii,TempAlpha ,tmp: Double;
- dynBeta : double;
- //tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7: double;
-begin
- update_all_nodes_info; // update voltages on all buses
- Get_PQ_DI( NodeNumofDG) ; // load measure
- // calclate alpha
- //with pnodeFMs^[NodeNumofDG] do
- case phase_num of
- 1: begin //phase A
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- //for i := 1 to nodes do den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ i]; //4.is this correct
- //tmp1 := Nodes;
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only count dgs with 3 phases or 1 phase that is same number
- //or (pnodeFMs^[j].vl_phase_num_dg = phase_num) then
- or (pnodeFMs^[j].vl_nodeType_phase[1] = 1) then //this phase has DG
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j] ;
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha1_dg;
- //end;
- end;
-
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
- //2.calculate gradient----------------
- //vl_gradient1_dg := (vl_V_ref1_dg-vl_v1)*vl_V1/(den)/(vl_V_ref1_dg*vl_V_ref1_dg); //*vl_Qmax, 0311-by dahei
- //vl_gradient1_dg := (beta*vl_V_ref1_dg*vl_V_ref1_dg* abs(Bii)*100/j)*vl_gradient1_dg;
- end;
- with pnodeFMs^[NodeNumofDG] do
- begin
- tmp := Calc_Grdt_for_Alpha_vivj(NodeNumofDG, phase_num);// vl_gradient1_dg updated inside
- if vl_Qmax_phase_dg <> 0 then
- dynBeta := (beta* abs(Bii)*100/j) *vl_V_ref1_dg *vl_V_ref1_dg
- /vl_Qmax_phase_dg;
- vl_alpha1_dg :=TempAlpha + dynBeta * vl_gradient1_dg;
- if vl_alpha1_dg >1 then vl_alpha1_dg := 1;
- if vl_alpha1_dg <-1 then vl_alpha1_dg := -1;
- end;
- result := pnodeFMs^[NodeNumofDG].vl_alpha1_dg;
- end;
- 2: begin //phase B
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- //for i := 1 to nodes do den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ i]; //4.is this correct
- //tmp1 := Nodes;
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only count dgs with 3 phases or 1 phase that is same number
- //or (pnodeFMs^[j].vl_phase_num_dg = phase_num) then
- or (pnodeFMs^[j].vl_nodeType_phase[2] = 1) then //this phase has DG
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j] ;
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha2_dg;
- //end;
- end;
-
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
- //2.calculate gradient----------------
- //vl_gradient1_dg := (vl_V_ref1_dg-vl_v1)*vl_V1/(den)/(vl_V_ref1_dg*vl_V_ref1_dg); //*vl_Qmax, 0311-by dahei
- //vl_gradient1_dg := (beta*vl_V_ref1_dg*vl_V_ref1_dg* abs(Bii)*100/j)*vl_gradient1_dg;
- end;
- with pnodeFMs^[NodeNumofDG] do
- begin
- tmp := Calc_Grdt_for_Alpha_vivj(NodeNumofDG, phase_num);
- if vl_Qmax_phase_dg <> 0 then
- dynBeta := (beta* abs(Bii)*100/j)/vl_Qmax_phase_dg*vl_V_ref2_dg *vl_V_ref2_dg; //
- vl_alpha2_dg :=TempAlpha + dynBeta * vl_gradient2_dg;
- if vl_alpha2_dg >1 then vl_alpha2_dg := 1;
- if vl_alpha2_dg <-1 then vl_alpha2_dg := -1;
- end;
- result := pnodeFMs^[NodeNumofDG].vl_alpha2_dg;
- end;
- 3: begin //phase C
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- //for i := 1 to nodes do den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ i]; //4.is this correct
- //tmp1 := Nodes;
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only count dgs with 3 phases or 1 phase that is same number
- //or (pnodeFMs^[j].vl_phase_num_dg = phase_num) then
- or (pnodeFMs^[j].vl_nodeType_phase[3] = 1) then //this phase has DG
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j] ;
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha3_dg;
- //end;
- end;
-
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
- //2.calculate gradient----------------
- //vl_gradient1_dg := (vl_V_ref1_dg-vl_v1)*vl_V1/(den)/(vl_V_ref1_dg*vl_V_ref1_dg); //*vl_Qmax, 0311-by dahei
- //vl_gradient1_dg := (beta*vl_V_ref1_dg*vl_V_ref1_dg* abs(Bii)*100/j)*vl_gradient1_dg;
- end;
- with pnodeFMs^[NodeNumofDG] do
- begin
- tmp := Calc_Grdt_for_Alpha_vivj(NodeNumofDG, phase_num);
- if vl_Qmax_phase_dg <> 0 then
- dynBeta := (beta* abs(Bii)*100/j)/vl_Qmax_phase_dg*vl_V_ref3_dg *vl_V_ref3_dg ; //
- vl_alpha3_dg :=TempAlpha + dynBeta * vl_gradient3_dg;
- if vl_alpha3_dg >1 then vl_alpha3_dg := 1;
- if vl_alpha3_dg <-1 then vl_alpha3_dg := -1;
- end;
- result := pnodeFMs^[NodeNumofDG].vl_alpha3_dg;
- end;
- 0: begin //pos seq value
-
- end;
- else
- end;
-end;
-
-Function TFMonitorObj.Calc_Alpha_L(NodeNumofDG, phase_num:Integer; dbNodeRef: integer; Bii,beta,Volt_Trhd: double):Double;
-Var
- i,j : integer;
- sum_Sij_j, den : double;
- alpha:double;
- den_dij,dii,TempAlpha ,tmp: Double;
- dynBeta : double;
- //tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7: double;
-begin
- update_all_nodes_info; // update voltages on all buses
- Get_PQ_DI( NodeNumofDG,ActorID ) ; // load measure
- // calclate alpha
- //with pnodeFMs^[NodeNumofDG] do
- case phase_num of
- 1: begin //phase A
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- //for i := 1 to nodes do den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ i]; //4.is this correct
- //tmp1 := Nodes;
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only count dgs with 3 phases or 1 phase that is same number
- //or (pnodeFMs^[j].vl_phase_num_dg = phase_num) then
- or (pnodeFMs^[j].vl_nodeType_phase[1] = 1) then //this phase has DG
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j] ;
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha1_dg;
- //end;
- end;
-
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
- //2.calculate gradient----------------
- //vl_gradient1_dg := (vl_V_ref1_dg-vl_v1)*vl_V1/(den)/(vl_V_ref1_dg*vl_V_ref1_dg); //*vl_Qmax, 0311-by dahei
- //vl_gradient1_dg := (beta*vl_V_ref1_dg*vl_V_ref1_dg* abs(Bii)*100/j)*vl_gradient1_dg;
- end;
- with pnodeFMs^[NodeNumofDG] do
- begin
- tmp := Calc_Grdt_for_Alpha(NodeNumofDG, phase_num);// vl_gradient1_dg updated inside
- if vl_Qmax_phase_dg <> 0 then
- dynBeta := (beta* abs(Bii)*100/j) *vl_V_ref1_dg *vl_V_ref1_dg
- /vl_Qmax_phase_dg;
- vl_alpha1_dg :=TempAlpha + dynBeta * vl_gradient1_dg;
- if vl_alpha1_dg >1 then vl_alpha1_dg := 1;
- if vl_alpha1_dg <-1 then vl_alpha1_dg := -1;
- end;
- result := pnodeFMs^[NodeNumofDG].vl_alpha1_dg;
- end;
- 2: begin //phase B
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- //for i := 1 to nodes do den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ i]; //4.is this correct
- //tmp1 := Nodes;
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only count dgs with 3 phases or 1 phase that is same number
- //or (pnodeFMs^[j].vl_phase_num_dg = phase_num) then
- or (pnodeFMs^[j].vl_nodeType_phase[2] = 1) then //this phase has DG
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j] ;
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha2_dg;
- //end;
- end;
-
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
- //2.calculate gradient----------------
- //vl_gradient1_dg := (vl_V_ref1_dg-vl_v1)*vl_V1/(den)/(vl_V_ref1_dg*vl_V_ref1_dg); //*vl_Qmax, 0311-by dahei
- //vl_gradient1_dg := (beta*vl_V_ref1_dg*vl_V_ref1_dg* abs(Bii)*100/j)*vl_gradient1_dg;
- end;
- with pnodeFMs^[NodeNumofDG] do
- begin
- tmp := Calc_Grdt_for_Alpha(NodeNumofDG, phase_num);
- if vl_Qmax_phase_dg <> 0 then
- dynBeta := (beta* abs(Bii)*100/j)/vl_Qmax_phase_dg*vl_V_ref2_dg *vl_V_ref2_dg; //
- vl_alpha2_dg :=TempAlpha + dynBeta * vl_gradient2_dg;
- if vl_alpha2_dg >1 then vl_alpha2_dg := 1;
- if vl_alpha2_dg <-1 then vl_alpha2_dg := -1;
- end;
- result := pnodeFMs^[NodeNumofDG].vl_alpha2_dg;
- end;
- 3: begin //phase C
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- //for i := 1 to nodes do den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ i]; //4.is this correct
- //tmp1 := Nodes;
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only count dgs with 3 phases or 1 phase that is same number
- //or (pnodeFMs^[j].vl_phase_num_dg = phase_num) then
- or (pnodeFMs^[j].vl_nodeType_phase[3] = 1) then //this phase has DG
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j] ;
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha3_dg;
- //end;
- end;
-
- end;
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
- //2.calculate gradient----------------
- //vl_gradient1_dg := (vl_V_ref1_dg-vl_v1)*vl_V1/(den)/(vl_V_ref1_dg*vl_V_ref1_dg); //*vl_Qmax, 0311-by dahei
- //vl_gradient1_dg := (beta*vl_V_ref1_dg*vl_V_ref1_dg* abs(Bii)*100/j)*vl_gradient1_dg;
- end;
- with pnodeFMs^[NodeNumofDG] do
- begin
- tmp := Calc_Grdt_for_Alpha(NodeNumofDG, phase_num);
- if vl_Qmax_phase_dg <> 0 then
- dynBeta := (beta* abs(Bii)*100/j)/vl_Qmax_phase_dg*vl_V_ref3_dg *vl_V_ref3_dg ; //
- vl_alpha3_dg :=TempAlpha + dynBeta * vl_gradient3_dg;
- if vl_alpha3_dg >1 then vl_alpha3_dg := 1;
- if vl_alpha3_dg <-1 then vl_alpha3_dg := -1;
- end;
- result := pnodeFMs^[NodeNumofDG].vl_alpha3_dg;
- end;
- 0: begin //pos seq value
-
- end;
- else
- end;
-end;
-
-{----------------------------------------------------------------------------------}
-Function TFMonitorObj.Calc_sum_dij_Alphaj(NodeNumofDG, phase_num,ActorID:Integer):Double;
-var
- j : integer;
- sum_Sij_j : double;
-begin
- update_all_nodes_info;
-
- // calclate alpha
- with pnodeFMs^[NodeNumofDG] do
- case phase_num of
- 1: begin //phase A
- vl_Alpha1_dg := 0.0;//init as zero
- sum_Sij_j := 0.0;
- //for j := 1 to Nodes do
- for j := 1 to NodeNumofDG-1 do
- begin
- vl_Alpha1_dg := vl_Alpha1_dg + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha1_dg ;
- sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNumofDG-1)*nodes +j];
- end;
- for j := NodeNumofDG + 1 to Nodes do
- begin
- vl_Alpha1_dg := vl_Alpha1_dg + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha1_dg ;
- sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNumofDG-1)*nodes +j];
- end;
- vl_Alpha1_dg := vl_Alpha1_dg / sum_Sij_j;
- //Alpha1 := Alpha1 - kcq* gradient1;
- result := vl_Alpha1_dg;
- end;
- 2: begin //phase B
- vl_Alpha2_dg := 0.0;//init as zero
- sum_Sij_j := 0.0;
- //for j := 1 to Nodes do
- for j := 1 to NodeNumofDG-1 do
- begin
- vl_Alpha2_dg := vl_Alpha2_dg + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha2_dg ;
- sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNumofDG-1)*nodes +j];
- end;
- for j := NodeNumofDG + 1 to Nodes do
- begin
- vl_Alpha2_dg := vl_Alpha2_dg + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha2_dg ;
- sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNumofDG-1)*nodes +j];
- end;
- vl_Alpha2_dg := vl_Alpha2_dg / sum_Sij_j;
- //Alpha2 := Alpha2 - kcq* gradient2;
- result := vl_Alpha2_dg;
- end;
- 3: begin //phase C
- vl_Alpha3_dg := 0.0;//init as zero
- sum_Sij_j := 0.0;
- //for j := 1 to Nodes do
- for j := 1 to NodeNumofDG-1 do
- begin
- vl_Alpha3_dg := vl_Alpha3_dg + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha3_dg ;
- sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNumofDG-1)*nodes +j];
- end;
- for j := NodeNumofDG + 1 to Nodes do
- begin
- vl_Alpha3_dg := vl_Alpha3_dg + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha3_dg ;
- sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNumofDG-1)*nodes +j];
- end;
- vl_Alpha3_dg := vl_Alpha3_dg / sum_Sij_j;
- //Alpha3 := Alpha3 - kcq* gradient3;
- result := vl_Alpha3_dg;
- end;
- 0: begin //pos seq value
- vl_Alpha_dg := 0.0;//init as zero
- sum_Sij_j := 0.0;
- //for j := 1 to Nodes do
- for j := 1 to NodeNumofDG-1 do
- begin
- vl_Alpha_dg := vl_Alpha_dg + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha_dg ;
- sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNumofDG-1)*nodes +j];
- end;
- for j := NodeNumofDG + 1 to Nodes do
- begin
- vl_Alpha_dg := vl_Alpha_dg + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha_dg ;
- sum_Sij_j := sum_Sij_j + pcommmatrix^[(NodeNumofDG-1)*nodes +j];
- end;
- vl_Alpha_dg := vl_Alpha_dg / sum_Sij_j;
- //Alpha := Alpha - kcq* gradient;
- result := vl_Alpha_dg;
- end;
- else
- end;
-end;
-Function TFMonitorObj.AvgPmax : double;
-var
- i,k : integer;
-begin
- result := 0.0;
- k := 1;
- //nodes;//all nodes included;
-
- for i := 1 to nodes do
- begin
- with pnodeFMs^[i] do
- if ((vl_PF_flag_dg=1) and (vl_cc_switch_dg=true)) then //
- begin
- result := result + vl_Pmax_dg ;
- result := result /k;
- k := k+1;
- end;
- end;
-end;
-Function TFMonitorObj.AvgQmax : double;
-var
- i,k : integer;
-begin
- result := 0.0;
- k := 1;
- //nodes;//all nodes included;
- for i := 1 to nodes do
- begin
- with pnodeFMs^[i] do
- if ((vl_QV_flag_dg=1) and (vl_cc_switch_dg=true)) then //volt/var control is on
- begin
- result := result + vl_Qmax_dg ;
- result := result /k;
- k := k+1;
- end;
- end;
-end;
-{----------------------------------------------------------------------}
-{}
-function TFMonitorObj.Get_FileName: String;
-begin
- Result := DSS.OutputDirectory + CircuitName_ + 'Mon_' + Name + '.csv'
-end;
-
-function TFMonitorObj.Calc_fm_ul_0(NodeNumofDG, phase_num:Integer; dbNodeRef: integer; Bii,beta,Volt_Trhd: double): double;
-var
- i,j : integer;
- den : double;
- den_dij,TempAlpha : Double;
- tmp : double;
- dly : integer;
-begin
-
- //update_all_nodes_info; // update voltages on all buses
- //with pNodeFMs^[NodeNumofDG] do
- // begin
- case phase_num of //pos seq
- 0: begin
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlpha := 0;
- {-----------------------------------}
- //no delay
- if T_intvl_smpl=0.0 then
- begin
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only 3 phase nodes
- and ((pnodeFMs^[j].vl_nodeType_phase[1]+pnodeFMs^[j].vl_nodeType_phase[2]+pnodeFMs^[j].vl_nodeType_phase[3]) = 3)
- then //this phase has DG
- begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j];
- TempAlpha := TempAlpha + pcommmatrix^[(NodeNumofDG-1)*nodes
- +j]*pnodeFMs^[j].vl_Alpha_dg;
- end;
-
- end;
- end
- //with delay
- else begin
- for j := 1 to NodeNumofDG-1 do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only 3 phase nodes
- and ((pnodeFMs^[j].vl_nodeType_phase[1]+pnodeFMs^[j].vl_nodeType_phase[2]+pnodeFMs^[j].vl_nodeType_phase[3]) = 3)
- then //this phase has DG
- begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j];
- //how many steps of delay from node j to node 'NodeNumofDG'
- dly := pcommDelaysteps^[(NodeNumofDG-1)*nodes +j];
- if dly=0 then
- begin
- TempAlpha := TempAlpha
- + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha_dg;
- end
- else begin
- TempAlpha := TempAlpha
- + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_smpl_dg[1][dly];
- end;
- end;
-
- end;
-
- j := NodeNumofDG;
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only 3 phase nodes
- and ((pnodeFMs^[j].vl_nodeType_phase[1]+pnodeFMs^[j].vl_nodeType_phase[2]+pnodeFMs^[j].vl_nodeType_phase[3]) = 3)
- then //this phase has DG
- begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j];
- TempAlpha := TempAlpha
- + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha_dg;
- end;
-
- for j := NodeNumofDG + 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only 3 phase nodes
- and ((pnodeFMs^[j].vl_nodeType_phase[1]+pnodeFMs^[j].vl_nodeType_phase[2]+pnodeFMs^[j].vl_nodeType_phase[3]) = 3)
- then //this phase has DG
- begin
- den_dij := den_dij+pCommMatrix^[(NodeNumofDG-1)*Nodes+ j];
- dly := pcommDelaysteps^[(NodeNumofDG-1)*nodes +j];
- if dly=0 then
- begin
- TempAlpha := TempAlpha
- + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_Alpha_dg;
- end
- else begin
- TempAlpha := TempAlpha
- + pcommmatrix^[(NodeNumofDG-1)*nodes +j]*pnodeFMs^[j].vl_smpl_dg[1][dly];
- end;
- end;
-
- end;
- end;
- {-----------------------------------}
-
- if den_dij=0 then TempAlpha := 0.0 else
- begin
- TempAlpha := TempAlpha/den_dij;
- end;
-
- tmp := (TempAlpha - pNodeFMs^[NodeNumofDG].vl_Alpha_dg);
- //Tolerance of alpha_i alpha_j
- if abs(tmp)< 0.002 then
- Result := 0.0
- else
- Result := tmp * pNodeFMs^[NodeNumofDG].vl_kc_ul_dg;
- end;
-
- end;
- //end;
- //result := 0;
-end;
-Function TFMonitorObj.Calc_fm_us_0(NodeNumofDG, phase_num:Integer; dbNodeRef: integer; Bii,beta,Volt_Trhd: double): double;
-var
- i,j : integer;
- den : double;
-
-begin
- //update voltage
- Get_PDElem_terminal_voltage(NodeNumofDG,pNodeFMs^[NodeNumofDG].vl_strMeasuredName,pNodeFMs^[NodeNumofDG].vl_terminalNum,ActorID ) ;
- //calc gradient
- with pNodeFMs^[NodeNumofDG] do
- begin
- case phase_num of //pos seq
- 0: begin
- den := abs(vl_Q_DG - vl_Q_Di- vl_V*vl_V* Bii); // pos ctrl: Bii use the first one
- if abs(den)1 then vl_gradient_dg := 1;
- if vl_gradient_dg <-1 then vl_gradient_dg := -1;
- result := vl_gradient_dg ;
- end;
-
- end;
- end;
-end;
-Procedure TFMonitorObj.Agnt_smpl(NodeNumofDG, phase_num,ActorID:Integer); //abandoned
-Var
- crnt_time: double;
- i : integer;
-begin
- //if True then
- if ActiveCircuit.Solution.DynaVars.IterationFlag = 1 then {1= Same Time Step as last iteration}
- begin
- //
- if pNodeFMs^[NodeNumofDG].vl_SmplCnt=0 then//the first step
- begin
- for i:= 1 to MaxLocalMem do
- pNodeFMs^[NodeNumofDG].vl_smpl_dg[1][i] := pNodeFMs^[NodeNumofDG].vl_Alpha_dg ;
- pNodeFMs^[NodeNumofDG].vl_smpl_dg[2][i] := pNodeFMs^[NodeNumofDG].vl_AlphaP_dg;
- end;
- //
- crnt_time := ActiveCircuit.Solution.DynaVars.intHour*3600+ActiveCircuit.Solution.DynaVars.t ;
- //Move the array only at the first time-step
-
- //if t_k greater or equal to current sample time plus smp interval, do another sample
- if crnt_time >= (T_intvl_smpl +pNodeFMs^[NodeNumofDG].vl_crnt_smp_time) then
- begin
- //if Trunc(crnt_time/T_intvl_smpl) >= pNodeFMs^[NodeNumofDG].vl_SmplCnt +1 then
- //begin
- //save alf into the first entry of smpl_ary for communication
- //alpha
- pNodeFMs^[NodeNumofDG].vl_smpl_dg[1][0] := pNodeFMs^[NodeNumofDG].vl_Alpha_dg;// [0] is the newest value
- // [0] and [1] are always the same
- //alphaP
- pNodeFMs^[NodeNumofDG].vl_smpl_dg[2][0] := pNodeFMs^[NodeNumofDG].vl_AlphaP_dg;//
- //0 seq voltage
- //pNoddeFMs^[NodeNumofDG].vl_smpl_dg[3][0] := pNodeFMs^[NodeNumofDG].vl_V;//
-
- for i := 0 to MaxLocalMem-1 do // [0]->[1],[MaxLocalMem-1]->[MaxLocalMem]
- begin
- pNodeFMs^[NodeNumofDG].vl_smpl_dg[1][MaxLocalMem-i] := pNodeFMs^[NodeNumofDG].vl_smpl_dg[1][MaxLocalMem-i-1] ;
- pNodeFMs^[NodeNumofDG].vl_smpl_dg[2][MaxLocalMem-i] := pNodeFMs^[NodeNumofDG].vl_smpl_dg[2][MaxLocalMem-i-1] ;
- end;
-
- //vl_SmplCnt increase
- inc(pNodeFMs^[NodeNumofDG].vl_SmplCnt);
- //update vl_crnt_time
- pNodeFMs^[NodeNumofDG].vl_crnt_smp_time := crnt_time;
- //end;
- end;
-
- end;
-end;
-Procedure TFMonitorObj.Init_delay_array(NodeNumofDG, ActorID:Integer);
-Var
- i : integer;
-begin
- //measure all voltages
- for i := 1 to nodes do
- Get_PDElem_terminal_voltage(i, pnodefms^[i].vl_strMeasuredName, pnodefms^[i].vl_terminalNum,ActorID ) ;
- // inti delay array
- for i:= 0 to MaxLocalMem do
- begin
- //alpha
- pNodeFMs^[NodeNumofDG].vl_smpl_dg[1][i] := pNodeFMs^[NodeNumofDG].vl_Alpha_dg;
- //alphaP
- pNodeFMs^[NodeNumofDG].vl_smpl_dg[2][i] := pNodeFMs^[NodeNumofDG].vl_AlphaP_dg ;
- // vl_v, which is the 0 seq. voltage
- pNodeFMs^[NodeNumofDG].vl_smpl_dg[3][i] := pNodeFMs^[NodeNumofDG].vl_V ;
-
- end;
-end;
-
-Function TFMonitorObj.Calc_Gradient_ct_P(NodeNuminClstr, phase_num, ActorID:Integer):Double; // NodeNuminClstr: node number in cluster
-var
- dvDGtemp : double;
- Grdnt_P : double;
-begin
- //tempCplx
- dvDGtemp := (pNodeFMs^[NodeNuminClstr].vl_V - pNodeFMs^[NodeNuminClstr].vl_V_ref_dg)
- /pNodeFMs^[NodeNuminClstr].vl_V_ref_dg; //
- // if this DG is above 1.05, then it should have P curtail gradient
- //if ( ActiveCircuit.Solution.bCurtl=true ) and (dvDGtemp>0.0) then //overall system need control
- if ( ActiveCircuit.Solution.bCurtl=true ) then
- begin
- if ld_fm_info[0].b_Curt_Ctrl=true then // if false, the curtailment will be zero for any node in this cluster
- Grdnt_P := (activecircuit.Solution.LD_FM[0].volt_lwst
- -1.0)
- //- activecircuit.Solution.LD_FM[0].volt_lw_lmt);
- end else
- Grdnt_P := 0.0;
- //!!!!!!!!!!!!!
- case phase_num of //pos seq
- 0: begin
- result := Grdnt_P; //
- end;
- 1: begin
- result := Grdnt_P; //
- end;
- 2: begin
- result := Grdnt_P; //
- end;
- 3: begin
- result := Grdnt_P; //
- end;
- end;
-end;
-//////////////////////////
-{-------------------------}
-Function TFMonitorObj.Calc_ul_P(NodeNuminClstr, phase_num:Integer):Double; // NodeNuminClstr: node number in cluster //with delay
-var
- j : integer;
- den_dij,TempAlphaP : Double;
- dly : integer;
-
-begin
- //alphaP = sum (alphaP) + Beta * Gp
-
- //with pNodeFMs^[NodeNuminClstr] do
- // begin
- case phase_num of //pos seq
- 0: begin
- //1.calculate d_ij*alpha_j summation
- den_dij := 0;
- TempAlphaP := 0;
- {-----------------------------------}
- //no delay
- if T_intvl_smpl=0.0 then
- begin
- for j := 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only 3 phase nodes
- and ((pnodeFMs^[j].vl_nodeType_phase[1]+pnodeFMs^[j].vl_nodeType_phase[2]+pnodeFMs^[j].vl_nodeType_phase[3]) = 3)
- then // has 3-phase DG
- // if (pnodeFMs^[j].vl_ndphases_dg = 3) then //only 3 phase nodes
- begin
- //if pnodeFMs^[j].vl_nodeType = 1 then // only DG nodes
- //begin
- den_dij := den_dij+pCommMatrix^[(NodeNuminClstr-1)*Nodes+ j];
- TempAlphaP := TempAlphaP
- + pcommmatrix^[(NodeNuminClstr-1)*nodes +j]*pnodeFMs^[j].vl_AlphaP_dg;
- //end;
- end;
- end;
- end else
- // with delay
- begin
- for j := 1 to NodeNuminClstr-1 do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only 3 phase nodes
- and ((pnodeFMs^[j].vl_nodeType_phase[1]+pnodeFMs^[j].vl_nodeType_phase[2]+pnodeFMs^[j].vl_nodeType_phase[3]) = 3)
- then //has 3-phase DG
- begin
- den_dij := den_dij+pCommMatrix^[(NodeNuminClstr-1)*Nodes+ j];
- //how many steps of delay from node j to node 'NodeNumofDG'
- dly := pcommDelaysteps^[(NodeNuminClstr-1)*nodes +j];
- if dly=0 then
- begin
- TempAlphaP := TempAlphaP
- + pcommmatrix^[(NodeNuminClstr-1)*nodes +j]*pnodeFMs^[j].vl_AlphaP_dg;
- end
- else begin
- TempAlphaP := TempAlphaP
- + pcommmatrix^[(NodeNuminClstr-1)*nodes +j]*pnodeFMs^[j].vl_smpl_dg[2][dly];
- end;
- end;
-
- end;
-
- j := NodeNuminClstr;
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only 3 phase nodes
- and ((pnodeFMs^[j].vl_nodeType_phase[1]+pnodeFMs^[j].vl_nodeType_phase[2]+pnodeFMs^[j].vl_nodeType_phase[3]) = 3)
- then //has 3-phase DG
- begin
- den_dij := den_dij+pCommMatrix^[(NodeNuminClstr-1)*Nodes+ j];
- TempAlphaP := TempAlphaP
- + pcommmatrix^[(NodeNuminClstr-1)*nodes +j]*pnodeFMs^[j].vl_AlphaP_dg;
- end;
-
- for j := NodeNuminClstr + 1 to Nodes do
- begin
- if (pnodeFMs^[j].vl_ndphases_dg = 3) //only 3 phase nodes
- and ((pnodeFMs^[j].vl_nodeType_phase[1]+pnodeFMs^[j].vl_nodeType_phase[2]+pnodeFMs^[j].vl_nodeType_phase[3]) = 3)
- then //has 3-phase DG
- begin
- den_dij := den_dij+pCommMatrix^[(NodeNuminClstr-1)*Nodes+ j];
- dly := pcommDelaysteps^[(NodeNuminClstr-1)*nodes +j];
- if dly=0 then
- begin
- TempAlphaP := TempAlphaP
- + pcommmatrix^[(NodeNuminClstr-1)*nodes +j]*pnodeFMs^[j].vl_AlphaP_dg;
- end
- else begin
- TempAlphaP := TempAlphaP
- + pcommmatrix^[(NodeNuminClstr-1)*nodes +j]*pnodeFMs^[j].vl_smpl_dg[2][dly];
- end;
- end;
-
- end;
- end;
- if den_dij=0 then TempAlphaP := 0.0 else
- begin
- TempAlphaP := TempAlphaP/den_dij; //the average
- end;
- //Tolerance of alphap_i alphap_j
- TempAlphaP := TempAlphaP - pNodeFMs^[NodeNuminClstr].vl_AlphaP_dg; //uL for cooperative control of active power
- if abs(TempAlphaP)< 0.002 then
- Result := 0.0
- else
- Result := TempAlphaP;
- end;
- end;
- // end;
-end;
-
-Procedure TFMonitorObj.update_ld_info; //all nodes , p.u. value
-var
- i : integer;
- v0_tmp : double;
-begin
-
- ld_fm_info[0].volt_avg := 0.0; //recalculate voltage average
- ld_fm_info[0].volt_lwst := 999999; //search new value at each round
- ld_fm_info[0].volt_hghst := -99999;
- for i := 1 to nodes do
- begin
- //update all nodes voltage into agents
- Get_PDElem_terminal_voltage(i, pnodefms^[i].vl_strMeasuredName, pnodefms^[i].vl_terminalNum ) ;
- //
- //synchronous sampling
- v0_tmp := pnodefms^[i].vl_V/(pnodefms^[i].vl_basevolt);
- //update highest voltage
- if ld_fm_info[0].volt_hghst < v0_tmp then
- begin
- ld_fm_info[0].volt_hghst := v0_tmp;
- ld_fm_info[0].ndnum_hghst := i;
- end;
- //update lowest voltage
- if ld_fm_info[0].volt_lwst > v0_tmp then
- begin
- ld_fm_info[0].volt_lwst := v0_tmp;
- ld_fm_info[0].ndnum_lwst := i;
- end;
-
- //other information should be updated?
- //
- ld_fm_info[0].volt_avg := ld_fm_info[0].volt_avg + v0_tmp; //p.u.
- end;
-
- ld_fm_info[0].volt_avg := ld_fm_info[0].volt_avg / nodes;
-end;
-Procedure TFMonitorObj.update_ld_dly( ActorID: integer); //all nodes , p.u. value
-var
- i, j , ndlys : integer;
- v0_tmp : double;
- crnt_time: double;
-begin
- ld_fm_info[0].volt_avg := 0.0; //recalculate voltage average
- ld_fm_info[0].volt_lwst := 999999; //search new value at each round
- ld_fm_info[0].volt_hghst := -99999;
- for i := 1 to Nodes do
- begin
- //update vl_v1/v2/v3, vl_v_1c/v_2c/v_3c, update vl_v for node i
- Get_PDElem_terminal_voltage(i, pnodefms^[i].vl_strMeasuredName, pnodefms^[i].vl_terminalNum, ActorID ) ;
- //synchronous sampling
- if t_intvl_smpl=0.0 then
- begin
- // pNodeFMs^[i].vl_smpl_dg[i][j] is not used
- v0_tmp := pnodefms^[i].vl_V/(pnodefms^[i].vl_basevolt);
- end else
- begin
- //asynchronous sampling
- //update pNodeFMs^[i].vl_smpl_dg[i][j] first
- if pNodeFMs^[i].vl_SmplCnt=0 then//the first step
- begin
- for j:= 0 to MaxLocalMem do
- //alphas
- pNodeFMs^[i].vl_smpl_dg[1][j] := pNodeFMs^[i].vl_Alpha_dg ;
- pNodeFMs^[i].vl_smpl_dg[2][j] := pNodeFMs^[i].vl_AlphaP_dg;
- //voltage
- pNodeFMs^[i].vl_smpl_dg[3][j] := pNodeFMs^[i].vl_V ; // 0 seq.
- end;
- //
- crnt_time := ActiveCircuit.Solution.DynaVars.intHour*3600+ActiveCircuit.Solution.DynaVars.t ;
- //Move the array only at the first time-step
-
- //if t_k greater or equal to current sample time plus smp interval, do another sample
- if crnt_time >= (T_intvl_smpl +pNodeFMs^[i].vl_crnt_smp_time) then
- begin
-
- //if Trunc(crnt_time/T_intvl_smpl) >= pNodeFMs^[NodeNumofDG].vl_SmplCnt +1 then
- //begin
- //save alf into the first entry of smpl_ary for communication
- //alpha
- pNodeFMs^[i].vl_smpl_dg[1][0] := pNodeFMs^[i].vl_Alpha_dg;// [0] is the newest value
- //alphaP
- pNodeFMs^[i].vl_smpl_dg[2][0] := pNodeFMs^[i].vl_AlphaP_dg;//
- // VL_V //0 seq voltage
- pNodeFMs^[i].vl_smpl_dg[3][0] := pNodeFMs^[i].vl_V;//
-
-
- //pNoddeFMs^[NodeNumofDG].vl_smpl_dg[3][0] := pNodeFMs^[NodeNumofDG].vl_V;//
-
- for j := 0 to MaxLocalMem-1 do // [0]->[1],[MaxLocalMem-1]->[MaxLocalMem]
- begin
- pNodeFMs^[i].vl_smpl_dg[1][MaxLocalMem-j] := pNodeFMs^[i].vl_smpl_dg[1][MaxLocalMem-j-1] ;
- pNodeFMs^[i].vl_smpl_dg[2][MaxLocalMem-j] := pNodeFMs^[i].vl_smpl_dg[2][MaxLocalMem-j-1] ;
- pNodeFMs^[i].vl_smpl_dg[3][MaxLocalMem-j] := pNodeFMs^[i].vl_smpl_dg[3][MaxLocalMem-j-1]
- end;
-
- //vl_SmplCnt increase
- inc(pNodeFMs^[i].vl_SmplCnt);
- //update vl_crnt_time
- pNodeFMs^[i].vl_crnt_smp_time := crnt_time;
- //end;
- end;
- // delay steps from agent to virtual leader
- ndlys := pcommDelaysteps^[(virtual_Ld_Nd - 1)*Nodes +i];
- // total delay steps: ndlys+nup_dlys
- v0_tmp := pnodefms^[i].vl_smpl_dg[3][ndlys+nUp_dlys]/(pnodefms^[i].vl_basevolt);
- end;
- //update highest voltage
- if ld_fm_info[0].volt_hghst < v0_tmp then
- begin
- ld_fm_info[0].volt_hghst := v0_tmp;
- ld_fm_info[0].ndnum_hghst := i;
- end;
- //update lowest voltage
- if ld_fm_info[0].volt_lwst > v0_tmp then
- begin
- ld_fm_info[0].volt_lwst := v0_tmp;
- ld_fm_info[0].ndnum_lwst := i;
- end;
-
- //other information should be updated?
- //
- ld_fm_info[0].volt_avg := ld_fm_info[0].volt_avg + v0_tmp; //p.u.
- end;
- //avg of valtage
- ld_fm_info[0].volt_avg := ld_fm_info[0].volt_avg / nodes;
-end;
-
-initialization
- //WriteDLLDebugFile('Monitor');
-
-end.
-
-
-
diff --git a/src/PCElements/GICLine.pas b/src/PCElements/GICLine.pas
index b2c187d40..02a9088b9 100644
--- a/src/PCElements/GICLine.pas
+++ b/src/PCElements/GICLine.pas
@@ -7,30 +7,26 @@
----------------------------------------------------------
}
-{
- 6-23-2011 Created from VSource object
-
- Simplified 2-terminal VSource with series impedance for GIC studies.
- For representing induced voltages in lines
-
- Contains blocking capacitor inherent in model. Set C > 0.0 to activate.
-
- Blocking capacitors may also be added as separate items or the branch may be
- disconnected.
-
- Example:
- New GICline.Myline Bus1=MyBus1 Bus2=MyBus2 Volts=1234 R=0.5
-
- This takes the following defaults:
- Angle=0
- X=0
- C=0
- Frequency=0.1 Hz
- Sequence = ZERO sequence
- ScanType = ZERO sequence
-
-
-}
+// 6-23-2011 Created from VSource object
+//
+// Simplified 2-terminal VSource with series impedance for GIC studies.
+// For representing induced voltages in lines
+//
+// Contains blocking capacitor inherent in model. Set C > 0.0 to activate.
+//
+// Blocking capacitors may also be added as separate items or the branch may be
+// disconnected.
+//
+// Example:
+// New GICline.Myline Bus1=MyBus1 Bus2=MyBus2 Volts=1234 R=0.5
+//
+// This takes the following defaults:
+// Angle=0
+// X=0
+// C=0
+// Frequency=0.1 Hz
+// Sequence = ZERO sequence
+// ScanType = ZERO sequence
interface
@@ -40,26 +36,42 @@ interface
PCClass,
PCElement,
ucmatrix,
- ucomplex,
+ UComplex, DSSUcomplex,
Spectrum;
type
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+{$SCOPEDENUMS ON}
+ TGICLineProp = (
+ INVALID = 0,
+ bus1 = 1,
+ bus2 = 2,
+ Volts = 3,
+ Angle = 4,
+ frequency = 5,
+ phases = 6,
+ R = 7,
+ X = 8,
+ C = 9,
+ EN = 10,
+ EE = 11,
+ Lat1 = 12,
+ Lon1 = 13,
+ Lat2 = 14,
+ Lon2 = 15
+ );
+{$SCOPEDENUMS OFF}
+
TGICLine = class(TPCClass)
- PRIVATE
- procedure GICLineSetBus1(const S: String);
PROTECTED
- procedure DefineProperties;
- function MakeLike(const OtherLine: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TGICLineObj = class(TPCElement)
PRIVATE
Angle: Double;
@@ -91,6 +103,8 @@ TGICLineObj = class(TPCElement)
constructor Create(ParClass: TDSSClass; const SourceName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
@@ -99,19 +113,14 @@ TGICLineObj = class(TPCElement)
procedure GetInjCurrents(Curr: pComplexArray);
procedure GetCurrents(Curr: pComplexArray); OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
implementation
uses
- ParserDel,
Circuit,
DSSClassDefs,
DSSGlobals,
@@ -122,306 +131,157 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TGICLineObj;
+ TProp = TGICLineProp;
const
- NumPropsThisClass = 15;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TGICLine.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TGICLine.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'GICLine';
- DSSClassType := GIC_Line + PC_ELEMENT;
-
- ActiveElement := 0;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, GIC_Line, 'GICLine');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TGICLine.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TGICLine.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
- PropertyName[1] := 'bus1';
- PropertyName[2] := 'bus2';
- PropertyName[3] := 'Volts';
- PropertyName[4] := 'Angle';
- PropertyName[5] := 'frequency';
- PropertyName[6] := 'phases';
- PropertyName[7] := 'R';
- PropertyName[8] := 'X';
- PropertyName[9] := 'C';
- // PropertyName[10] := 'ScanType';
- // PropertyName[11] := 'Sequence';
- PropertyName[10] := 'EN';
- PropertyName[11] := 'EE';
- PropertyName[12] := 'Lat1';
- PropertyName[13] := 'Lon1';
- PropertyName[14] := 'Lat2';
- PropertyName[15] := 'Lon2';
-
- // define Property help values
- PropertyHelp[1] := 'Name of bus to which the main terminal (1) is connected.' + CRLF +
- 'bus1=busname' + CRLF +
- 'bus1=busname.1.2.3';
- PropertyHelp[2] := 'Name of bus to which 2nd terminal is connected.' + CRLF +
- 'bus2=busname' + CRLF +
- 'bus2=busname.1.2.3' + CRLF + CRLF +
- 'No Default; must be specified.';
-
- PropertyHelp[3] := 'Voltage magnitude, in volts, of the GIC voltage induced across this line. ' +
- 'When spedified, voltage source is assumed defined by Voltage and Angle properties. ' + CRLF + CRLF +
- 'Specify this value' + CRLF + CRLF + 'OR' + CRLF + CRLF +
- 'EN, EE, lat1, lon1, lat2, lon2. ' + CRLF + CRLF +
- 'Not both!! Last one entered will take precedence. ' +
- 'Assumed identical in each phase of the Line object.';
- PropertyHelp[4] := 'Phase angle in degrees of first phase. Default=0.0. See Voltage property';
- PropertyHelp[5] := 'Source frequency. Defaults to 0.1 Hz.';
- PropertyHelp[6] := 'Number of phases. Defaults to 3.';
- PropertyHelp[7] := 'Resistance of line, ohms of impedance in series with GIC voltage source. ';
- PropertyHelp[8] := 'Reactance at base frequency, ohms. Default = 0.0. This value is generally not important for GIC studies but may be used if desired.';
- PropertyHelp[9] := 'Value of line blocking capacitance in microfarads. Default = 0.0, implying that there is no line blocking capacitor.';
- // PropertyHelp[10] := '{pos | zero* | none} Maintain specified sequence for harmonic solution. Default is ZERO sequence. '+
- // 'Otherwise, angle between phases rotates with harmonic.';
- // PropertyHelp[11] := '{pos | neg | zero*} Set the phase angles for the specified symmetrical component sequence for non-harmonic solution modes. '+
- // 'Default is ZERO sequence. ';
- PropertyHelp[10] := 'Northward Electric field (V/km). If specified, Voltage and Angle are computed from EN, EE, lat and lon values.';
- PropertyHelp[11] := 'Eastward Electric field (V/km). If specified, Voltage and Angle are computed from EN, EE, lat and lon values.';
- PropertyHelp[12] := 'Latitude of Bus1 (degrees)';
- PropertyHelp[13] := 'Longitude of Bus1 (degrees)';
- PropertyHelp[14] := 'Latitude of Bus2 (degrees)';
- PropertyHelp[15] := 'Longitude of Bus2 (degrees)';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyType[ord(TProp.bus2)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+ PropertyOffset[ord(TProp.bus2)] := 2;
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.Volts)] := ptruint(@obj.Volts);
+ PropertyOffset[ord(TProp.Angle)] := ptruint(@obj.Angle);
+ PropertyOffset[ord(TProp.R)] := ptruint(@obj.R);
+ PropertyOffset[ord(TProp.X)] := ptruint(@obj.X);
+ PropertyOffset[ord(TProp.C)] := ptruint(@obj.C);
+ PropertyOffset[ord(TProp.Lat1)] := ptruint(@obj.Lat1);
+ PropertyOffset[ord(TProp.Lon1)] := ptruint(@obj.Lon1);
+ PropertyOffset[ord(TProp.Lat2)] := ptruint(@obj.Lat2);
+ PropertyOffset[ord(TProp.Lon2)] := ptruint(@obj.Lon2);
+ PropertyOffset[ord(TProp.frequency)] := ptruint(@obj.SrcFrequency);
+ PropertyOffset[ord(TProp.EN)] := ptruint(@obj.ENorth);
+ PropertyOffset[ord(TProp.EE)] := ptruint(@obj.EEast);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- // Override help string
- PropertyHelp[NumPropsThisClass + 1] := 'Inherited Property for all PCElements. Name of harmonic spectrum for this source. Default is "defaultvsource", which is defined when the DSS starts.';
- PropertyHelp[NumPropsThisClass + 2] := 'Inherited Property for all PCElements. Base frequency for specification of reactance value.';
+ inherited DefineProperties;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGICLine.NewObject(const ObjName: String): Integer;
+function TGICLine.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new voltage source and add it to GICLine class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TGICLineObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TGICLine.GICLineSetBus1(const S: String);
+procedure TGICLineObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- s2: String;
+ S, S2: String;
dotpos: Integer;
-
- // Special handling for Bus 1
- // Set Bus2 = Bus1.0.0.0
-
begin
- with DSS.ActiveGICLineObj do
- begin
- SetBus(1, S);
-
- // Strip node designations from S
- dotpos := Pos('.', S);
- if dotpos > 0 then
- S2 := Copy(S, 1, dotpos - 1)
- else
- S2 := Copy(S, 1, Length(S)); // copy up to Dot
+ case Idx of
+ 1:
+ // Special handling for Bus 1
+ // Set Bus2 = Bus1.0.0.0
+ begin
+ S := GetBus(1);
+ // Strip node designations from S
+ dotpos := Pos('.', S);
+ if dotpos > 0 then
+ S2 := Copy(S, 1, dotpos - 1)
+ else
+ S2 := Copy(S, 1, Length(S)); // copy up to Dot
- SetBus(2, S2); // default setting for Bus2 is same as Bus1
+ SetBus(2, S2); // default setting for Bus2 is same as Bus1
+ end;
+ ord(TProp.phases):
+ NConds := Fnphases; // Force Reallocation of terminal info
+ 3, 4:
+ VoltsSpecified := TRUE;
+ 10..15:
+ VoltsSpecified := FALSE;
end;
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGICLine.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName,
- Param: String;
-
+function TGICLine.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
begin
- // continue parsing with contents of Parser
- DSS.ActiveGICLineObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveGICLineObj;
-
- Result := 0;
-
- with DSS.ActiveGICLineObj do
+ with TObj(ptr) do
begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "VSource.' + Name + '"', 320);
- 1:
- GICLineSetBus1(param); // special handling of Bus 1
- 2:
- SetBus(2, param);
-
- 3:
- Volts := Parser.DblValue; // basekv
- 4:
- Angle := Parser.DblValue; // Ang
- 5:
- SrcFrequency := Parser.DblValue; // freq
- 6:
- begin
- Nphases := Parser.Intvalue; // num phases
- NConds := Fnphases; // Force Reallocation of terminal info
- end;
- 7:
- R := Parser.DblValue;
- 8:
- X := Parser.DblValue;
- 9:
- C := Parser.DblValue;
-
- (* 10: Case Uppercase(Param)[1] of
- 'P': ScanType := 1;
- 'Z': ScanType := 0;
- 'N': ScanType := -1;
- ELSE
- DoSimpleMsg('Unknown Scan Type for "' + Class_Name +'.'+ Name + '": '+Param, 321);
- END;
- 11: Case Uppercase(Param)[1] of
- 'P': Sequencetype := 1;
- 'Z': Sequencetype := 0;
- 'N': Sequencetype := -1;
- ELSE
- DoSimpleMsg('Unknown Sequence Type for "' + Class_Name +'.'+ Name + '": '+Param, 321);
- END;
- *)
- 10:
- ENorth := Parser.DblValue;
- 11:
- EEast := Parser.DblValue;
- 12:
- Lat1 := Parser.DblValue;
- 13:
- Lon1 := Parser.DblValue;
- 14:
- Lat2 := Parser.DblValue;
- 15:
- Lon2 := Parser.DblValue;
-
- else
- ClassEdit(DSS.ActiveGICLineObj, ParamPointer - NumPropsThisClass)
- end;
-
- case ParamPointer of
- 3, 4:
- VoltsSpecified := TRUE;
- 10..15:
- VoltsSpecified := FALSE;
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
RecalcElementData;
YPrimInvalid := TRUE;
+ Exclude(Flags, Flg.EditionActive);
end;
-
+ Result := True;
end;
-//----------------------------------------------------------------------------
-function TGICLine.MakeLike(const OtherLine: String): Integer;
+procedure TGICLineObj.MakeLike(OtherPtr: Pointer);
var
- OtherGICLine: TGICLineObj;
- i: Integer;
-
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this line name in the present collection}
- OtherGICLine := Find(OtherLine);
- if OtherGICLine <> NIL then
- with DSS.ActiveGICLineObj do
- begin
-
- if Fnphases <> OtherGICLine.Fnphases then
- begin
- Nphases := OtherGICLine.Fnphases;
- NConds := Fnphases; // Forces reallocation of terminal stuff
+ inherited MakeLike(OtherPtr);
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
-
- if Z <> NIL then
- Z.Free;
- if Zinv <> NIL then
- Zinv.Free;
-
- Z := TCmatrix.CreateMatrix(Fnphases);
- Zinv := TCMatrix.CreateMatrix(Fnphases);
- end;
-
- Z.CopyFrom(OtherGICLine.Z);
- // Zinv.CopyFrom(OtherLine.Zinv);
- R := OtherGICLine.R;
- X := OtherGICLine.X;
- C := OtherGICLine.C;
- Volts := OtherGICLine.Volts;
- Angle := OtherGICLine.Angle;
+ Other := TObj(OtherPtr);
+ if Fnphases <> Other.Fnphases then
+ begin
+ FNphases := Other.Fnphases;
+ NConds := Fnphases; // Forces reallocation of terminal stuff
- SrcFrequency := OtherGICLine.SrcFrequency;
- Scantype := OtherGICLine.Scantype;
- Sequencetype := OtherGICLine.Sequencetype;
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
- ClassMakeLike(OtherGICLine);
+ if Z <> NIL then
+ Z.Free;
+ if Zinv <> NIL then
+ Zinv.Free;
- for i := 1 to ParentClass.NumProperties do
- FPropertyValue[i] := OtherGICLine.FPropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in GICLine MakeLike: "' + OtherLine + '" Not Found.', 322);
+ Z := TCmatrix.CreateMatrix(Fnphases);
+ Zinv := TCMatrix.CreateMatrix(Fnphases);
+ end;
+ Z.CopyFrom(Other.Z);
+ // Zinv.CopyFrom(OtherLine.Zinv);
+ R := Other.R;
+ X := Other.X;
+ C := Other.C;
+ Volts := Other.Volts;
+ Angle := Other.Angle;
+
+ SrcFrequency := Other.SrcFrequency;
+ Scantype := Other.Scantype;
+ Sequencetype := Other.Sequencetype;
end;
-//=============================================================================
function TGICLineObj.Compute_VLine: Double;
var
-
Phi: Double;
DeltaLat, DeltaLon: Double;
begin
-
Phi := (Lat2 + Lat1) / 2.0 * (pi / 180.0); // deg to radians
DeltaLat := Lat2 - Lat1;
DeltaLon := Lon2 - Lon1;
@@ -433,10 +293,10 @@ function TGICLineObj.Compute_VLine: Double;
constructor TGICLineObj.Create(ParClass: TDSSClass; const SourceName: String);
begin
inherited create(ParClass);
- Name := LowerCase(SourceName);
+ Name := AnsiLowerCase(SourceName);
DSSObjType := ParClass.DSSClassType; //SOURCE + NON_PCPD_ELEM; // Don't want this in PC Element List
- Nphases := 3;
+ FNphases := 3;
Fnconds := 3;
Nterms := 2; // Now a 2-terminal device
Z := NIL;
@@ -461,17 +321,12 @@ constructor TGICLineObj.Create(ParClass: TDSSClass; const SourceName: String);
Scantype := 0;
SequenceType := 0; // default to zero sequence (same voltage induced in all phases)
- Spectrum := ''; // no default
-
- InitPropertyValues(0);
+ SpectrumObj := NIL; // no default
Yorder := Fnterms * Fnconds;
RecalcElementData;
-
end;
-
-//=============================================================================
destructor TGICLineObj.Destroy;
begin
Z.Free;
@@ -480,12 +335,10 @@ destructor TGICLineObj.Destroy;
inherited Destroy;
end;
-//=============================================================================
procedure TGICLineObj.RecalcElementData;
var
Zs, Zm: Complex;
i, j: Integer;
-
begin
if Z <> NIL then
Z.Free;
@@ -516,28 +369,17 @@ procedure TGICLineObj.RecalcElementData;
Vmag := Volts;
- SpectrumObj := DSS.SpectrumClass.Find(Spectrum);
- if (SpectrumObj = NIL) and (Length(Spectrum) > 0) then
- begin
- DoSimpleMsg('Spectrum Object "' + Spectrum + '" for Device GICLine.' + Name + ' Not Found.', 324);
- end;
-
Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
-
end;
-//=============================================================================
procedure TGICLineObj.CalcYPrim;
-
var
Value: Complex;
i, j: Integer;
FreqMultiplier: Double;
Xc: Double;
-
begin
-
- // Build only YPrim Series
+ // Build only YPrim Series
if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Series = NIL) {YPrimInvalid} then
begin
if YPrim_Series <> NIL then
@@ -578,8 +420,9 @@ procedure TGICLineObj.CalcYPrim;
if Zinv.InvertError > 0 then
begin {If error, put in Large series conductance}
- DoErrorMsg('TGICLineObj.CalcYPrim', 'Matrix Inversion Error for GICLine "' + Name + '"',
- 'Invalid impedance specified. Replaced with small resistance.', 325);
+ DoErrorMsg('TGICLineObj.CalcYPrim',
+ Format(_('Matrix Inversion Error for GICLine "%s"'), [Name]),
+ _('Invalid impedance specified. Replaced with small resistance.'), 325);
Zinv.Clear;
for i := 1 to Fnphases do
Zinv.SetElement(i, i, Cmplx(1.0 / EPSILON, 0.0));
@@ -594,7 +437,7 @@ procedure TGICLineObj.CalcYPrim;
Value := Zinv.GetElement(i, j);
YPrim_series.SetElement(i, j, Value);
YPrim_series.SetElement(i + FNPhases, j + FNPhases, Value);
- YPrim_series.SetElemsym(i + FNPhases, j, CNegate(Value))
+ YPrim_series.SetElemsym(i + FNPhases, j, -Value)
end;
end;
@@ -605,23 +448,17 @@ procedure TGICLineObj.CalcYPrim;
inherited CalcYPrim;
YPrimInvalid := FALSE;
-
end;
-//=============================================================================
procedure TGICLineObj.GetVterminalForSource;
-
var
i: Integer;
Vharm: Complex;
SrcHarmonic: Double;
-
begin
-
try
-
- {This formulation will theoretically handle voltage sources of any number of phases assuming they are
- equally displaced in time.}
+ // This formulation will theoretically handle voltage sources of any number of phases assuming they are
+ // equally displaced in time.
Vmag := Volts;
with ActiveCircuit.Solution do
@@ -629,7 +466,7 @@ procedure TGICLineObj.GetVterminalForSource;
if IsHarmonicModel and (SpectrumObj <> NIL) then
begin
SrcHarmonic := Frequency / SrcFrequency;
- Vharm := CMulReal(SpectrumObj.GetMult(SrcHarmonic), Vmag); // Base voltage for this harmonic
+ Vharm := SpectrumObj.GetMult(SrcHarmonic) * Vmag; // Base voltage for this harmonic
RotatePhasorDeg(Vharm, SrcHarmonic, Angle); // Rotate for phase 1 shift
for i := 1 to Fnphases do
begin
@@ -668,63 +505,46 @@ procedure TGICLineObj.GetVterminalForSource;
end;
except
- DoSimpleMsg('Error computing Voltages for GICLine.' + Name + '. Check specification. Aborting.', 326);
+ DoSimpleMsg('Error computing Voltages for %s. Check specification. Aborting.', [FullName], 326);
if DSS.In_Redirect then
DSS.Redirect_Abort := TRUE;
end;
-
end;
-//===========================================================================
-
function TGICLineObj.InjCurrents: Integer;
-
begin
-
GetInjCurrents(InjCurrent);
-
-{This is source injection}
-
+ // This is source injection
Result := inherited InjCurrents; // Add into system array
-
end;
-//===========================================================================
procedure TGICLineObj.GetCurrents(Curr: pComplexArray);
-
var
i: Integer;
-
begin
try
- with ActiveCircuit.Solution do
+ with ActiveCircuit.Solution do
begin
-
for i := 1 to Yorder do
Vterminal^[i] := NodeV^[NodeRef^[i]];
YPrim.MVMult(Curr, Vterminal); // Current from Elements in System Y
GetInjCurrents(ComplexBuffer); // Get present value of inj currents
- // Add Together with yprim currents
+ // Add Together with yprim currents
for i := 1 to Yorder do
- Curr^[i] := Csub(Curr^[i], ComplexBuffer^[i]);
+ Curr^[i] := Curr^[i] - ComplexBuffer^[i];
- end; {With}
+ end;
except
On E: Exception do
- DoErrorMsg(('GetCurrents for Element: ' + Name + '.'), E.Message,
- 'Inadequate storage allotted for circuit element.', 327);
+ DoErrorMsg(Format(_('GetCurrents for Element: %s.'), [FullName]), E.Message,
+ _('Inadequate storage allotted for circuit element.'), 327);
end;
-
end;
-
-//=============================================================================
procedure TGICLineObj.GetInjCurrents(Curr: pComplexArray);
-
begin
-
{ source injection currents given by this formula:
_ _ _ _
|Iinj1| |GICLine |
@@ -737,16 +557,12 @@ procedure TGICLineObj.GetInjCurrents(Curr: pComplexArray);
YPrim.MVMult(Curr, Vterminal);
ITerminalUpdated := FALSE;
-
end;
-//=============================================================================
-procedure TGICLineObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
+procedure TGICLineObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i, j: Integer;
c: Complex;
-
begin
inherited DumpProperties(F, Complete);
@@ -775,74 +591,25 @@ procedure TGICLineObj.DumpProperties(F: TFileStream; Complete: Boolean);
FSWriteln(F);
end;
end;
-
end;
-
-//=============================================================================
-procedure TGICLineObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- {PropertyValue Allocated in DSSObject.Create}
- PropertyValue[1] := GetBus(1);
- PropertyValue[2] := GetBus(2);
- PropertyValue[3] := '0.0';
- PropertyValue[4] := '0';
- PropertyValue[5] := '0.1';
- PropertyValue[6] := '3';
- PropertyValue[7] := '1.0';
- PropertyValue[8] := '0';
- PropertyValue[9] := '0';
-
- // PropertyValue[10] := 'zero';
- // PropertyValue[11] := 'zero';
- PropertyValue[10] := '1.0';
- PropertyValue[11] := '1.0';
- PropertyValue[12] := '33.613499';
- PropertyValue[13] := '-87.373673';
- PropertyValue[14] := '33.547885';
- PropertyValue[15] := '-86.074605';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-//=============================================================================
-function TGICLineObj.GetPropertyValue(Index: Integer): String;
-begin
- case Index of
- 1:
- Result := GetBus(1);
- 2:
- Result := GetBus(2);
- 3:
- Result := Format('%.8g', [Volts]);
- 4:
- Result := Format('%.8g', [Angle]);
- 5:
- Result := Format('%.8g', [SrcFrequency]);
- else
- Result := inherited GetPropertyValue(Index);
- end;
-end;
-
-//=============================================================================
-procedure TGICLineObj.MakePosSequence;
-
+procedure TGICLineObj.MakePosSequence();
var
- S: String;
+ Volts_new, Angle_new, R_new, X_new: Double;
begin
-
- S := 'Phases=1 ';
- S := S + Format('Voltage=%-.8g Angle=%=.5g', [Volts, Angle]);
- S := S + Format('R=%-.8g ', [R]);
- S := S + Format('X=%-.8g ', [X]);
-
- Parser.CmdString := S;
- Edit;
+ Volts_new := Volts;
+ Angle_new := Angle;
+ R_new := R;
+ X_new := X;
+ BeginEdit(True);
+ SetInteger(ord(TProp.Phases), 1);
+ SetDouble(ord(TProp.Volts), Volts_new);
+ SetDouble(ord(TProp.Angle), Angle_new);
+ SetDouble(ord(TProp.R), R_new);
+ SetDouble(ord(TProp.X), X_new);
+ EndEdit(5);
inherited;
-
end;
-end.
+end.
\ No newline at end of file
diff --git a/src/PCElements/GICsource.pas b/src/PCElements/GICsource.pas
index b72f304bc..cf8ee4ff8 100644
--- a/src/PCElements/GICsource.pas
+++ b/src/PCElements/GICsource.pas
@@ -7,10 +7,7 @@
----------------------------------------------------------
}
-{
- Develpoed from Isource and GICLine May 2018
-}
-
+// Developed from Isource and GICLine May 2018
interface
@@ -20,35 +17,44 @@ interface
PCClass,
PCElement,
ucmatrix,
- ucomplex,
+ UComplex, DSSUcomplex,
Line;
type
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+{$SCOPEDENUMS ON}
+ TGICsourceProp = (
+ INVALID = 0,
+ Volts = 1,
+ angle = 2,
+ frequency = 3,
+ phases = 4,
+ EN = 5,
+ EE = 6,
+ Lat1 = 7,
+ Lon1 = 8,
+ Lat2 = 9,
+ Lon2 = 10
+ );
+{$SCOPEDENUMS OFF}
TGICsource = class(TPCClass)
- PRIVATE
PROTECTED
- procedure DefineProperties;
- function MakeLike(const OtherSource: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TGICSourceObj = class(TPCElement)
PRIVATE
-
FphaseShift: Double;
Bus2Defined: Boolean;
// Vmag: Double;
Angle: Double;
SrcFrequency: Double;
- LineName: String;
pLineElem: TLineObj; // Pointer to associated Line
VN, VE: Double; // components of vmag
@@ -71,26 +77,21 @@ TGICSourceObj = class(TPCElement)
constructor Create(ParClass: TDSSClass; const SourceName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
function InjCurrents: Integer; OVERRIDE;
procedure GetCurrents(Curr: pComplexArray); OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
-
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
implementation
-
uses
- ParserDel,
Circuit,
DSSClassDefs,
DSSGlobals,
@@ -102,232 +103,133 @@ implementation
DSSObjectHelper,
TypInfo;
-const
- NumPropsThisClass = 10;
+type
+ TObj = TGICSourceObj;
+ TProp = TGICsourceProp;
+const
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TGICsource.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TGICsource.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'GICsource';
- DSSClassType := SOURCE + NON_PCPD_ELEM; // Don't want this in PC Element List
-
- ActiveElement := 0;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, SOURCE or NON_PCPD_ELEM, 'GICsource');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TGICsource.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TGICsource.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
- PropertyName[1] := 'Volts';
- PropertyName[2] := 'angle';
- PropertyName[3] := 'frequency';
- PropertyName[4] := 'phases';
- PropertyName[5] := 'EN';
- PropertyName[6] := 'EE';
- PropertyName[7] := 'Lat1';
- PropertyName[8] := 'Lon1';
- PropertyName[9] := 'Lat2';
- PropertyName[10] := 'Lon2';
-
- // define Property help values
- PropertyHelp[1] := 'Voltage magnitude, in volts, of the GIC voltage induced across the associated line. ' +
- 'When specified, induced voltage is assumed defined by Voltage and Angle properties. ' + CRLF + CRLF +
- 'Specify this value' + CRLF + CRLF + 'OR' + CRLF + CRLF +
- 'EN, EE, lat1, lon1, lat2, lon2. ' + CRLF + CRLF +
- 'Not both!! Last one entered will take precedence. ' +
- 'Assumed identical in each phase of the Line object.';
- PropertyHelp[2] := 'Phase angle in degrees of first phase. Default=0.0. See Voltage property';
- PropertyHelp[3] := 'Source frequency. Defaults to 0.1 Hz. So GICSource=0 at power frequency.';
- PropertyHelp[4] := 'Number of phases. Defaults to 3. All three phases are assumed in phase (zero sequence)';
- PropertyHelp[5] := 'Northward Electric field (V/km). If specified, Voltage and Angle are computed from EN, EE, lat and lon values.';
- PropertyHelp[6] := 'Eastward Electric field (V/km). If specified, Voltage and Angle are computed from EN, EE, lat and lon values.';
- PropertyHelp[7] := 'Latitude of Bus1 of the line(degrees)';
- PropertyHelp[8] := 'Longitude of Bus1 of the line (degrees)';
- PropertyHelp[9] := 'Latitude of Bus2 of the line (degrees)';
- PropertyHelp[10] := 'Longitude of Bus2 of the line (degrees)';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.Volts)] := ptruint(@obj.Volts);
+ PropertyOffset[ord(TProp.angle)] := ptruint(@obj.Angle);
+ PropertyOffset[ord(TProp.frequency)] := ptruint(@obj.SrcFrequency);
+ PropertyOffset[ord(TProp.EN)] := ptruint(@obj.ENorth);
+ PropertyOffset[ord(TProp.EE)] := ptruint(@obj.EEast);
+ PropertyOffset[ord(TProp.Lat1)] := ptruint(@obj.Lat1);
+ PropertyOffset[ord(TProp.Lon1)] := ptruint(@obj.Lon1);
+ PropertyOffset[ord(TProp.Lat2)] := ptruint(@obj.Lat2);
+ PropertyOffset[ord(TProp.Lon2)] := ptruint(@obj.Lon2);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- // Override help string
- PropertyHelp[NumPropsThisClass + 1] := 'Not used.';
- PropertyHelp[NumPropsThisClass + 2] := 'Not used.';
-
+ inherited DefineProperties;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGICsource.NewObject(const ObjName: String): Integer;
+function TGICsource.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new voltage source and add it to GICsource class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TGICSourceObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGICsource.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName,
- Param: String;
-
+procedure TGICsourceObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- // continue parsing with contents of Parser
- DSS.ActiveGICsourceObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveGICsourceObj;
-
- Result := 0;
-
- with DSS.ActiveGICsourceObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
+ case Idx of
+ 1, 2:
+ VoltsSpecified := TRUE;
+ ord(TProp.Phases):
begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 330);
- 1:
- Volts := Parser.DblValue;
- 2:
- Angle := Parser.DblValue; // Ang
- 3:
- SrcFrequency := Parser.DblValue; // freq Usually 0.1 Hz
- 4:
- begin
- Nphases := Parser.IntValue; // num phases
- FphaseShift := 0.0; // Zero Sequence
- NConds := Fnphases; // Force Reallocation of terminal info
- end;
- 5:
- ENorth := Parser.DblValue;
- 6:
- EEast := Parser.DblValue;
- 7:
- Lat1 := Parser.DblValue;
- 8:
- Lon1 := Parser.DblValue;
- 9:
- Lat2 := Parser.DblValue;
- 10:
- Lon2 := Parser.DblValue;
-
- else
- ClassEdit(DSS.ActiveGICsourceObj, ParamPointer - NumPropsThisClass);
- end;
-
- case ParamPointer of
- 1, 2:
- VoltsSpecified := TRUE;
- 5..10:
- VoltsSpecified := FALSE;
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ FphaseShift := 0.0; // Zero Sequence
+ NConds := Fnphases; // Force Reallocation of terminal info
end;
+ 5..10:
+ VoltsSpecified := FALSE;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
+function TGICsource.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
+ begin
RecalcElementData; // Updates Volts
YPrimInvalid := TRUE;
+ Exclude(Flags, Flg.EditionActive);
end;
-
+ Result := True;
end;
-//----------------------------------------------------------------------------
-function TGICsource.MakeLike(const OtherSource: String): Integer;
+procedure TGICsourceObj.MakeLike(OtherPtr: Pointer);
var
- OtherGICsource: TGICSourceObj;
- i: Integer;
-
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this line name in the present collection}
- OtherGICsource := Find(OtherSource);
- if OtherGICsource <> NIL then
- with DSS.ActiveGICsourceObj do
- begin
-
- if Fnphases <> OtherGICsource.Fnphases then
- begin
- Nphases := OtherGICsource.Fnphases;
- NConds := Fnphases; // Forces reallocation of terminal stuff
+ inherited MakeLike(OtherPtr); // set spectrum, base frequency
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
- end;
-
- Volts := OtherGICsource.Volts;
- Angle := OtherGICsource.Angle;
- SrcFrequency := OtherGICsource.SrcFrequency;
- LineName := OtherGICsource.LineName;
-
- ENorth := OtherGICsource.ENorth;
- EEast := OtherGICsource.EEast;
- Lat1 := OtherGICsource.Lat1;
- Lon1 := OtherGICsource.Lon1;
- Lat2 := OtherGICsource.Lat2;
- Lon2 := OtherGICsource.Lon2;
+ Other := TObj(OtherPtr);
+ if Fnphases <> Other.Fnphases then
+ begin
+ FNphases := Other.Fnphases;
+ NConds := Fnphases; // Forces reallocation of terminal stuff
- Bus2Defined := OtherGICsource.Bus2Defined;
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
+ end;
- ClassMakeLike(OtherGICsource); // set spectrum, base frequency
+ Volts := Other.Volts;
+ Angle := Other.Angle;
+ SrcFrequency := Other.SrcFrequency;
+ pLineElem := Other.pLineElem;
- Spectrum := ''; // Spectrum not allowed
- SpectrumObj := NIL;
+ ENorth := Other.ENorth;
+ EEast := Other.EEast;
+ Lat1 := Other.Lat1;
+ Lon1 := Other.Lon1;
+ Lat2 := Other.Lat2;
+ Lon2 := Other.Lon2;
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherGICsource.PropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in GICsource MakeLike: "' + OtherSource + '" Not Found.', 332);
+ Bus2Defined := Other.Bus2Defined;
+ SpectrumObj := NIL; // Spectrum not allowed
end;
-//----------------------------------------------------------------------------
+
constructor TGICSourceObj.Create(ParClass: TDSSClass; const SourceName: String);
begin
inherited create(ParClass);
- Name := LowerCase(SourceName);
+ Name := AnsiLowerCase(SourceName);
DSSObjType := ParClass.DSSClassType; // SOURCE + NON_PCPD_ELEM; // Don't want this in PC Element List
- LineName := Name; // GICsource name must be same as associated Line
-
LineClass := DSS.DSSClassList.Get(DSS.ClassNames.Find('Line'));
- Nphases := 3;
+ pLineElem := LineClass.Find(Name); // GICsource name must be same as associated Line
+ FNphases := 3;
Fnconds := 3;
Nterms := 2; // 4/27/2018 made a 2-terminal I source
@@ -345,30 +247,22 @@ constructor TGICSourceObj.Create(ParClass: TDSSClass; const SourceName: String);
SrcFrequency := 0.1; // this is the GIC source
FphaseShift := 0.0; // always zero sequence
Bus2Defined := FALSE;
- InitPropertyValues(0);
Yorder := Fnterms * Fnconds;
// Don't do This here RecalcElementData;
- Spectrum := ''; // Spectrum not allowed
- SpectrumObj := NIL;
-
+ SpectrumObj := NIL; // Spectrum not allowed
end;
-
-//----------------------------------------------------------------------------
destructor TGICSourceObj.Destroy;
begin
- LineName := '';
inherited Destroy;
end;
-//=============================================================================
function TGICSourceObj.Compute_VLine: Double;
var
Phi: Double;
DeltaLat, DeltaLon: Double;
-
begin
Phi := (Lat2 + Lat1) / 2.0 * (pi / 180.0); // deg to radians
DeltaLat := Lat1 - Lat2; // switched 11-20 to get pos GIC for pos ENorth
@@ -378,61 +272,49 @@ function TGICSourceObj.Compute_VLine: Double;
Result := VN + VE;
end;
-//----------------------------------------------------------------------------
procedure TGICSourceObj.RecalcElementData;
-
var
GICBus: String;
LineBus2: String;
-
begin
-
- pLineElem := LineClass.Find(LineName);
-
if pLineElem = NIL then
begin
- DoSimpleMsg('Line Object "' + LineName + '" associated with GICsource.' + Name + ' Not Found. Make sure you define it first.', 333);
+ pLineElem := LineClass.Find(Name);
+ if pLineElem = NIL then
+ begin
+ DoSimpleMsg('Line Object %s associated with %s not found. Make sure you define it first.', [Name, FullName], 333);
+ end;
end
else
begin
-
LineBus2 := pLineElem.GetBus(2);
-
- // If LineBus2 already begins with GIC, Don't insert the GIC Bis
-
+ // If LineBus2 already begins with GIC, Don't insert the GIC Bus
if CompareTextShortest('GIC_', LineBus2) <> 0 then
begin
- // Define buses -- inserting a new bus GIC_{LineName}
- GICBus := 'GIC_' + LineName;
+ // Define buses -- inserting a new bus GIC_{Name}
+ GICBus := 'GIC_' + Name;
SetBus(1, GICBus);
SetBus(2, LineBus2);
// Redefine the bus2 spec for LineElem
- Parser.CmdString := 'Bus2=' + GICBus;
- pLineElem.Edit; // invoke the Line's editor to process Parser
+ pLineElem.ParsePropertyValue(ord(TLineProp.Bus2), GICBus);
end;
Bus2Defined := TRUE;
if not VoltsSpecified then
Volts := Compute_VLine;
-
end;
Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
-
end;
-//----------------------------------------------------------------------------
procedure TGICSourceObj.CalcYPrim;
-
var
// Rs, Rm, Rzero: Double;
i: Integer;
Value: Complex;
NegValue: Complex;
-
begin
-
- // Build only YPrim Series
+ // Build only YPrim Series
if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Series = NIL) {YPrimInvalid} then
begin
if YPrim_Series <> NIL then
@@ -448,12 +330,9 @@ procedure TGICSourceObj.CalcYPrim;
YPrim.Clear;
end;
-
- {
- Assume 0.0001 ohms resistance for GIC Source
- }
+ // Assume 0.0001 ohms resistance for GIC Source
Value := Cmplx(10000.0, 0.0);
- NegValue := Cnegate(Value);
+ NegValue := -Value;
with YPrim_Series do
for i := 1 to Fnphases do
begin
@@ -463,51 +342,20 @@ procedure TGICSourceObj.CalcYPrim;
end;
YPrim.Copyfrom(Yprim_Series); // Initialize YPrim for series impedances
-(* ****************************************************************************
- {
- Compute R0 of associated line
- Average diagonals of Z matrix and off-diagonals
- Z0=Zs + 2 Zm
- }
-
- IF abs(ActiveCircuit.Solution.Frequency - SrcFrequency) < EPSILON2 THEN Begin
- Rs := 0.0; { zero the accumulators}
- Rm := 0.0;
- With pLineElem Do
- Begin
- for i := 1 to NPhases do Begin
- Rs := Rs + Z.GetElement(i,i).re;
- for j := i+1 to NPhases do
- Rm := Rm + Z.GetElement(i,j).re;
- End;
- Rs := Rs / NPhases;
- Rm := Rm / (NPhases * (NPhases-1)/2);
- Rzero := (Rs + 2.0*Rm) * Len / UnitsConvert; // Total for entire line
- Gzero := 1.0/Rzero; // zero-sequence conductance of line
-
- End;
- End;
- ***********************************************************************************
- *)
-
- {Now Account for Open Conductors}
- {For any conductor that is open, zero out row and column}
+ // Now Account for Open Conductors
+ // For any conductor that is open, zero out row and column
inherited CalcYPrim;
YPrimInvalid := FALSE;
-
end;
procedure TGICSourceObj.GetVterminalForSource;
-
var
Vmag: Double;
i: Integer;
-
begin
-
try
- // If the solution frequency not 0.1 Hz, source is shorted.
+ // If the solution frequency not 0.1 Hz, source is shorted.
with ActiveCircuit.Solution do
begin
if abs(Frequency - SrcFrequency) < EPSILON2 then
@@ -523,68 +371,52 @@ procedure TGICSourceObj.GetVterminalForSource;
end;
except
- DoSimpleMsg('Error computing current for GICsource.' + Name + '. Check specification. Aborting.', 334);
+ DoSimpleMsg('Error computing current for %s. Check specification. Aborting.', [FullName], 334);
if DSS.In_Redirect then
DSS.Redirect_Abort := TRUE;
end;
-
end;
function TGICSourceObj.InjCurrents: Integer;
-
-{Sum Currents directly into solution array}
-
+// Sum Currents directly into solution array
begin
-
GetInjCurrents(InjCurrent);
-
Result := inherited Injcurrents; // Adds into system array
-
end;
procedure TGICSourceObj.GetCurrents(Curr: pComplexArray);
-
-{Total currents into a device}
-
+// Total currents into a device
var
i: Integer;
-
begin
-
try
- with ActiveCircuit.Solution do
+ with ActiveCircuit.Solution do
begin
-
- for i := 1 to Yorder do
+ for i := 1 to Yorder do
Vterminal^[i] := NodeV^[NodeRef^[i]];
YPrim.MVMult(Curr, Vterminal); // Current from Elements in System Y
GetInjCurrents(ComplexBuffer); // Get present value of inj currents
- // Add Together with yprim currents
+ // Add Together with yprim currents
for i := 1 to Yorder do
- Curr^[i] := Csub(Curr^[i], ComplexBuffer^[i]);
-
- end; {With}
+ Curr^[i] := Curr^[i] - ComplexBuffer^[i];
+ end;
except
On E: Exception do
- DoErrorMsg(('GetCurrents for GICsource Element: ' + Name + '.'), E.Message,
- 'Inadequate storage allotted for circuit element?', 335);
+ DoErrorMsg(Format(_('GetCurrents for Element: %s.'), [FullName]), E.Message,
+ _('Inadequate storage allotted for circuit element?'), 335);
end;
-
end;
procedure TGICSourceObj.GetInjCurrents(Curr: pComplexArray);
-
- { source injection currents given by this formula:
- _ _ _ _
- |Iinj1| |GICLineVolts |
- | | = [Yprim] | |
- |Iinj2| | 0 |
- _ _ _ _
- }
-
+// source injection currents given by this formula:
+// _ _ _ _
+// |Iinj1| |GICLineVolts |
+// | | = [Yprim] | |
+// |Iinj2| | 0 |
+// _ _ _ _
begin
GetVterminalForSource; // only at 0.1 Hz
YPrim.MVMult(Curr, Vterminal);
@@ -592,75 +424,11 @@ procedure TGICSourceObj.GetInjCurrents(Curr: pComplexArray);
ITerminalUPdated := FALSE;
end;
-function TGICSourceObj.GetPropertyValue(Index: Integer): String;
-begin
- begin
- case Index of
- 1:
- Result := Format('%.8g', [Volts]);
- 2:
- Result := Format('%.8g', [Angle]);
- 3:
- Result := Format('%.8g', [SrcFrequency]);
- else
- Result := inherited GetPropertyValue(Index);
- end;
- end;
-end;
-
-procedure TGICSourceObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
+procedure TGICSourceObj.MakePosSequence();
begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
- if Complete then
- begin
- FSWriteln(F);
- FSWriteln(F);
- end;
-
-end;
-
-procedure TGICSourceObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := '0';
- PropertyValue[2] := '0';
- PropertyValue[3] := Format('%-.6g', [SrcFrequency]);
- PropertyValue[4] := '3';
- PropertyValue[5] := '1.0';
- PropertyValue[6] := '1.0';
- PropertyValue[7] := '33.613499';
- PropertyValue[8] := '-87.373673';
- PropertyValue[9] := '33.547885';
- PropertyValue[10] := '-86.074605';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-procedure TGICSourceObj.MakePosSequence;
-begin
-
if Fnphases > 1 then
- begin
- Parser.CmdString := 'phases=1';
- Edit;
- end;
+ SetInteger(ord(TProp.Phases), 1);
inherited;
-
end;
-
-initialization
-
-end.
+end.
\ No newline at end of file
diff --git a/src/PCElements/GenUserModel.pas b/src/PCElements/GenUserModel.pas
index 73ed8efe3..8c6a44cda 100644
--- a/src/PCElements/GenUserModel.pas
+++ b/src/PCElements/GenUserModel.pas
@@ -1,6 +1,5 @@
unit GenUserModel;
-{$M+}
{
----------------------------------------------------------
Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
@@ -16,13 +15,9 @@
interface
-USES GeneratorVars, Dynamics, DSSCallBackRoutines, ucomplex, Arraydef, DSSClass;
+USES Dynamics, DSSCallBackRoutines, UComplex, DSSUcomplex, Arraydef, DSSClass;
TYPE
-
-
-
-
TGenUserModel = class(TObject)
private
FHandle: NativeUInt; // Handle to DLL containing user model
@@ -31,9 +26,8 @@ TGenUserModel = class(TObject)
Fname: String; // Name of the DLL file containing user model
FuncError:Boolean;
-
{These functions should only be called by the object itself}
- FNew: Function(Var GenVars:TGeneratorVars; Var DynaData:TDynamicsRec; Var CallBacks:TDSSCallBacks): Integer; Stdcall;// Make a new instance
+ FNew: Function(GenVars: Pointer; Var DynaData:TDynamicsRec; Var CallBacks:TDSSCallBacks): Integer; Stdcall;// Make a new instance
FDelete: Procedure(var x:Integer); Stdcall; // deletes specified instance
FSelect: Function (var x:Integer):Integer; Stdcall; // Select active instance
@@ -54,7 +48,7 @@ TGenUserModel = class(TObject)
FIntegrate: Procedure; stdcall; // Integrates any state vars
FUpdateModel: Procedure; Stdcall; // Called when props of generator updated
- FActiveGeneratorVars:pTGeneratorVars;
+ FActiveGeneratorVars: Pointer;
{Save and restore data}
FSave: Procedure; Stdcall;
@@ -77,17 +71,15 @@ TGenUserModel = class(TObject)
Procedure Select;
Procedure Integrate;
- constructor Create(dssContext: TDSSContext; ActiveGeneratorVars:pTGeneratorVars);
+ constructor Create(dssContext: TDSSContext; ActiveGeneratorVars: Pointer);
destructor Destroy; override;
- published
-
end;
implementation
-Uses Generator, DSSGlobals, {$IFDEF FPC}dynlibs{$ELSE}Windows{$ENDIF}, Sysutils,
+Uses Generator, DSSGlobals, dynlibs, Sysutils,
DSSHelper;
{ TGenUserModel }
@@ -96,13 +88,13 @@ function TGenUserModel.CheckFuncError(Addr: Pointer; FuncName: String): Pointer
begin
If Addr=nil then
Begin
- DoSimpleMsg(DSS, 'Generator User Model Does Not Have Required Function: ' + FuncName, 569);
+ DoSimpleMsg(DSS, 'Generator User Model Does Not Have Required Function: %s', [FuncName], 569);
FuncError := True;
End;
Result := Addr;
end;
-constructor TGenUserModel.Create(dssContext: TDSSContext; ActiveGeneratorVars:pTGeneratorVars);
+constructor TGenUserModel.Create(dssContext: TDSSContext; ActiveGeneratorVars: Pointer);
begin
DSS := dssContext;
FID := 0;
@@ -110,19 +102,16 @@ constructor TGenUserModel.Create(dssContext: TDSSContext; ActiveGeneratorVars:pT
FName := '';
FActiveGeneratorVars := ActiveGeneratorVars;
-
end;
destructor TGenUserModel.Destroy;
begin
-
If FID <> 0 Then
Begin
FDelete(FID); // Clean up all memory associated with this instance
FreeLibrary(FHandle);
End;
inherited;
-
end;
function TGenUserModel.Get_Exists: Boolean;
@@ -153,10 +142,7 @@ procedure TGenUserModel.Set_Edit(const Value: String);
end;
procedure TGenUserModel.Set_Name(const Value:String);
-
-
begin
-
{If Model already points to something, then free it}
IF FHandle <> 0 Then
@@ -181,7 +167,7 @@ procedure TGenUserModel.Set_Name(const Value:String);
End;
If FHandle = 0 Then
- DoSimpleMsg(DSS, 'Generator User Model ' + Value + ' Not Loaded. DSS Directory = '+DSSDirectory, 570)
+ DoSimpleMsg(DSS, 'Generator User Model %s Not Loaded. DSS Directory = %s', [Value, DSSDirectory], 570)
Else
Begin
FName := Value;
@@ -211,11 +197,9 @@ procedure TGenUserModel.Set_Name(const Value:String);
FName := '';
end
Else Begin
- FID := FNew(FActiveGeneratorVars^, DSS.ActiveCircuit.Solution.Dynavars, CallBackRoutines); // Create new instance of user model
+ FID := FNew(FActiveGeneratorVars, DSS.ActiveCircuit.Solution.Dynavars, CallBackRoutines); // Create new instance of user model
End;;
End;
end;
-
-
end.
diff --git a/src/PCElements/GeneratorVars.pas b/src/PCElements/GeneratorVars.pas
deleted file mode 100644
index 2c475100b..000000000
--- a/src/PCElements/GeneratorVars.pas
+++ /dev/null
@@ -1,62 +0,0 @@
-unit GeneratorVars;
-
-{
- ----------------------------------------------------------
- Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
- All rights reserved.
- ----------------------------------------------------------
-
- Definition of Generator Public Data Record for passing to DLLs and other object
-}
-
-interface
-
-uses
- UComplex;
-
-type
- pTGeneratorVars = ^TGeneratorVars;
-
- {Generator public data/state variable structure}
- TGeneratorVars = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}packed{$ENDIF} record
-
- Theta, {Direct-Axis voltage magnitude & angle}
- Pshaft,
- Speed,
- w0, {present Shaft Power and relative Speed, rad/sec, difference from Synchronous speed, w0}
- {actual speed = Speed + w0}
- Hmass, {Per unit mass constant}
- Mmass, {Mass constant actual values (Joule-sec/rad}
- D, Dpu, {Actual and per unit damping factors}
- kVArating,
- kVGeneratorBase,
- Xd, Xdp, Xdpp, {machine Reactances, ohms}
- puXd, puXdp, puXdpp, {machine Reactances, per unit}
- dTheta,
- dSpeed, {Derivatives of Theta and Speed}
- ThetaHistory,
- SpeedHistory, {history variables for integration}
- Pnominalperphase,
- Qnominalperphase {Target P and Q for power flow solution, watts, vars}: Double; { All Doubles }
-
- {32-bit integers}
- NumPhases, {Number of phases}
- NumConductors, {Total Number of conductors (wye-connected will have 4)}
- Conn: Integer; // 0 = wye; 1 = Delta
-
- { Revisons (additions) to structure ...
- Later additions are appended to end of the structure so that
- previously compiled DLLs do not break
- }
-
- VthevMag: Double; {Thevinen equivalent voltage for dynamic model}
- VThevHarm: Double; {Thevinen equivalent voltage mag reference for Harmonic model}
- ThetaHarm: Double; {Thevinen equivalent voltage angle reference for Harmonic model}
- VTarget: Double; // Target voltage for generator with voltage control
- Zthev: Complex;
- XRdp: Double; // Assumed X/R for Xd'
- end;
-
-implementation
-
-end.
diff --git a/src/PCElements/Generic5OrderMach.pas b/src/PCElements/Generic5OrderMach.pas
deleted file mode 100644
index cccc359bf..000000000
--- a/src/PCElements/Generic5OrderMach.pas
+++ /dev/null
@@ -1,4393 +0,0 @@
-unit Generic5OrderMach;
-
-{ Change Log
-
- November 3, 2017
-
- Created by
- Darhey Xu
-
-}
-
-interface
-
-uses
- Classes,
- DSSClass, // Base class for most DSS objects
- PCClass, // Base class for collection manager for PC elements
- PCElement, // Base class for PC Elements
- ucmatrix, // Unit for managing complex matrice (for Yprim, etc)
- ucomplex, // Complex math functions, type definitions
- ArrayDef, // definitions of basic DSS arrays
-
- // common modules used in PC elements
- LoadShape, // class for supporting/representing loadshapes
- GrowthShape, // Class for holding growth shapes
- Spectrum, // Definitions for harmonic spectra
- Dynamics,
- GeneratorVars; // for elements that interact with dynamics variables
-
-type
-
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
-
-{ Collection manager for this class of element }
- TGeneric5 = class(TPCClass) { Notes Andres: -- definition of the class -- }
- PRIVATE
-
- {These private functions are generally helper functions for Edit procedure}
-
- { A typical function }
- procedure SetNcondsForConnection;
-
- PROTECTED
- procedure DefineProperties; // Define the property names and help strings
- function MakeLike(const OtherIndMach012Name: String): Integer; OVERRIDE; // copy properties of another similar object
-
- PUBLIC
-
- constructor Create(dssContext: TDSSContext);
- destructor Destroy; OVERRIDE;
-
- function Edit: Integer; OVERRIDE; // Definition of the main property editing function
-
- function NewObject(const ObjName: String): Integer; OVERRIDE; // This function is called by the DSS New command
-
- {any public functions that might be called from other elements}
-
- end;
-
-{ Class definition for this class of element}
- TSymCompArray5 = array[0..2] of Complex;
- //pTDynamicsRec = ^TDynamicsRec;
- //pTGeneratorVars = ^TGeneratorVars;
-
- TGeneric5Obj = class(TPCElement)
- PRIVATE
-
- {Private variables of this class}
- Connection: Integer; {0 = line-neutral; 1=Delta}
- Yeq: Complex; // Y at nominal voltage
-
- puRs, puXs, puRr, puXr, puXm,
- //S1, // Pos seq slip
- //S2,
- MaxSlip, // limit for slip to prevent solution blowing up
- //dSdP, // for power flow
-
- {Dynamics variables}
- Xopen,
- Xp,
- T0p // Rotor time constant
- : Double;
- //NOrder, //NOrder = 5 is defined as constant
- NumOrderX, // system order
- NumOrderY: Integer; // system output Y order
- {X,V}
- { X_var:pComplexArray;
- Y_out_var :pComplexArray;
- V_in_var :pComplexArray;
- {A, B Matrix }
- { Amm:pComplexArray;
- Bmn:pComplexArray;
- Cnm:pComplexArray;
- Dnn:pComplexArray;
- {}
- X_var: pdoubleArray;
- dX_vardt: pdoubleArray;
- X_varn: pdoubleArray;//for tropdize integrate
- dX_vardtn: pdoubleArray;
- Y_out_var: pdoubleArray;
- V_in_var: pdoubleArray;
- pV_f_CC: pdoublearray;
- CC_Switch: Boolean;
- //Cluster_num : integer;
- {A, B Matrix }
- Amm: pdoubleArray;
- Bmn: pdoubleArray;
- Cnm: pdoubleArray;
- Dnn: pdoubleArray;
- {}
-
- InDynamics: Boolean;
- Zs, Zm, Zr: Complex;
- Is1, Ir1, V1, // Keep the last computed voltages and currents
- Is2, Ir2, V2: Complex;
-
- {Complex variables for dynamics}
- //E1, E1n, dE1dt, dE1dtn,
- //E2, E2n, dE2dt, dE2dtn,
- Zsp: Complex;
-
- {}
- Id, Iq: Double; //Id related to P ; Iq related to Q
- flag_dyna_Id_chg: Boolean;
-
- dIddt, dIqdt: Double;
- Idn, Iqn, dIddtn, dIqdtn: Double; //save last time step for integration.
- {the input for control purpose}
- kcd, kcq, kcq_drp2: Double; //the control gain in vi1, vi2
- Volt_Trhd: Double;
- droop: Integer;//droop type: 2, Q = kcq_drp2 * (1-v_dg). others: integral droop with kcq
- //flag_drp2 : integer; //if it is 1, drp2
- kqi: Double; //control gain for Q_ref
- vi1, vi2: Double; //the input of the control
- vi1n, vi2n: Double; //the input of the control
- dvi1dt, dvi2dt, dvi1dtn, dvi2dtn: Double;
- Id_ref, Iq_ref: Double; // The pursued value of Id related to P ; Iq related to Q
- {}
- P_ref, Q_ref, V_ref: Double;//Power and voltage goal of the machine
- DPX: Double;
- ctrl_mode: Integer; //ctrl_mode 0-local droop V_ref = V_DG_0, P_ref = P_DG_0
- {}
- P_DG, Q_DG: Double; //power of all phases totally in one
- V_DG: Double;// the voltage magetitude of current bus
- Theta_DG: Double; //the voltage angel of DG bus to slack
- //Cluster_Num : integer; //the cluster define --move to PCElement
- //Num_in_Cluster : integer; // node num in cluster; --move to PCElement
- QV_flag: Integer; // 0 Q_ref; 1 V_ref
- //QV_flag_0 : integer;
- //QV_switch: integer; //if Q hits limits, PV to PQ, the QV_switch:= 1; each time Edit function runs, check this and set QV_flag back to user set.
- {--for 3 phases--}
- //power, voltage, angle,
- P_DG1, P_DG2, P_dg3,
- Q_DG1, Q_dg2, Q_dg3,
- V_DG1, V_DG2, V_DG3,
- V_theta1, V_theta2, V_theta3: Double;//operation values
- Id1, Iq1, Id2, Iq2, Id3, Iq3: Double; //currents
- //set values
- P_ref1, P_ref2, P_ref3,
- Q_ref1, Q_ref2, Q_ref3,
- V_ref1, V_ref2, V_ref3: Double;// set values from outside
- {------Max Check-------}
- //SMax; // MachineData.kVArating; // 'g1.kva=100'
- PMax, // Activity power output limit
- PMax_phase, //limit per phase
- PMin, //(0, default) // 'g1.pmax=100'
- Pmin_phase, //
- Qmax, //Reactive power output limit
- Qmax_phase,
- Qmin, //(-Qmax, default)
- Qmin_phase: Double; //
- IdMax_phase,
- IqMax_phase: Double;//phase current limit
- PQpriority: Integer; //Active and reactive power control mode, control s
- //'g1.pqvflag=0 Q, 1 P;
- ///////////////////
- //Gradient ; move to public
- //Alpha, // Alpha := Q_DG/Qmax;
- //dAlpha,
- //Gradient: double;
- {----------------------}
- //equivalent frequency
- freq: Double;
- z_dfs_plot: Double;
- {----------------------}
- FirstIteration, FixedSlip: Boolean;
-
- //var_Remembered :Double; //Q remembered of last calc
- DQDV: Double; //for P_ref V_ref model
-
- RandomMult: Double;
- //Generic5SolutionCount : Integer;
- Generic5SwitchOpen: Boolean;
-
- // Debugging
- TraceFile: TFileStream;
- DebugTrace: Boolean;
-
- MachineData: TGeneratorVars; // Use generator variable structure
-
- // Andres: NEW variables from generator
- MachineON: Boolean;
- ShapeFactor: Complex;
-
- ShapeIsActual: Boolean;
- // Andres: end NEW variables from generator
-
- VBase: Double;
- kWBase: Double;
- {---deal with -Update_Pmax_by_Ftrs-}
- Pmpp,//Pmpp, default value is 1.0;
- Pbias, //Pbias, default value is 0.0;
- Pfctr1,//factors, default value all are 1.0;
- Pfctr2,
- Pfctr3,
- Pfctr4,
- Pfctr5,
- Pfctr6: Double;
-
- {----------------}
- //Gradient ; public
- Alpha, // Alpha := Q_DG/Qmax; //pos ctrl
- dAlpha,
- Gradient: Double;
- Alpha1, Alpha2, Alpha3, // Alpha := Q_DG/Qmax; //pos ctrl
- dAlpha1, dAlpha2, dAlpha3,
- Gradient1, Gradient2, Gradient3: Double;
- AlphaP, AlphaP1, AlphaP2, AlphaP3: Double;// for active P control
- GradientP, GradientP1, GradientP2, GradientP3: Double;
-// Procedure InterpretOption(s:String);
-
- //procedure set_Localslip(const Value: Double);
-
- //Procedure Get_PFlowModelCurrent(Const V:Complex; Const S:Double; var Istator, Irotor:Complex);
- procedure Get_DynamicModelCurrent;
- //procedure Set_Slip(const Value: Double);
-
- function GetRotorLosses: Double;
- function GetStatorLosses: Double;
- //Function Compute_dSdP:Double;
- procedure Randomize(Opt: Integer);
- procedure InitModel(V012, I012: TSymCompArray5);
- procedure InitModelVIabc;
-
- procedure CalcYPrimMatrix(Ymatrix: TcMatrix);
- procedure CalcGeneric5ModelContribution;
- procedure CalcInjCurrentArray;
-
- procedure DoGeneric5Model;
- procedure CalcModel(V, I: pComplexArray);
-
-
- // Andres: NEW procedures from generator
- procedure CalcDailyMult(Hr: Double);
- procedure CalcYearlyMult(Hr: Double);
- procedure CalcDutyMult(Hr: Double);
- // Andres: NEW procedures from generator
-
- procedure InitTraceFile;
- procedure WriteTraceRecord;
- function Get_PresentkV: Double;
- procedure Set_PresentkV(const Value: Double);
-
- procedure SetPowerkW(const PkW: Double);
- {}
- procedure update_controlinput;
- procedure update_pV_f_CC;// update cooperate control part, pV_f_CC
- procedure update_pV_f_CC_M2;// update cooperate control part, pV_f_CC
- procedure update_system_abcd;
- procedure Set_P_Ref(PrefkW: Double);
- procedure Set_Q_Ref(QrefkVAr: Double);
- procedure Set_V_Ref(VrefkV: Double);
- procedure Update_kWbase_by_Fctrs;
- procedure Update_PQlimits; //real time limits check
- // can also be used in power flow and simulation
- procedure InfoPublish;
- //Procedure Get_Bii;
- procedure CalGradient;
-// Procedure CalcDQDV;
- PROTECTED
-
- {A couple of virtual procedures you can override}
- procedure Set_ConductorClosed(Index: Integer; Value: Boolean); OVERRIDE;
- procedure GetTerminalCurrents(Curr: pComplexArray); OVERRIDE;
-
- procedure DoDynamicMode;
- procedure DoHarmonicMode;
-
- PUBLIC
-
- {Variables and functions accessed by DSS and other objects}
-
- // Andres: new variables from generator
- DailyDispShape: String; // Daily (24 HR) Generator shape
- DailyDispShapeObj: TLoadShapeObj; // Daily Generator Shape for this load
- DutyShapeObj: TLoadShapeObj; // Shape for this generator
- DutyShape: String; //
- YearlyShape: String; // ='fixed' means no variation on all the time
- YearlyShapeObj: TLoadShapeObj; // Shape for this Generator
- // Andres: New variables from generator
-
- constructor Create(ParClass: TDSSClass; const Generic5ObjName: String);
- destructor Destroy; OVERRIDE;
-
- procedure RecalcElementData; OVERRIDE; // Generally called after Edit is complete to recompute variables
- procedure CalcYPrim; OVERRIDE; // Calculate Primitive Y matrix
- {-----------}
-// Procedure CalcABMatrix;
- {-----------}
- procedure Integrate;
- procedure IntegrateABCD;
- procedure CalcDynamic(var V012, I012: TSymCompArray5);
- procedure CalcPFlow(var V012, I012: TSymCompArray5);
- // for abc phases: the below 2
- procedure CalcDynamicVIabc(var Vabc, Iabc: pComplexArray);
- procedure CalcPFlowVIabc(var Vabc, Iabc: pComplexArray);
-
- procedure SetNominalPower;
- procedure UpdateAlpha_qi; // \alpha_qi := Q_DG/Qmax;
-
- function InjCurrents: Integer; OVERRIDE;
- procedure GetInjCurrents(Curr: pComplexArray); OVERRIDE;
-
- // State variable management functions, if any
- // You can omit these if your PC element model is not using these
- // Default behavior is to basically do nothing
- function NumVariables: Integer; OVERRIDE;
- procedure GetAllVariables(States: pDoubleArray); OVERRIDE;
- function Get_Variable(i: Integer): Double; OVERRIDE;
- procedure Set_Variable(i: Integer; Value: Double); OVERRIDE;
- function VariableName(i: Integer): String; OVERRIDE;
-
- // Support for Dynamics Mode
- procedure InitStateVars; OVERRIDE;
- procedure IntegrateStates; OVERRIDE;
-
- // Support for Harmonics Mode
- procedure InitHarmonics; OVERRIDE;
-
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model, if possible
-
- // Functions required for managing values of properties
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
-
- // Property LocalSlip:Double read S1 write set_Localslip;
- //Property Slip:Double Write Set_Slip;
- property PresentkV: Double READ Get_PresentkV WRITE Set_PresentkV;
-
- end;
-
-
-implementation
-
-{Typical Uses Clause -- not all may not be needed}
-uses
- ParserDel, // DSS parser
- DSSClassDefs, // Where class is instantiated
- DSSGlobals, // Global DSS variables
- Circuit, // If access to circuit variables is needed
- Command, // DSS command and property support module
- Sysutils, // Delphi misc utility functions
- Math, // Delphi Math functions
- MathUtil, // DSS Math utilities
- Utilities, // DSS misc utility functions
- DSSHelper,
- DSSObjectHelper,
- TypInfo;
-
-const
- NumPropsThisClass = 48;//44;//24;//23; // Set this constant to the actual number of properties you define add grpnum
- NumGeneric5Variables = 36;//33;//25;//24;
- nOrder = 6;
-
-var // Define any useful module vars here, for example:
- cBuffer: array[1..24] of Complex; // Temp buffer for complex math calcs; allows up to 24-phase models.
- CDOUBLEONE: Complex; // 1 + j1 (see Initialization section below)
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TGeneric5.Create(dssContext: TDSSContext); // Creates main collection handler for all IndMach012 objects
-begin
- inherited Create(dssContext); // make the base class and init DSSClassType
-
- // Specify class name and bit mask ID for this class type
- // IndMach012_ELEMENT must be defined in DSSClassDefs as nn*8
- // First 3 bits are used for base class type (DSSClassType)
- Class_Name := 'Generic5';
- DSSClassType := DSSClassType + Generic5OrderMach_ELEMENT;
-
- ActiveElement := 0; // no active elements yet; init to 0
-
- {Initialize any other special variables here}
-
- DefineProperties; // This is where the properties for this class are defined
-
- // Use the Command processor to manage property names
- // PropertyName is an array of String defined in DefineProperties
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
-
- Generic5Class := Self;
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-destructor TGeneric5.Destroy;
-
-begin
-
- // ElementList and CommandList freed in inherited destroy
- inherited Destroy;
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TGeneric5.DefineProperties;
-
-// This is where the properties are defined, assigned names, indexes, and help strings
-// The Help strings will automatically show up when the Help is invoked
-
-begin
-
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays; {see DSSClass}
-
- // Refer to other classes for alternative methods of assigning properties
- // This example uses the AddProperty function to assign Name, Index, and Help string
- // in one statement.
-
- // First argument is string name of the property
- // Second argument is the index for the CASE statement
- // Third argument is help string
-
- // DSS properties are accessed in sequence if the property name is not explicitly specified.
- // The advantage of using the AddProperty function is that you may change the sequence simply
- // by shuffling the order of the definitions and you do not have to change the index in the CASE
- // statement in the EDIT function
-
-
- PropertyName[1] := 'phases';
- PropertyName[2] := 'bus1';
- PropertyName[3] := 'kv';
- PropertyName[4] := 'kW';
- PropertyName[5] := 'pf';
- PropertyName[6] := 'conn';
- PropertyName[7] := 'kVA';
- PropertyName[8] := 'H';
- PropertyName[9] := 'D';
- PropertyName[10] := 'P_ref1kW';
- PropertyName[11] := 'P_ref2kW';
- PropertyName[12] := 'P_ref3kW';
- PropertyName[13] := 'V_ref1kVLN';
- PropertyName[14] := 'V_ref2kVLN';
- PropertyName[15] := 'V_ref3kVLN';
- PropertyName[16] := 'MaxSlip';
- PropertyName[17] := 'SlipOption';
- PropertyName[18] := 'Yearly';
- PropertyName[19] := 'Daily';
- PropertyName[20] := 'Duty';
- PropertyName[21] := 'Debugtrace';
- PropertyName[22] := 'P_refkW';
- PropertyName[25] := 'V_refkVLN';
- PropertyName[23] := 'Q_refkVAr';
- PropertyName[24] := 'Cluster_num';
- PropertyName[26] := 'ctrl_mode';
- ///////////////////////////////////////////
- /// contrl mode
- /// ctrl_mode =0; phases = 3; // pos avg control---p_ref, V_ref, Q_ref
- /// ctrl_mode =1; phases = 1; bus1 = 452.1; ---p_ref1, V_ref1, Q_ref1
- /// ctrl_mode =2; phases = 1; bus1 = 452.2; ---p_ref2, V_ref2, Q_ref2
- /// ctrl_mode =3; phases = 1; bus1 = 452.3; ---p_ref3, V_ref3, Q_ref3
- /// ctrl_mode =4; phases = 3; bus1 = 452.2; ---p_ref1,2,3, V_ref1,2,3, Q_ref1,2,3
- PropertyName[27] := 'QV_flag';
- PropertyName[28] := 'kcd';//Idi control gain
- PropertyName[29] := 'kcq';//Iqi control gain to delta V
- PropertyName[30] := 'kqi';//Iqi control gain to delta Q
- PropertyName[31] := 'Q_ref1kVAr';
- PropertyName[32] := 'Q_ref2kVAr';
- PropertyName[33] := 'Q_ref3kVAr';
- PropertyName[34] := 'PmaxkW'; //
- PropertyName[35] := 'PminkW';
- PropertyName[36] := 'PQpriority';
- PropertyName[37] := 'PmppkW';
- PropertyName[38] := 'Pfctr1';
- PropertyName[39] := 'Pfctr2';
- PropertyName[40] := 'Pfctr3';
- PropertyName[41] := 'Pfctr4';
- PropertyName[42] := 'Pfctr5';
- PropertyName[43] := 'Pfctr6';
- PropertyName[44] := 'PbiaskW';
- PropertyName[45] := 'CC_Switch';
- PropertyName[46] := 'kcq_drp2';
- PropertyName[47] := 'Volt_Trhd';
- PropertyName[48] := 'droop';
-
- //PropertyName[46] := 'Num_in_Cluster. Num_in_Cluster = 1~33';
-
- PropertyHelp[1] := 'Number of Phases, this Induction Machine. ';
- PropertyHelp[2] := 'Bus to which the Induction Machine is connected. May include specific node specification.';
- PropertyHelp[3] := 'Nominal rated (1.0 per unit) voltage, kV. For 2- and 3-phase machines, specify phase-phase kV. ' +
- 'Otherwise, specify actual kV across each branch of the machine. ' +
- 'If wye (star), specify phase-neutral kV. ' +
- 'If delta or phase-phase connected, specify phase-phase kV.'; // line-neutral voltage// base voltage
- PropertyHelp[4] := 'Shaft Power, kW, for the Induction Machine. Output limit of a DG';//A positive value denotes power for a //load. ';//+CRLF+
- //'Negative value denotes an induction generator. ';
- PropertyHelp[5] := '[Read Only] Present power factor for the machine. ';
- PropertyHelp[6] := 'Connection of stator: Delta or Wye. Default is Delta.';
- PropertyHelp[7] := 'Rated kVA for the machine.';
- PropertyHelp[8] := 'Per unit mass constant of the machine. MW-sec/MVA. Default is 1.0.';
- PropertyHelp[9] := 'Damping constant. Usual range is 0 to 4. Default is 1.0. Adjust to get damping in Dynamics mode,';
- PropertyHelp[10] := 'P_ref1kW = 10, goes to P_ref1, unit kW, 1st phase set power';
- PropertyHelp[11] := 'P_ref2kW = 10, goes to P_ref2, unit kW, 2nd phase set power';
- PropertyHelp[12] := 'P_ref3kW = 10, goes to P_ref3, unit kW, 3rd phase set power';
- PropertyHelp[13] := 'V_ref1kVLN = 2.16, 1st phase set V, (Unit kV, L-N value): V mode will work if QV_flag =1(by default) V_ref is set which is prior to Q_ref ';
- PropertyHelp[14] := 'V_ref2kVLN = 2.16, 2nd phase set V, (Unit kV, L-N value): V mode will work if QV_flag =1(by default) V_ref is set which is prior to Q_ref ';
- PropertyHelp[15] := 'V_ref3kVLN = 2.16, 3rd phase set V, (Unit kV, L-N value): V mode will work if QV_flag =1(by default) V_ref is set which is prior to Q_ref ';
- PropertyHelp[16] := 'Max slip value to allow. Default is 0.1. Set this before setting slip.';
- PropertyHelp[17] := 'Option for slip model. One of {fixedslip | variableslip* }';
- PropertyHelp[18] := 'LOADSHAPE object to use for yearly simulations. Must be previously defined ' +
- 'as a Loadshape object. Is set to the Daily load shape ' +
- ' when Daily is defined. The daily load shape is repeated in this case. ' +
- 'Set Status=Fixed to ignore Loadshape designation. ' +
- 'Set to NONE to reset to no loadahape. ' +
- 'The default is no variation.';
- PropertyHelp[19] := 'LOADSHAPE object to use for daily simulations. Must be previously defined ' +
- 'as a Loadshape object of 24 hrs, typically. ' +
- 'Set Status=Fixed to ignore Loadshape designation. ' +
- 'Set to NONE to reset to no loadahape. ' +
- 'Default is no variation (constant) if not defined. ' +
- 'Side effect: Sets Yearly load shape if not already defined.';
- PropertyHelp[20] := 'LOADSHAPE object to use for duty cycle simulations. Must be previously defined ' +
- 'as a Loadshape object. Typically would have time intervals less than 1 hr. ' +
- 'Designate the number of points to solve using the Set Number=xxxx command. ' +
- 'If there are fewer points in the actual shape, the shape is assumed to repeat.' +
- 'Set to NONE to reset to no loadahape. ' +
- 'Set Status=Fixed to ignore Loadshape designation. ' +
- ' Defaults to Daily curve If not specified.';
- PropertyHelp[21] := '[Yes | No*] Write DebugTrace file.';
- PropertyHelp[22] := 'P_refkW = 10, goes to P_ref. Ref P Value (kW). P_ref has prority to kW which is nomimal value. (Incide variable P_ref is W)';
- PropertyHelp[25] := 'V_refkVLN = 2.16, pos sequence set V. V_ref (Unit kV, L-N value): V mode will work if QV_flag =1(by default) V_ref is set which is prior to Q_ref';
- PropertyHelp[23] := 'Q_refkVAr=10. Unit Qvar. Ref Q kVAr Value: work only when V_ref is not set';
- PropertyHelp[24] := 'Cluster_num: has to be coincident with Fmonitor attached. Default value is 0';
- { add properties here }
- PropertyHelp[26] := 'ctrl mode: /// contrl mode ' +
- ' /// ctrl_mode =0; phases = 3; // pos avg control---p_ref, V_ref, Q_ref \n ' + CRLF +
- ' /// ctrl_mode =1; phases = 1; bus1 = 452.1; ---p_ref1, V_ref1, Q_ref1 \n' + CRLF +
- '/// ctrl_mode =2; phases = 1; bus1 = 452.2; ---p_ref2, V_ref2, Q_ref2 \n' + CRLF +
- '/// ctrl_mode =3; phases = 1; bus1 = 452.3; ---p_ref3, V_ref3, Q_ref3 \n' + CRLF +
- '/// ctrl_mode =4; phases = 3; bus1 = 452.2; ---p_ref1,2,3, V_ref1,2,3, Q_ref1,2,3';
- PropertyHelp[27] := 'QV_flag : 0-Q_ref mode; 1- V_ref mode';
- PropertyHelp[28] := 'kcd: Idi control gain';
- PropertyHelp[29] := 'kcq: Iqi control gain to delta V';
- PropertyHelp[30] := 'kqi: Iqi control gain to delta Q';
- PropertyHelp[31] := 'Q_ref1kVAr=10. Unit Qvar. Ref Q kVAr Value: work only when V_ref is not set';
- PropertyHelp[32] := 'Q_ref2kVAr=10. Unit Qvar. Ref Q kVAr Value: work only when V_ref is not set';
- PropertyHelp[33] := 'Q_ref3kVAr=10. Unit Qvar. Ref Q kVAr Value: work only when V_ref is not set';
- PropertyHelp[34] := 'PmaxkW = 100, goes to Pmax, unit kW, set max active power output; Operation limit of active power for DG' + CRLF + ' Pmax should be less than or equal to kW';
- PropertyHelp[35] := 'PminkW = 10, goes to Pmin, unit kW; Operation limit of active power for DG';
- PropertyHelp[36] := 'PQpriority, goes to PQpriority, define how to set Qmax. 0: Q,1: P ';
- PropertyHelp[37] := 'PmppkW = 100, goes to Pmpp, unit kW, input Pmpp to calculate kW;' + CRLF + ' kW := (Pmpp + Pbias)*Pfctr1*Pfctr2*Pfctr3*Pfctr4*Pfctr5*Pfctr6;' + CRLF + 'Pbias = 0 by default, Pfctr*=1 by default; These properties will overwrite kW.';
- PropertyHelp[38] := 'Pfctr1 = 0.16, see PmppkW';
- PropertyHelp[39] := 'Pfctr2 = 1, 1 by default, see PmppkW';
- PropertyHelp[40] := 'Pfctr3 = 1, 1 by default, see PmppkW';
- PropertyHelp[41] := 'Pfctr4= 1, 1 by default, see PmppkW';
- PropertyHelp[42] := 'Pfctr5 =1, 1 by default, see PmppkW';
- PropertyHelp[43] := 'Pfctr6 = 1, 1 by default, see PmppkW';
- PropertyHelp[44] := 'Pbias = -0.1, 0 by default, see PmppkW';
- PropertyHelp[45] := 'CC_Switch: default value is false.' + CRLF + 'CC_Switch = true --cooperate control on' + CRLF + 'CC_Switch = false -- cooperate control off';
- PropertyHelp[46] := 'kcq_drp2. the droop gain: 0.0~0.1';
- PropertyHelp[47] := 'Volt_Trhd. 0.~0.05. 0 means v has to follow v_ref';
- PropertyHelp[48] := 'droop type: integer: 2- Q = kcq_drp2 * (1-v_dg). others: integral droop with kcq.';
- // Finally, we have to pick up any properties that were inherited
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- // You can optionally override default help string of an inherited property, for example
- PropertyHelp[NumPropsThisClass + 1] := 'Name of harmonic voltage or current spectrum for this IndMach012. ' +
- 'Voltage behind Xd" for machine - default. Current injection for inverter. ' +
- 'Default value is "default", which is defined when the DSS starts.';
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGeneric5.NewObject(const ObjName: String): Integer;
-
-// This function is called by the DSS whenever a New IndMach012... command is encountered
-
-begin
- // Make a new IndMach012 and add it to IndMach012 class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TGeneric5Obj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TGeneric5.SetNcondsForConnection;
-
-// This is a typical helper function found in many DSS circuit element class
-// for defining the number of conductors per terminal (Nconds) based on Y or delta connection
-
-begin
-
- with ActiveGeneric5Obj do
- begin
- case Connection of
- 0:
- NConds := Fnphases; // Neutral is not connected for induction machine
- 1:
- case Fnphases of // Delta connection
- 1, 2:
- NConds := Fnphases + 1; // L-L and Open-delta
- else
- NConds := Fnphases; // no neutral for this connection
- end;
- end;
- end;
-
-end;
-
-
-//- - - - - - - - - - - - - MAIN EDIT FUNCTION - - - - - - - - - - - - - - -
-//----------------------------------------------------------------------------
-function TGeneric5.Edit: Integer;
-//----------------------------------------------------------------------------
-
-// This function is the heart of the property managment for this class
-
-var // Define some local vars for handling parser results
-
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
-// The Edit function starts where the Parser is presently pointing and
-// manages the parsing of the rest of the command line in the parser.
-
-// The DSS executive processes the command verb on the front of the line and
-// then passes control to the appropriate Edit function
-
-begin
-
- // set the present element active
- // and continue parsing with contents of Parser
- ActiveGeneric5Obj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := ActiveGeneric5Obj;
- Result := 0;
-
- with ActiveGeneric5Obj do
- begin
- // peel off the next token on the edit line
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
-
- while Length(Param) > 0 do
- begin
- // Find the index for the CASE statement
- // If property is not named, just increment the index to the next property
- if (Length(ParamName) = 0) then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- // Update the PropertyValy for this property
- // Actual index is mapped via PropertyIdxMap array for this class
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[PropertyIdxMap[ParamPointer]] := Param
- else
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Generic5 "' + Name + '"', 560);
-
- // --------------- MAIN CASE STATEMENT ----------------------
- if ParamPointer > 0 then
- // since we used AddProperty function to define properties, have to
- // use PropertyIdxMap to map to the correct Case index
- case PropertyIdxMap[ParamPointer] of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 561);
- 1:
- NPhases := Parser.Intvalue; // num phases
- 2:
- begin
- SetBus(1, param); //'bus1 = 8.1.2.3'
- //if True then
-
- end;
-
- 3:
- PresentkV := Parser.DblValue;
- 4:
- begin
- kWBase := Parser.DblValue;
- if (Pmax < 0) or (Pmax > kWBase * 1000) then
- Pmax := kWBase * 1000;
- if PQpriority = 1 then
- Pmax := kWBase * 1000;
- end;
-
- 5: ; // Do nothing; read only power factor := Parser.DblValue;
- 6:
- InterpretConnection(Parser.StrValue);
- 7:
- MachineData.kVArating := Parser.DblValue;
- //8: MachineData.Hmass := Parser.DblValue;
- //9: MachineData.D := Parser.DblValue;
- 10:
- P_ref1 := 1000 * Parser.DblValue; //for phase ctrl unit kW to W
- 11:
- P_ref2 := 1000 * Parser.DblValue; //for phase ctrl
- 12:
- P_ref3 := 1000 * Parser.DblValue; //for phase ctrl
- 13:
- V_ref1 := 1000 * Parser.DblValue; //for phase ctrl unit kV to V
- 14:
- V_ref2 := 1000 * Parser.DblValue; //for phase ctrl
- 15:
- V_ref3 := 1000 * Parser.DblValue; //for phase ctrl
- 16:
- MaxSlip := Parser.DblValue;
- 17: ;//InterpretOption(Parser.StrValue);
- 18:
- YearlyShape := Param;
- 19:
- DailyDispShape := Param;
- 20:
- DutyShape := Param;
- 21:
- DebugTrace := InterpretYesNo(Param);
- {}
- 22:
- Set_P_Ref(Parser.DblValue);//to norm value W from kW(in script)//for avg ctrl 1000*PrefKw/3;
- 23:
- Set_Q_Ref(Parser.DblValue);//to VA from kVA(in script) //for avg ctrl 1000*QrefKVAr/3;
- 24:
- Cluster_Num := Parser.Intvalue;
- 25:
- Set_V_Ref(Parser.Dblvalue);//kV to V //for avg ctrl 1000*VrefkV;
- 26:
- ctrl_mode := Parser.Intvalue;
- ///////////////////////////////////////////
- ///
- ///////////////////////////////////////////
- 27:
- begin
- QV_flag := Parser.Intvalue;
- end; // QV_flag_0 :=QV_flag
- 28:
- kcd := Parser.dblvalue;
- 29:
- kcq := Parser.dblvalue;
- 30:
- kqi := Parser.dblvalue;
- 31:
- Q_ref1 := 1000 * Parser.DblValue; //for phase ctrl unit kVar to Var
- 32:
- Q_ref2 := 1000 * Parser.DblValue; //for phase ctrl
- 33:
- Q_ref3 := 1000 * Parser.DblValue; //for phase ctrl
- 34:
- begin
- Pmax := 1000 * Parser.DblValue; //Pmax has to be less then kW in script
- PMax_phase := Pmax / fnphases;
- end;
- 35:
- begin
- Pmin := 1000 * Parser.DblValue; //for phase ctrl
- PMin_phase := Pmax / fnphases;
- end;
- 36:
- PQpriority := Parser.intValue; //
- 37:
- Pmpp := 1000 * Parser.DblValue; //for pmpp kW
- 38:
- Pfctr1 := Parser.DblValue; //for pmpp
- 39:
- Pfctr2 := Parser.DblValue; //for pmpp
- 40:
- Pfctr3 := Parser.DblValue; //for pmpp
- 41:
- Pfctr4 := Parser.DblValue; //for pmpp
- 42:
- Pfctr5 := Parser.DblValue; //for pmpp
- 43:
- Pfctr6 := Parser.DblValue; //for pmpp
- 44:
- Pbias := 1000 * Parser.DblValue; //for pmpp
- 45:
- CC_switch := InterpretYesNo(parser.StrValue); // yes, true, y ,t; or no, false, n, f
- 46:
- kcq_drp2 := parser.DblValue; //cluster num
- 47:
- Volt_Trhd := parser.DblValue;
- 48:
- droop := Parser.intValue;
- else
- // Handle Inherited properties
- ClassEdit(ActiveGeneric5Obj, ParamPointer - NumPropsThisClass)
- end;
-
- // ---------------- SIDE EFFECTS CASE STATEMENT ---------------------
- // This case statment handles any side effects from setting a property
- // (for example, from Generator)
- if ParamPointer > 0 then
- case PropertyIdxMap[ParamPointer] of
- 1:
- SetNcondsForConnection; // Force Reallocation of terminal info
- 18:
- begin
- YearlyShapeObj := LoadShapeClass.Find(YearlyShape);
- if Assigned(YearlyShapeObj) then
- with YearlyShapeObj do
- if UseActual then
- SetPowerkW(MaxP);
- end;
- 19:
- begin
- DailyDispShapeObj := LoadShapeClass.Find(DailyDispShape);
- if Assigned(DailyDispShapeObj) then
- with DailyDispShapeObj do
- if UseActual then
- SetPowerkW(MaxP);
- end;
- 20:
- begin
- DutyShapeObj := LoadShapeClass.Find(DutyShape);
- if Assigned(DutyShapeObj) then
- with DutyShapeObj do
- if UseActual then
- SetPowerkW(MaxP);
- end;
- else
- end;
-
- //if Pmpp and fctrs are defined
- if ((ParamPointer >= 37) and (ParamPointer <= 44)) then
- begin
- Update_kWbase_by_Fctrs;// Update Pmax
- //will cover direct Pmax input by these
- end;
-
- // Get next token off Parser and continue editing properties
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
- // After editing is complete, the typical next step is to call the RecalcElementData function
- {---------------}
- //if QV_switch = 1 then //
- //begin
- //QV_flag := QV_flag_0;
- //QV_switch := 0;// wait next limit break
- //end;
- Update_PQlimits;
- {----------------}
- RecalcElementData;
- YPrimInvalid := TRUE; // Setting this flag notifies the DSS that something has changed
- // and the Yprim will have to be rebuilt
- end;
-
-end;
-
-//----------------------------------------------------------------------------
-// dont use this 0114-2018 by Ying
-//----------------------------------------------------------------------------
-function TGeneric5.MakeLike(const OtherIndMach012Name: String): Integer;
-
-// This function should be defined to handle the Like property inherited from
-// the base class.
-
-// The function copies the essential properties of another object of this class
-
-var
- OtherIndMach012: TGeneric5Obj;
- i: Integer;
-
-begin
- Result := 0;
- {See if we can find this IndMach012 name in the present collection}
- OtherIndMach012 := Find(OtherIndMach012Name);
- if (OtherIndMach012 <> NIL) // skip if not found
- then
- with ActiveGeneric5Obj do
- begin
- // You should first set the basic circuit element properties, for example
- if (Fnphases <> OtherIndMach012.Fnphases) then
- begin
- Nphases := OtherIndMach012.Fnphases;
- NConds := Fnphases; // Forces reallocation of terminal stuff
-
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
- end;
-
- PresentkV := OtherIndMach012.PresentkV;
- kWBase := OtherIndMach012.kWBase;
-
- puRs := OtherIndMach012.puRs;
- puRr := OtherIndMach012.puRr;
- puXr := OtherIndMach012.puXr;
- puXm := OtherIndMach012.puXm;
- puXs := OtherIndMach012.puXs;
- MaxSlip := OtherIndMach012.MaxSlip;
-
- MachineData.kVArating := OtherIndMach012.MachineData.kVArating;
- MachineData.Hmass := OtherIndMach012.MachineData.Hmass;
- MachineData.D := OtherIndMach012.MachineData.D;
-
- // Do inherited properties
- ClassMakeLike(OtherIndMach012);
-
- // Finally initialize all the property value strings to be the same as
- // the copied element
- for i := 1 to ParentClass.NumProperties do
- FPropertyValue^[i] := OtherIndMach012.FPropertyValue^[i];
-
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Load MakeLike: "' + OtherIndMach012Name + '" Not Found.', 562);
-
-end;
-
-//------------------------- MAIN OBJECT CONSTRUCTOR ---------------------
-constructor TGeneric5Obj.Create(ParClass: TDSSClass; const Generic5ObjName: String);
-var
- i, j: Integer;
-//----------------------------------------------------------------------------
-begin
- inherited create(ParClass);
- Name := LowerCase(Generic5ObjName);
- DSSObjType := ParClass.DSSClassType; // Same as Parent Class
- TraceFile := nil;
-
- // Set some basic circuit element properties
- Nphases := 3; // typical DSS default for a circuit element
- Fnconds := 3; // defaults to delta
- Yorder := 0; // To trigger an initial allocation
- Nterms := 1; // forces allocations of terminal quantities
- kWBase := -1;//00; // has to be set in DSS scripts
-
- YearlyShape := '';
- YearlyShapeObj := NIL; // if YearlyShapeobj = nil then the load alway stays nominal * global multipliers
- DailyDispShape := '';
- DailyDispShapeObj := NIL; // if DaillyShapeobj = nil then the load alway stays nominal * global multipliers
- DutyShape := '';
- DutyShapeObj := NIL; // if DutyShapeobj = nil then the load alway stays nominal * global multipliers
-
- Debugtrace := FALSE;
- FmonObj := NIL;
- Yorder := Fnterms * Fnconds;
- ShapeIsActual := FALSE;
- Generic5SwitchOpen := FALSE;
-
- Connection := 1; // Delta Default
-
- MachineData.kVGeneratorBase := 12.47;
-
- MachineData.kVArating := kWBase * 1.2;
- with MachineData do
- begin
- Hmass := 1.0; // W-sec/VA rating
- Theta := 0.0;
- w0 := TwoPi * Basefrequency;
- Speed := 0.0; // relative speed
- dSpeed := 0.0;
- D := 1.0;
- XRdp := 20.0; // not used for indmach
-
- // newly added
- Conn := connection;
- NumPhases := Fnphases;
- NumConductors := Fnconds;
- end;
-
- {---- end note Andres: from dll model ----}
-
- {Typical machine impedance data}
- puRs := 0.0053;
- puXs := 0.106;
- puRr := 0.007;
- puXr := 0.12;
- puXm := 4.0;
-
- // Set slip local and make generator model agree
- MaxSlip := 0.1; // 10% slip limit - set this before setting slip
- //Slip := -0.007; // About 1 pu power
- FixedSlip := FALSE; // Allow Slip to float to match specified power
-
- InDynamics := FALSE;
-
- // call the procedure to set the initial property string values
- InitPropertyValues(0);
- //NumOrder := 2;
- NumOrderX := nOrder;//2;// // system order
- NumOrderY := nOrder; //2;//// system output Y order
- {A,B,C,D, X_var, Y_out_var, V_in_var matrix}
- ReAllocMem(Amm, nOrder * nOrder * Sizeof(Amm^[1]));// dot X = Ax +Bu
- ReAllocMem(Bmn, nOrder * nOrder * Sizeof(Bmn^[1]));// suppose Y and U have the same dimesion. Square
- ReAllocMem(Cnm, nOrder * nOrder * Sizeof(Cnm^[1]));
- ReAllocMem(Dnn, nOrder * nOrder * Sizeof(Dnn^[1]));
- ReAllocMem(X_var, nOrder * Sizeof(X_var^[1]));
- ReAllocMem(dX_vardt, nOrder * Sizeof(dX_vardt^[1]));
- ReAllocMem(X_varn, nOrder * Sizeof(X_varn^[1])); // for trapezoid integration
- ReAllocMem(dX_vardtn, nOrder * Sizeof(dX_vardtn^[1])); // for trapezoid integration
- ReAllocMem(Y_out_var, nOrder * Sizeof(Y_out_var^[1]));
- ReAllocMem(V_in_var, nOrder * Sizeof(V_in_var^[1]));
- ReAllocMem(pV_f_cc, nOrder * Sizeof(pV_f_cc^[1]));
- //Allocate ABCDXYV
- {A,B matrix, X_var} //5 order system
- for i := 1 to nOrder do
- begin
- for j := 1 to nOrder do
- begin
- Amm^[(i - 1) * nOrder + j] := 0.0;//CMPLX(0.0, 0.0);
- //if j=i then
- // Amm^[(i-1)*nOrder +j] := 1;//Amm := 0;
- end;
- for j := 1 to nOrder do
- begin
- Bmn^[(i - 1) * nOrder + j] := 0.0;//CMPLX(0.0, 0.0);
- if j = i then
- Bmn[(i - 1) * nOrder + j] := 1;
- end;
- X_var^[i] := 0.0;//CMPLX(0.0, 0.0);
- dX_vardt^[i] := 0.0;// derivatives
- X_varn^[i] := 0.0;// for trapezoid
- dX_vardtn^[i] := 0.0;// derivatives
- end;
- {C,D Matrix, Y, V}
- for i := 1 to nOrder do
- begin
- for j := 1 to nOrder do
- begin
- Dnn[(i - 1) * nOrder + j] := 0.0;//CMPLX(0.0, 0.0);
- end;
- for j := 1 to nOrder do
- begin
- Cnm[(i - 1) * nOrder + j] := 0.0;//CMPLX(0.0, 0.0);
- if i = j then
- Cnm[(i - 1) * nOrder + j] := 1;
- end;
- Y_out_var[i] := 0.0;//CMPLX(0.0, 0.0);
- V_in_var[i] := 0.0;//CMPLX(0.0, 0.0);
- pV_f_cc[i] := 0.0;
- end;
- P_ref := 0;
- V_ref := 1;
- Id := 0;
- Iq := 0;//default current
- Id1 := 0;
- Iq1 := 0;
- Id2 := 0;
- Iq2 := 0;
- Id3 := 0;
- Iq3 := 0;//
- {}
- kcd := 0.1;
- kcq := 0.1;
- kqi := 0.1; //for local control gain in vi1, vi2
- Volt_Trhd := 0.0;
- //Id_ref := 1;
- //Iq_ref := 0;
- Cluster_Num := 0;//by default.
- // Update anything that has to be calculated from property values
- DQDV := 1;//
- ctrl_mode := 0;// avg contrl by default
- QV_flag := 0;
- //QV_flag_0 := QV_flag;
- {------------------}
- //PQ max
- PMax := -1; // Activity power output limit MachineData.kVArating := 1.2*kWbase; Pmax, Smax
- PMax_phase := PMax / fnphases; //limit per phase
- PMin := 0; //(0, default)
- Pmin_phase := PMin / fnphases; //
- Qmax := 1000 * MachineData.kVArating; //Reactive power output limit
- Qmax_phase := Qmax / fnphases;
- Qmin := -Qmax; //(-Qmax, default)
- Qmin_phase := Qmin / fnphases; //
- PQpriority := 1;//P priority
- Pmpp := 1;//Pmpp, default value is 1.0;
- Pbias := 0; //Pbias, default value is 0.0;
- Pfctr1 := 1;//factors, default value all are 1.0;
- Pfctr2 := 1;
- Pfctr3 := 1;
- Pfctr4 := 1;
- Pfctr5 := 1;
- Pfctr6 := 1;
- {------------------}
- kcq_drp2 := 0;
- CC_switch := FALSE;
- flag_dyna_Id_chg := FALSE;
-
- z_dfs_plot := 0.0;
-
- RecalcElementData;
-
-end;
-
-
-//----------------------------------------------------------------------------
-destructor TGeneric5Obj.Destroy;
-//----------------------------------------------------------------------------
-
-// Free everything here that needs to be freed
-// If you allocated anything, dispose of it here
-
-begin
-
- //A, B matrix
- if Assigned(Amm) then
- ReallocMem(Amm, 0);
- if Assigned(Bmn) then
- Reallocmem(Bmn, 0);
- if Assigned(Cnm) then
- Reallocmem(Cnm, 0);
- if Assigned(Dnn) then
- Reallocmem(Dnn, 0);
- if Assigned(X_var) then
- Reallocmem(X_var, 0);
- if Assigned(dX_vardt) then
- Reallocmem(dX_vardt, 0);
- if Assigned(X_varn) then
- Reallocmem(X_varn, 0);
- if Assigned(dX_vardtn) then
- Reallocmem(dX_vardtn, 0);
- if Assigned(Y_out_var) then
- Reallocmem(Y_out_var, 0);
- if Assigned(V_in_var) then
- Reallocmem(V_in_var, 0);
- ReAllocMem(pV_f_cc, 0);
-
- FreeAndNil(TraceFile);
-
- inherited Destroy; // This will take care of most common circuit element arrays, etc.
-
-end;
-
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.RecalcElementData;
-//----------------------------------------------------------------------------
-
-var
- Rs, Xs,
- Rr, Xr,
- Xm, ZBase: Double;
- modetest: Boolean;
- numPhase, DotPos: Integer;
- strtemp: String;
-begin
-
- with MachineData do
- begin
- ZBase := Sqr(kVGeneratorBase) / kVArating * 1000.0;
- Conn := connection;
- NumPhases := Fnphases;
- NumConductors := Fnconds;
- end;
-
-
- Rs := puRs * ZBase;
- Xs := puXs * ZBase;
- Rr := puRr * ZBase;
- Xr := puXr * ZBase;
- Xm := puXm * ZBase;
- Zs := Cmplx(Rs, Xs);
- Zm := Cmplx(0.0, Xm);
- Zr := Cmplx(Rr, Xr);
-
- Xopen := Xs + Xm;
- Xp := Xs + (Xr * Xm) / (Xr + Xm);
- Zsp := Cmplx(Rs, Xp);
- //Yeq := Cinv(Zsp); // for Yprim for dynamics
- //Yeq := Cmplx(1.0/ZBase, -0.5/Zbase); // vars are half the watts
- Yeq := Cmplx(0.0, -1.0 / ZBase); // vars only for power flow
- T0p := (Xr + Xm) / (MachineData.w0 * Rr);
-
- // dSdP := Compute_dSdP;
-
- Is1 := CZERO;
- V1 := CZERO;
- Is2 := CZERO;
- V2 := CZERO;
-
- FirstIteration := TRUE;
-
- Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
-
- SetNominalPower;
- ///////////////////////////////////////////
- /// contrl mode
- /// ctrl_mode =0; phases = 3; // pos avg control---p_ref, V_ref, Q_ref
- /// ctrl_mode =1; phases = 1; bus1 = 452.1; ---p_ref1, V_ref1, Q_ref1
- /// ctrl_mode =2; phases = 1; bus1 = 452.2; ---p_ref2, V_ref2, Q_ref2
- /// ctrl_mode =3; phases = 1; bus1 = 452.3; ---p_ref3, V_ref3, Q_ref3
- /// ctrl_mode =4; phases = 3; bus1 = 452.2; ---p_ref1,2,3, V_ref1,2,3, Q_ref1,2,3
- ///
- ///////////////////////////////////////////
- modetest := FALSE;
- if (((ctrl_mode = 0) or (ctrl_mode = 4)) and (fnphases = 3)) then
- modetest := TRUE
- else
- if (fnphases = 1) then
- begin
- if ((ctrl_mode = 1) or (ctrl_mode = 2) or (ctrl_mode = 3)) then
- begin
- strtemp := FirstBus; //only one
- DotPos := Pos('.', strtemp);
- if DotPos <> 0 then
- begin
- numphase := sysutils.StrToInt(Trim(Copy(strtemp, DotPos + 1, 1))); // Bus Name . node Num
- if numphase = ctrl_mode then
- modetest := TRUE
- end;
- end;
- end;
- if modetest = FALSE then
- DoSimpleMsg('ctrl_mode and bus node connection dont match, see help for generic5.ctrl_mode', 561);
- //////////////////////////////////////////////
- //if cluster_num >= 1 then // assign the virtue leader to this DG
- //FMonObj := ActiveCircuit.Fmonitors.Get(cluster_num);
- //if function 'get' fails , return nil
- //////////////////////////////////////////////
- if CompareText(YearlyShape, 'none') = 0 then
- YearlyShape := '';
- if CompareText(DailyDispShape, 'none') = 0 then
- DailyDispShape := '';
- if CompareText(DutyShape, 'none') = 0 then
- DutyShape := '';
-
- if YearlyShapeObj = NIL then
- if Length(YearlyShape) > 0 then
- DoSimpleMsg('WARNING! Yearly load shape: "' + YearlyShape + '" Not Found.', 563);
- if DailyDispShapeObj = NIL then
- if Length(DailyDispShape) > 0 then
- DoSimpleMsg('WARNING! Daily load shape: "' + DailyDispShape + '" Not Found.', 564);
- if DutyShapeObj = NIL then
- if Length(DutyShape) > 0 then
- DoSimpleMsg('WARNING! Duty load shape: "' + DutyShape + '" Not Found.', 565);
-
- SpectrumObj := SpectrumClass.Find(Spectrum);
- if SpectrumObj = NIL then
- DoSimpleMsg('ERROR! Spectrum "' + Spectrum + '" Not Found.', 566);
-
- if DebugTrace then
- InitTraceFile
- else
- FreeAndNil(TraceFile);
-
-end;
-
- {
-Procedure TGeneric5Obj.CalcABMatrix ;
-//var
-// i,j :Integer;
-begin
- //this is useless. All things is to be done in updateabcd
-end;
-
-
-procedure TGeneric5Obj.InterpretOption(s: String);
-begin
- Case Uppercase(s)[1] of
- 'F': Fixedslip := TRUE;
- 'V': Fixedslip := FALSE;
- Else
-
- End;
-end;
-
-//---------------------------------------------------------------------------- }
-procedure TGeneric5Obj.SetPowerkW(const PkW: Double);
-begin
- kWBase := PkW;
-end;
-
-procedure TGeneric5Obj.Set_PresentkV(const Value: Double);
-begin
- with MachineData do
- begin
- kVGeneratorBase := Value;
- case FNphases of
- 2, 3:
- VBase := kVGeneratorBase * InvSQRT3x1000;
- else
- VBase := kVGeneratorBase * 1000.0;
- end;
- end;
-end;
-
-function TGeneric5Obj.Get_PresentkV: Double;
-begin
- Result := MachineData.kVGeneratorBase;
-end;
-
-//----------------------------------------------------------------------------
-//--------------------- MAIN CALC ROUTINES -----------------------------------
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.Integrate;
-//----------------------------------------------------------------------------
-
-var
- h2: Double;
-
-begin
- if ctrl_mode = 0 then
- begin
- with ActiveCircuit.Solution.Dynavars do
- begin
- if IterationFlag = 0 then
- begin // on predictor step
- Idn := Id;
- Iqn := Iq;
- dIddtn := dIddt;
- dIqdtn := dIqdt;
- vi1n := vi1;
- vi2n := vi2;
- dvi1dtn := dvi1dt;
- dvi2dtn := dvi2dt;
- end;
-
- update_system_abcd; //vi1, vi2 calculation
-
- h2 := h * 0.5;
-
- Id_ref := kcd * P_ref / V_DG;//active
-
- Iq_ref := Q_DG / V_DG; //V_ref ~= 1; reactive
- //vi1 := kcq* (Iq_ref - Iq);
- DPx := P_ref - P_DG;
- vi1 := 1 * kcd * DPx / V_DG;//+ kcq* (Iq_ref - Iq);//+ //active + additional control
- //vi2 := kcd* (Id_ref - Id); //reactive
- dvi1dt := kcd * (DPx);
- //dvi2dt := -kcd* (V_ref-V_DG) ; //voltage droop control
- if QV_flag = 1 then
- vi2 := kcq * (V_ref - V_DG) //voltage droop control
- else
- vi2 := kqi * (Q_ref - Q_DG);
- {--------------------------------------------}
-
-
- dIddt := vi1;//1; //active P
- dIqdt := vi2;//2; //reactive Q
-
- Id := Idn + h2 * (dIddt + dIddtn);
- Iq := Iqn + h2 * (dIqdt + dIqdtn);
- end;
- end;
-end;
-
-{-------------------------------------------------------------------------------}
-{integrate with ABCD}
-procedure TGeneric5Obj.IntegrateABCD;
-//----------------------------------------------------------------------------
-var
- h2: Double;
- i, j: Integer;
-begin
- if ActiveCircuit.Solution.Dynavars.IterationFlag = 0 then
- begin
- for i := 1 to numOrderX do
- begin
- X_varn^[i] := X_var^[i];
- dX_vardtn^[i] := dX_vardt^[i];
- end;
- end;
-
- update_system_abcd; //Matrix ABCD calculation if they are state-dependant
- update_controlinput; //vi1, vi2 calculation,
- // co control strategies from network vfi can be done here
- //dX_vardt calculation
- for i := 1 to numOrderX do // numOrderX, numOrderY should be less than norder 5
- begin
- dX_vardt^[i] := 0.0;
- for j := 1 to numOrderY do
- begin
- dX_vardt^[i] := dX_vardt^[i] + Amm^[norder * (i - 1) + j] * X_var^[j] + Bmn^[norder * (i - 1) + j] * V_in_var^[j];
- //cooperate control if exist is involved in
- end;
-
- end;
-
- // Trapezoidal Integration
- h2 := ActiveCircuit.Solution.Dynavars.h * 0.5;
- for i := 1 to numorderX do
- begin
- X_var[i] := X_varn[i] + h2 * (dX_vardtn[i] + dX_vardt[i]);
- end;
- {----------------}
- //Y=CX to be added
- {----------------}
- ///
- /// the following is to connect with calcDynamic or CalcDynamicVIabc
- /// because Id, Iq; Id1, Iq1, Id2, Iq2, Id3, Iq3 will be used there
- if ctrl_mode = 0 then //pos seq control
- begin
- Id := X_var[1];//can be put in calcdyna, so the integrate is just for X_var
- Iq := X_var[2];
- end
- else // all other ctrl_mode's are phase control modes
- begin
- Id1 := X_var[1];//1st phase, or the only phase if fnphases=1
- Iq1 := X_var[2];//can be put in calcdyna in futher, so the integrate is just for X_var
- Id2 := X_var[3];//2nd phase; zero if single phase
- Iq2 := X_var[4];
- Id3 := X_var[5];//3rd phase; zero if single phase
- Iq3 := X_var[6];
- end;
-end;
-
-{-------------------------------------------------------------------------------}
-//This part deals with the control input, is based on the voltage measurement
-procedure TGeneric5Obj.update_controlInput;
-var
- j: Integer;
- temp_pref, temp_qref, temp_vref, Pref3: Double;
-begin
- //Update_PQlimits;
- ///////////////////////////////////
- //local control input and alpha gradient calculation
- ///////////////////////////////////
- //gradient, gradient1, gradient2, gradient3
- // gradient will be calculated in FMonitor because of Bii, Q_Di etc
- //calculate_gradient; //alpha, and gradients,
- //V_DG, Q_DG have been updated either in 'init' or in 'calcdynamic'
-
- if ctrl_mode = 0 then //pos seq control mode
- begin
- //Id
- // P and Q control
- {
- //Iq
- if QV_flag=1 then
- vi2 := kcq* (V_ref - V_DG) //reactive V_ref control
- else
- vi2 := kqi* (Q_ref - Q_DG); //reactive Q_ref control
- }
- // vi1, vi2 local gradient
- // vi1, vi2 =0, local gradient calculated outside
- vi1 := 0;
- vi2 := 0; // local gradient calculated IN fMONITOR Node
- {---if in curtailment P_ref has to be changed here-----}
- if (ActiveCircuit.Solution.bCurtl = TRUE) and (FmonObj.ld_fm_info[0].b_ctrl_hghst = TRUE) then
- //if (ActiveCircuit.Solution.bCurtl=true) then // this will cause oscillation
- begin
- Pref3 := V_DG * Id; //Here, P_ref will never go out of limits.
- //if cuitailment is needed, update P_ref here; then vi1 will be 0
- //check limits
- if Pref3 > Pmax then
- begin
- Pref3 := Pmax; //set real power change during the simulation
- end
- else
- if Pref3 < Pmin then
- begin
- Pref3 := Pmin;
- end;
- P_ref := Pref3 / 3.0;
- end;
- {--use vi1 to follow p_ref--}
-
- DPx := fnphases * P_ref - P_DG;
- vi1 := 100 * kcd * DPx / V_DG; //pref control is 100 times faster than Curtailment
- //update V_in_var
- V_in_var^[1] := vi1;
- V_in_var^[2] := vi2;
-
- end
- else
- begin //phases control mode
-
- if fnphases = 3 then
- begin
- //12
- DPx := P_ref1 - P_DG1;
- vi1 := kcd * DPx / V_DG1;
- //Iq
- if QV_flag = 1 then
- begin
- if cc_switch = FALSE then //droop
- vi2 := kcq * (V_ref1 - V_DG1) //reactive V_ref control
- //gradient
- else
- vi2 := Qmax_phase / V_DG1 * (-kcq * Gradient1);
- if ((Q_DG1 >= Qmax_phase) or (Q_DG1 <= Qmin_phase)) then // switch control mode to Q_ref control
- begin
- //QV_flag := 0;
- if (Q_DG1 >= Qmax_phase) then
- begin
- Q_ref1 := Qmax_phase; // set Q_ref
- Q_ref2 := Qmax_phase; // set Q_ref
- Q_ref3 := Qmax_phase; // set Q_ref
- end
- else
- begin
- Q_ref1 := Qmin_phase;
- Q_ref2 := Qmin_phase;
- Q_ref3 := Qmin_phase;
- end;
- vi2 := kqi * (Q_ref1 - Q_DG1); //reactive Q_ref control
- end
- end
- else
- vi2 := kqi * (Q_ref1 - Q_DG1); //reactive Q_ref control
- //update V_in_var
- V_in_var^[1] := vi1;
- V_in_var^[2] := vi2;
- //34
- DPx := P_ref2 - P_DG2;
- vi1 := kcd * DPx / V_DG2;
- //Iq
- if QV_flag = 1 then
- begin
- if cc_switch = FALSE then //droop
- vi2 := kcq * (V_ref2 - V_DG2) //reactive V_ref control
- //gradient
- else
- vi2 := Qmax_phase / V_DG2 * (-kcq * Gradient2);
- if ((Q_DG2 >= Qmax_phase) or (Q_DG2 <= Qmin_phase)) then // switch control mode to Q_ref control
- begin
- //QV_flag:=0;
- if (Q_DG2 >= Qmax_phase) then
- begin
- Q_ref1 := Qmax_phase; // set Q_ref
- Q_ref2 := Qmax_phase; // set Q_ref
- Q_ref3 := Qmax_phase; // set Q_ref
- end
- else
- begin
- Q_ref1 := Qmin_phase;
- Q_ref2 := Qmin_phase;
- Q_ref3 := Qmin_phase;
- end;
- vi2 := kqi * (Q_ref2 - Q_DG2); //reactive Q_ref control
- end
- end
- else
- vi2 := kqi * (Q_ref2 - Q_DG2); //reactive Q_ref control
- //update V_in_var
- V_in_var^[3] := vi1;
- V_in_var^[4] := vi2;
- //56
- DPx := P_ref3 - P_DG3;
- vi1 := kcd * DPx / V_DG3;
- //Iq
- if QV_flag = 1 then
- begin
- if cc_switch = FALSE then //droop
- vi2 := kcq * (V_ref3 - V_DG3) //reactive V_ref control
- //gradient
- else
- vi2 := Qmax_phase / V_DG3 * (-kcq * Gradient3);
- if ((Q_DG3 >= Qmax_phase) or (Q_DG3 <= Qmin_phase)) then // switch control mode to Q_ref control
- begin
- //QV_flag := 0;
- if (Q_DG3 >= Qmax_phase) then
- begin
- Q_ref1 := Qmax_phase; // set Q_ref
- Q_ref2 := Qmax_phase; // set Q_ref
- Q_ref3 := Qmax_phase; // set Q_ref
- end
- else
- begin
- Q_ref1 := Qmin_phase;
- Q_ref2 := Qmin_phase;
- Q_ref3 := Qmin_phase;
- end;
- vi2 := kqi * (Q_ref3 - Q_DG3); //reactive Q_ref control
- end
- end
- else
- vi2 := kqi * (Q_ref3 - Q_DG3); //reactive Q_ref control
- //update V_in_var
- V_in_var^[5] := vi1;
- V_in_var^[6] := vi2;
- end
- else
- if fnphases = 1 then
- begin
- //choose ref
- case ctrl_mode of
- 1:
- begin
- temp_pref := P_ref1;
- temp_qref := q_ref1;
- temp_vref := v_ref1;
- end;
- 2:
- begin
- temp_pref := P_ref2;
- temp_qref := q_ref2;
- temp_vref := v_ref2;
- end;
- 3:
- begin
- temp_pref := P_ref3;
- temp_qref := q_ref3;
- temp_vref := v_ref3;
- end;
- end;
-
- DPx := temp_pref - P_DG1;
- vi1 := kcd * DPx / V_DG1;
- //Iq
- if QV_flag = 1 then
- begin
- if cc_switch = FALSE then //droop
- vi2 := kcq * (temp_vref - V_DG) //reactive V_ref control
- else
- vi2 := Qmax_phase / V_DG1 * (-kcq * Gradient1);
- if ((Q_DG1 >= Qmax_phase) or (Q_DG1 <= Qmin_phase)) then // switch control mode to Q_ref control
- begin
- //QV_flag := 0;
- if (Q_DG1 >= Qmax_phase) then
- temp_qref := Qmax_phase // set Q_ref
- else
- temp_qref := Qmin_phase;
- //
- vi2 := kqi * (temp_qref - Q_DG); //reactive Q_ref control
- //send Qref back
- //q_ref1 := temp_qref;
- //q_ref2 := temp_qref;
- //q_ref3 := temp_qref;
- end
- end
- else
- vi2 := kqi * (temp_qref - Q_DG); //reactive Q_ref control
- //update V_in_var
- V_in_var^[1] := vi1;
- V_in_var^[2] := vi2;
- end;
-
- end;
- {--------------------------------}
- // cooperate part is done here
- //pVinput^[j];
- update_pV_f_CC; //update pV_f_CC which is cooperate control
- {--------------------------------}
-//implement cooperate control
- for j := 1 to numOrderX do
- V_in_var^[j] := V_in_var^[j] + pV_f_CC^[j];
-end;
-
-procedure TGeneric5Obj.update_pV_f_CC_M2; //for power flow
-var
- j: Integer;
- num_vleader: Integer;
- Bii: Double;
-begin
- if cc_switch = FALSE then
- begin
- for j := 1 to numOrderX do
- pV_f_CC^[j] := 0.0;
- exit;
- end;
-
- //avg ctrl, under V120, I120
- //////////////////////////////////
- if FmonObj <> NIL then
- begin
- //try
- num_vleader := 1;
- /////////////////////////////////
- if ctrl_mode = 0 then
- begin
- //u = gradient + pV_f_CC; pV_f_CC = -alpha + sum(alpha_j)
- Bii := ActiveCircuit.Solution.NodeYii^[NodeRef^[1]].im;
- // Q ctrl with v_ref
- // pV_f_CC^[2] := FmonObj.Calc_Alpha_M2(ndNumincluster,0,NodeRef^[1],Bii,kcq,Volt_Trhd); // for dIddt, diqdt
- // Q ctrl with loss
- //pV_f_CC^[2] := FmonObj.Calc_Alpha_L(ndNumincluster,0,NodeRef^[1],Bii,kcq,Volt_Trhd);
- pV_f_CC^[2] := FmonObj.Calc_Alpha_LnM2(ndNumincluster, 0, NodeRef^[1], Bii, kcq, Volt_Trhd);
- // pV_f_CC^[2] := alpha * Qmax / v ;
- //P ctrl
- //pV_f_CC^[1] := FmonObj.Calc_AlphaP(ndNumincluster,0); // for dIddt, diqdt
- pV_f_CC^[1] := 0;
- end
- // phases control
- else
- begin
- if fnphases = 3 then
- begin
- pV_f_CC^[6] := 0.0;
- //u = gradient + pV_f_CC; pV_f_CC = -alpha + sum(alpha_j)
- Bii := ActiveCircuit.Solution.NodeYii^[NodeRef^[1]].im;
- pV_f_CC^[2] := FmonObj.Calc_Alpha_M2(ndNumincluster, 1, NodeRef^[1], Bii, kcq, Volt_Trhd);
- //pV_f_CC^[2] := FmonObj.Calc_Alpha_L(ndNumincluster,1,NodeRef^[1],Bii,kcq,Volt_Trhd);
- //pV_f_CC^[2] := FmonObj.Calc_Alpha_LnM2(ndNumincluster,1,NodeRef^[1],Bii,kcq,Volt_Trhd);
- pV_f_CC^[1] := FmonObj.Calc_AlphaP(ndNumincluster, 1);
- Bii := ActiveCircuit.Solution.NodeYii^[NodeRef^[2]].im;
- pV_f_CC^[4] := FmonObj.Calc_Alpha_M2(ndNumincluster, 2, NodeRef^[2], Bii, kcq, Volt_Trhd);
- //pV_f_CC^[4] := FmonObj.Calc_Alpha_L(ndNumincluster,2,NodeRef^[2],Bii,kcq,Volt_Trhd);
- //pV_f_CC^[4] := FmonObj.Calc_Alpha_LnM2(ndNumincluster,2,NodeRef^[2],Bii,kcq,Volt_Trhd);
- pV_f_CC^[3] := FmonObj.Calc_AlphaP(ndNumincluster, 2);
- Bii := ActiveCircuit.Solution.NodeYii^[NodeRef^[3]].im;
- pV_f_CC^[6] := FmonObj.Calc_Alpha_M2(ndNumincluster, 3, NodeRef^[3], Bii, kcq, Volt_Trhd);
- //pV_f_CC^[6] := FmonObj.Calc_Alpha_L(ndNumincluster,3,NodeRef^[3],Bii,kcq,Volt_Trhd);
- //pV_f_CC^[6] := FmonObj.Calc_Alpha_LnM2(ndNumincluster,3,NodeRef^[3],Bii,kcq,Volt_Trhd);
- pV_f_CC^[5] := FmonObj.Calc_AlphaP(ndNumincluster, 3);
- //pV_f_CC[1-6]; // for dIddt1, diqdt1,dIddt2, diqdt2,dIddt3, diqdt3
- end
- else
- if fnphases = 1 then
- begin
- //if ctrl_mode=1 then
- Bii := ActiveCircuit.Solution.NodeYii^[NodeRef^[1]].im;
- pV_f_CC^[2] := FmonObj.Calc_Alpha_M2(ndNumincluster, ctrl_mode, NodeRef^[1], Bii, kcq, Volt_Trhd); // for dIddt1, diqdt1
- //pV_f_CC^[2] := FmonObj.Calc_Alpha_L(ndNumincluster,ctrl_mode,NodeRef^[1],Bii,kcq,Volt_Trhd); // for dIddt1, diqdt1
- //pV_f_CC^[2] := FmonObj.Calc_Alpha_LnM2(ndNumincluster,ctrl_mode,NodeRef^[1],Bii,kcq,Volt_Trhd);
- pV_f_CC^[1] := FmonObj.Calc_AlphaP(ndNumincluster, ctrl_mode);
- end;
- end;
- end;
-end;
-
-procedure TGeneric5Obj.update_pV_f_CC; //used in dynamic mode to update alpha
-var
- p_mode,
- j,
- num_vleader: Integer;
- Bii,
- us_i, ul_i: Double;
-begin
- if cc_switch = FALSE then //no control at all //local gradient control will be set by communication matrix
- begin
- for j := 1 to numOrderX do
- pV_f_CC^[j] := 0.0;
- exit;
- end;
-
- //avg ctrl, under V120, I120
- //////////////////////////////////
- if FmonObj <> NIL then
- begin
- //try
- num_vleader := 1;
- /////////////////////////////////
- if ctrl_mode = 0 then
- begin
- p_mode := 0;
- if fmonobj <> NIL then
- p_mode := fmonobj.Get_P_mode;
- //u = gradient + pV_f_CC; pV_f_CC = -alpha + sum(alpha_j)
- Bii := ActiveCircuit.Solution.NodeYii^[NodeRef^[1]].im;
-
- if ActiveCircuit.Solution.DynaVars.SolutionMode = DYNAMICMODE then
- begin
- //Ip control
- if FmonObj.ld_fm_info[0].b_Curt_Ctrl = TRUE then // curtailment algorithm
- begin
- ul_i := FmonObj.Calc_ul_P(ndNumincluster, 0);
- us_i := kcd * FmonObj.Calc_Gradient_ct_P(ndNumincluster, 0);
- GradientP := us_i;
- if cc_switch = FALSE then //local
- pV_f_CC^[1] := 0.0
- else
- begin
- pV_f_CC^[1] := ul_i + us_i;
- pV_f_CC^[1] := pV_f_CC^[1] * Pmax / v_DG;
- end;
- end;
-
- //if pMode then
-
- if (p_mode = 1) and (cc_switch = TRUE) then //if delta P = p_trans_ref - p_trans
- begin //balance p_trans
- pV_f_CC^[1] := FmonObj.Calc_AlphaP(ndNumincluster, 0);//new alfa_p
- pV_f_CC^[1] := pV_f_CC^[1] - AlphaP; //derivative of alfa_p
- //use us_i to calculate the frequncy
- us_i := -FmonObj.omg_fm; //frequency droop
- pV_f_CC^[1] := (pV_f_CC^[1] + us_i) * Pmax / v_DG; // derivative of Ip in dynamic mode,
- //use us_i to
- end;
-
- //Iq control
- ul_i := FmonObj.Calc_fm_ul_0(ndNumincluster, 0, NodeRef^[1], Bii, kcq, Volt_Trhd);
- us_i := FmonObj.Calc_fm_us_0(ndNumincluster, 0, NodeRef^[1], Bii, kcq, Volt_Trhd);
- Gradient := us_i;
-
- if FmonObj.ld_fm_info[0].b_Curt_Ctrl = FALSE then
- begin // if curtailment for this cluster is off
-
- end
- else
- begin // if curtailment for this cluster is on
- //Q will try to boost the voltage while P is decreasing
- if (ActiveCircuit.Solution.bCurtl = TRUE) and (Gradient = 0.0) then
- us_i := -GradientP * Pmax / Qmax;
- end;
-
-
- if cc_switch = FALSE then //local
- begin
- pV_f_CC^[2] := us_i;
- end
- else
- begin // cc_switch is on
- pV_f_CC^[2] := ul_i + us_i; //cc //attack comes in ul_i (FmonObj.Calc_fm_ul_0)
- end;
- pV_f_CC^[2] := pV_f_CC^[2] * Qmax / v_DG;
-
- end
- else //power flow
- begin
- //alphaP: p ratio
-
- //if pMode then
- if (p_mode = 1) or (FmonObj.ld_fm_info[0].b_Curt_Ctrl = TRUE) then
- pV_f_CC^[1] := FmonObj.Calc_AlphaP(ndNumincluster, 0)
- else
- pV_f_CC^[1] := 0.0;
- //alpha : q ratio
- pV_f_CC^[2] := FmonObj.Calc_Alpha_M2(ndNumincluster, 0, NodeRef^[1], Bii, kcq, Volt_Trhd); // for dIddt, diqdt
-
- end;
- end;
- end;
-
-end;
-
-procedure TGeneric5Obj.InfoPublish;
-//var
-// j : integer;
-begin
- Update_PQlimits;
- if FmonObj <> NIL then
- begin
- with FmonObj.pNodeFMs^[NdNuminCluster] do
- begin
- case ctrl_mode of
- 1:
- begin
- vl_V1 := V_DG1;//Phase A or the first phase if there are less than 3 phases
- vl_P_DG1 := P_DG1;
- vl_Q_DG1 := Q_DG1;
- //vl_Alpha1_dg := Alpha1;
- //vl_AlphaP1_dg := AlphaP1;
- vl_V_ref1_dg := V_ref1;
- end;
- 2:
- begin
- vl_V2 := V_DG2;//Phase B if exists
- vl_P_DG2 := P_DG2;
- vl_Q_DG2 := Q_DG2;
- //vl_Alpha2_dg := Alpha2;
- //vl_AlphaP2_dg := AlphaP2;
- vl_V_ref2_dg := V_ref2;
- end;
- 3:
- begin
- vl_V3 := V_DG3;//Phase c if exists
- vl_P_DG3 := P_DG3;
- vl_Q_DG3 := Q_DG3;
- //vl_Alpha3_dg := Alpha3;
- //vl_AlphaP3_dg := AlphaP3;
- vl_V_ref3_dg := V_ref3;
-
- end;
- 0:
- begin
- vl_V := V_DG; //0 seq , will be used in FmonObj.Agnt_smpl
- vl_P_DG := P_DG;
- vl_Q_DG := Q_DG;
- //
- alpha := Q_DG / Qmax;
- vl_alpha_dg := alpha; // update first, will be used in FmonObj.Agnt_smpl
- //P control
- alphap := p_dg / Pmax;
- vl_alphaP_dg := alphaP;
- //
- vl_V_ref_dg := V_ref;
- if ActiveCircuit.Solution.DynaVars.SolutionMode = DYNAMICMODE then
- begin
- z_dfs_plot := z_dfs; // defense value
- end;
- end;
- 4:
- begin
- vl_V1 := V_DG1;//Phase A or the first phase if there are less than 3 phases
- vl_P_DG1 := P_DG1;
- vl_Q_DG1 := Q_DG1;
- vl_V2 := V_DG2;//Phase B if exists
- vl_P_DG2 := P_DG2;
- vl_Q_DG2 := Q_DG2;
- vl_V3 := V_DG3;//Phase c if exists
- vl_P_DG3 := P_DG3;
- vl_Q_DG3 := Q_DG3;
- vl_V_ref1_dg := V_ref1;
- vl_V_ref2_dg := V_ref2;
- vl_V_ref3_dg := V_ref3;
-
- //vl_Alpha1_dg := Alpha1;
- //vl_AlphaP1_dg := AlphaP1;
- //vl_V_ref1_dg := V_ref1;
- //vl_Alpha2_dg := Alpha2;
- //vl_AlphaP2_dg := AlphaP2;
- //vl_V_ref2_dg := V_ref2;
- //vl_Alpha3_dg := Alpha3;
- //vl_AlphaP3_dg := AlphaP3;
- //vl_V_ref3_dg := V_ref3;
- end;
- end;
- vl_Qmax_dg := Qmax;
- vl_Qmax_phase_dg := Qmax_Phase;
- vl_Pmax_dg := Pmax;
- vl_Pmax_phase_dg := Pmax_Phase;
- vl_CC_switch_dg := CC_switch;
- vl_QV_flag_dg := QV_flag;
- vl_kcd_dg := kcd;
- vl_kcq_dg := kcq;
- vl_volt_thrd_dg := Volt_Trhd;
- end;
- end;
- if FmonObj2 <> NIL then
- begin
- {with FmonObj2.pNodeFMs^[NdNuminCluster2] do
- begin
-
- vl_V := V_DG;
- vl_P_DG := P_DG;
- vl_Q_DG := Q_DG;
- vl_V1 := V_DG1;//Phase A or the first phase if there are less than 3 phases
- vl_P_DG1 := P_DG1;
- vl_Q_DG1 := Q_DG1;
- vl_V2 := V_DG2;//Phase B if exists
- vl_P_DG2 := P_DG2;
- vl_Q_DG2 := Q_DG2;
- vl_V3 := V_DG3;//Phase c if exists
- vl_P_DG3 := P_DG3;
- vl_Q_DG3 := Q_DG3;
- vl_Qmax_dg := Qmax;
- vl_Qmax_phase_dg := Qmax_Phase;
- vl_Pmax_dg := Pmax;
- vl_Pmax_phase_dg := Pmax_Phase;
- vl_Alpha_dg := alpha;
- vl_Alpha1_dg := Alpha1;
- vl_Alpha2_dg := Alpha2;
- vl_Alpha3_dg := Alpha3;
- vl_AlphaP_dg := alphaP;
- vl_AlphaP1_dg := AlphaP1;
- vl_AlphaP2_dg := AlphaP2;
- vl_AlphaP3_dg := AlphaP3;
- vl_V_ref_dg := V_ref;
- vl_V_ref1_dg := V_ref1;
- vl_V_ref2_dg := V_ref2;
- vl_V_ref3_dg := V_ref3;
- vl_CC_switch_dg := CC_switch;
- vl_QV_flag_dg := QV_flag;
-
- end; }
- end;
-
-end;
-
-procedure TGeneric5Obj.Set_P_ref(PrefKw: Double);
-begin
- P_ref := 1000 * prefKw / fnphases; //nphases
- P_ref1 := P_ref;
- P_ref2 := P_ref;
- P_ref3 := P_ref;
- if prefKw > kWbase then
- DoSimpleMsg('P ref should be leq than kW', 562);
- if ActiveCircuit.Solution.DynaVars.SolutionMode = DYNAMICMODE then //if P_ref changed in the dynamic simulation
- begin //The sudden change of Id has to be applied
- flag_dyna_Id_chg := TRUE;
- end;
- //
-end;
-
-procedure TGeneric5Obj.Set_Q_Ref(QrefkVAr: Double);
-begin
- //
- Q_ref := 1000 * QrefKVAr / fnphases; //nphases
- Q_ref1 := Q_ref;
- Q_ref2 := Q_ref;
- Q_ref3 := Q_ref;
-end;
-
-procedure TGeneric5Obj.Set_V_Ref(VrefkV: Double);
-begin
- //
- V_ref := 1000 * VrefkV;
- V_ref1 := V_ref;
- V_ref2 := V_ref;
- V_ref3 := V_ref;
-end;
-
-procedure TGeneric5Obj.Update_kWbase_by_Fctrs;
-begin
- kWbase := (Pmpp + Pbias) * Pfctr1 * Pfctr2 * Pfctr3 * Pfctr4 * Pfctr5 * Pfctr6;
- kWbase := kWbase / 1000;
- update_PQlimits;
-end;
-
-procedure TGeneric5Obj.update_system_abcd;
-begin
- //2 order system example
- //Amn := Zeros;
- //Bmn := I;
- //Cnm := I;
- {-Done in the initial part-}
-end;
-
-procedure TGeneric5Obj.UpdateAlpha_qi;
-begin
- //Update_PQlimits;
- if Qmax > epsilon then
- begin
- //pos control
- Alpha := Q_DG / Qmax;
- // phase control
- Alpha1 := Q_DG1 / Qmax_phase;
-
- if FNPhases = 3 then
- begin
- Alpha2 := Q_DG2 / Qmax_phase;
- Alpha3 := Q_DG3 / Qmax_phase;
- end;
- end;
-end;
-
-procedure TGeneric5Obj.Update_PQlimits;
-begin
- if PQpriority = 1 then //P prior by default
- begin
- if (Pmax <= 0) or (pmax > kWbase) then
- Pmax := kWbase * 1000;// first value is set to be kWbase; when kWbase is set, Pmax will be update in edit;
- Pmax := kWBase * 1000;//if PQpriority=1 then
- //if (Pmax <0 )or (Pmax > kWBase*1000) then Pmax := kWBase*1000;
- //Pmax
- //Pmax; should be what it is
- //P_DG
- if 1000 * machinedata.kVArating >= P_DG then // Pmax P_DG
- Qmax := sqrt(machinedata.kVArating * 1000 * machinedata.kVArating * 1000 - P_DG * P_DG) // PMax*PMax)//
- else
- Qmax := epsilon; //error when used as demoninator
- Qmin := -Qmax;
- end
- else
- if PQpriority = 0 then //Q prior
- begin
- Qmax := machinedata.kVArating;
- Qmin := -machinedata.kVArating;
-
- Pmax := min(sqrt(machinedata.kVArating * 1000 * machinedata.kVArating * 1000 - Q_DG * Q_DG), kWbase * 1000); //which one is smaller
- Pmin := 0;
- //Pmax_phase := Pmax / fnphases;
- //Pmin_phase := Pmin / fnphases;
- end;
- //if machinedata.kVArating <= 0 then
- //begin
- // if fmonobj <> nil then
- // Qmax := fmonobj.CalcAvgQmax; //if the node is without control, Qmax := cluster avg
- //end;
-
- Pmax_phase := Pmax / fnphases;
- Pmin_phase := Pmin / fnphases;
- Qmax_phase := Qmax / fnphases;
- Qmin_phase := Qmin / fnphases;
- //used for limit currents derivative
- Idmax_phase := Pmax_phase / (Vbase);// vBase := kVGeneratorBase*InvSQRT31000
- Iqmax_phase := Qmax_phase / (Vbase);
-end;
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.CalcDynamic(var V012, I012: TSymCompArray5);
-//----------------------------------------------------------------------------
-var
- Pref3: Double;
-begin
- //Update_Pqlimits;// confirm P, Q limits
- if ctrl_mode = 0 then
- begin
-
- InDynamics := TRUE;
- V1 := V012[1]; // Save for variable calcs
- V2 := V012[2];
-
- V_DG := cabs(V1);
- Theta_DG := cang(V1);
-
-
- {P} //P_DG follows ref, and allows sudden change
-
- P_DG := V_DG * Id; //update P_DG
- // if P_DG, Q_DG exceed the limits
- if P_DG > Pmax then
- begin
- P_DG := Pmax; //set real power change during the simulation
- Id := P_DG / V_DG; //set Id
- Idn := Id;
- X_var[1] := Id;
- X_varn[1] := Idn;
- dX_vardtn[1] := 0.0;
- end
- else
- if P_DG < Pmin then
- begin
- P_DG := Pmin;
- Id := P_DG / V_DG; //set Id
- Idn := Id;
- X_var[1] := Id;
- X_varn[1] := Idn;
- dX_vardtn[1] := 0.0;
- end;
-
- {Q}
- Q_DG := V_DG * Iq;
-
- if Q_DG >= Qmax then
- begin
- Q_DG := Qmax;
- Iq := Q_DG / V_DG;
- Iqn := Iq;
- X_var[2] := Iq;
- X_varn[2] := Iqn;
- dX_vardtn[1] := 0.0;
- end
- else
- if Q_DG <= Qmin then
- begin
- Q_DG := Qmin;
- Iq := Q_DG / V_DG;
- Iqn := Iq;
- X_var[2] := Iq;
- X_varn[2] := Iqn;
- dX_vardtn[1] := 0.0;
- end;
-
-
- Get_DynamicModelCurrent; //Iq Iq does not change, Is1 := cmplx(Id, Iq)*1 NIL then
- p_mode := fmonobj.Get_P_mode;
- if (p_mode = 1) and (cc_switch = TRUE) then //if delta P = p_trans_ref - p_trans
- begin //balance p_trans
- AlphaP := pV_f_CC^[1]; //alpha_p
- p_DG := Pmax * AlphaP;
- end
- else
- begin
- P_DG := fnphases * P_ref; // local
-
- end;
- if (p_DG > Pmax) then
- begin
- P_DG := Pmax;
- end
- else
- if (P_DG < Pmin) then
- begin
- P_DG := Pmin;
- end;
- AlphaP := P_DG / Pmax;
- {--- real power is controled above --}
- if QV_flag = 0 then //P_ref, Q_ref
- Curr := Conjg(Cdiv(Cmplx(P_DG / 3.0, Q_ref), V1))
- else //P_ref, V_ref
- begin
- if ActiveCircuit.Solution.Iteration = 1 then
- begin
- Iq := 0; //In power flow, start value of Iq for each power flow
- end;
-
- //update_pV_f_CC; //Alpha
- Alpha := pV_f_CC^[2]; // only when not dynamode
- Q_DG := Qmax * Alpha;
- Curr := Conjg(Cdiv(Cmplx(P_DG / 3.0, Q_DG / 3.0), V1));
- {----------------}
- end;
-
- I012[1] := Curr; // Save for variable calcs
- I012[2] := cmplx(0.0, 0.0);//force to be balanced output DG
- I012[0] := cmplx(0.0, 0.0);
- {change direction}//added by dahei
- I012[1] := cnegate(I012[1]);
- I012[2] := cnegate(I012[2]);
- I012[0] := cnegate(I012[0]);
- end // avg ctrl
- else //direct phase ctrl
- begin
- if fnphases = 3 then
- begin
- //3-phase ctrl
- end
- else
- if fnphases = 1 then
- begin
- //1-phase ctrl
- end
- else
- begin
- //no consideration for 2-phase DG
- end;
- end;
-end;
-
-procedure TGeneric5Obj.CalcPFlowVIabc(var Vabc, Iabc: pComplexArray);
-//----------------------------------------------------------------------------
-var
- tempV1, tempV2, tempV3,
- Curr1,
- Curr2,
- Curr3: Complex;
- temp_pref, temp_qref, temp_vref, temp_alpha: Double;
- flmt: Double;
- p_mode: Integer;
-begin
- flmt := 0.9;
- Update_Pqlimits; // Pmax_phase, Qmax_phase will be used in the following steps
- update_pV_f_CC_M2; // pV_f_CC, updated from virtual leader
- // Q ctrl: 3-phase, pV_f_CC^[2], [4], [6]
- // 1-phase, pV_f_CC^[2]
- // P ctrl: 3-phase, pV_f_CC^[1], [3], [5]
- // 1-phase, pV_f_CC^[1]
-
- if fnphases = 3 then
- begin
- tempV1 := Vabc[1]; // Save for variable calcs //assume Vabc[1][2][3] is ABC!
- tempV2 := Vabc[2];
- tempV3 := Vabc[3];
- if cabs(tempV1) = 0 then
- tempV1 := Cmplx(1, 0);
- if cabs(tempV2) = 0 then
- tempV2 := Cmplx(1, 0);
- if cabs(tempV3) = 0 then
- tempV3 := Cmplx(1, 0);
- end
- else
- if fnphases = 1 then
- begin
- tempV1 := Vabc[1]; // Save for variable calcs //assume Vabc[1][2][3] is ABC!
- tempV2 := Cmplx(1, 0);
- tempV3 := Cmplx(1, 0);
- end;
-
- V_DG1 := cabs(tempV1); // Save for variable calcs
- V_DG2 := cabs(tempV2);
- V_DG3 := cabs(tempV3);
- {----real power is control by Pref in DG----}
- P_DG1 := P_ref1;
- P_DG2 := P_ref2;
- P_DG3 := P_ref3;
-
- {}
- //calculate_gradient; //alpha, dalpha, and gradients
- //alpha is implemented in M2
- p_mode := 0;
- if fmonobj <> NIL then
- p_mode := fmonobj.Get_P_mode;
- if (p_mode = 1) and (cc_switch = TRUE) then //if delta P = p_trans_ref - p_trans
- begin
- case ctrl_mode of
- 1:
- begin
- AlphaP1 := pV_f_CC^[1];
- p_DG1 := p_DG1 + Pmax_phase * AlphaP1;
- //p_DG1 := Pmax_phase * AlphaP1;
- end;
- 2:
- begin
- AlphaP2 := pV_f_CC^[1];//if single phase only pV_f_CC^[1] and pV_f_CC^[2]
- p_DG2 := p_DG2 + Pmax_phase * AlphaP2;
- //p_DG2 := Pmax_phase * AlphaP2;
- end;
- 3:
- begin
- AlphaP3 := pV_f_CC^[1]; //if single phase only pV_f_CC^[1] and pV_f_CC^[2]
- p_DG3 := p_DG3 + Pmax_phase * AlphaP3;
- //p_DG3 := Pmax_phase * AlphaP3;
- end;
- 4:
- begin
- AlphaP1 := pV_f_CC^[1];
- p_DG1 := p_DG1 + Pmax_phase * AlphaP1;
- //p_DG1 := Pmax_phase * AlphaP1;
- AlphaP2 := pV_f_CC^[3];
- p_DG2 := p_DG2 + Pmax_phase * AlphaP2;
- //p_DG2 := Pmax_phase * AlphaP2;
- AlphaP3 := pV_f_CC^[5];
- p_DG3 := p_DG3 + Pmax_phase * AlphaP3;
- //p_DG3 := Pmax_phase * AlphaP3;
- end;
- end;
- if (p_DG1 > Pmax_phase) then
- begin
- P_DG1 := Pmax_phase;
- end
- else
- if (P_DG1 < Pmin_phase) then
- begin
- P_DG1 := Pmin_phase;
- end;
- if (P_DG2 > Pmax_phase) then
- begin
- P_DG2 := Pmax_phase;
- end
- else
- if (P_DG2 < Pmin_phase) then
- begin
- P_DG2 := Pmin_phase;
- end;
- if (P_DG3 > Pmax_phase) then
- begin
- P_DG3 := Pmax_phase;
- end
- else
- if (P_DG3 < Pmin_phase) then
- begin
- P_DG3 := Pmin_phase;
- end;
- Update_Pqlimits; // Qmax_phase will be updated accordingly
- end;
- // calc P_DG
- case ctrl_mode of
- 1:
- begin
- P_DG := P_dg1;
- end;
- 2:
- begin
- P_DG := P_dg2;
- end;
- 3:
- begin
- P_DG := P_dg3;
- end;
- 4:
- begin
- P_DG := P_DG1 + P_DG2 + P_DG3;
- end;
- end;
- {Q Control}
- if ctrl_mode = 0 then
- begin
- //will never be used
- //will be in CalcPFlow
- end // avg ctrl
- else //direct phase ctrl
- begin
- if fnphases = 3 then
- begin
- //3-phase ctrl
- // V_Theta1 := cang(V1);
- // V_Theta2 := cang(V2);
- // V_Theta3 := cang(V3);
-
- InDynamics := FALSE;
-
- //if (P_Mode = 1) and then
- //real power control
-
- // Guess at a new var output value
- if QV_flag = 0 then //P_ref, Q_ref
- begin
- Curr1 := Conjg(Cdiv(Cmplx(P_dg1, Q_ref1), tempV1)); //currents A,B,C
- Curr2 := Conjg(Cdiv(Cmplx(P_dg2, Q_ref2), tempV2));
- Curr3 := Conjg(Cdiv(Cmplx(P_dg3, Q_ref3), tempV3));
- end
- else //P_ref, V_ref
- begin
- //phase A
- //1 st ireration Iq := 0;
- if ActiveCircuit.Solution.Iteration = 1 then
- begin
- Iq1 := 0; //In power flow, start value of Iq for each power flow
- Iq2 := 0;
- Iq3 := 0;
- end; //should be taken care of here
- if cc_switch = FALSE then //droop //Q_DG starts from 0
- begin
- ///////////integral droop
- dIqdt := kcq * (V_ref1 - V_DG1) / ActiveCircuit.Solution.Iteration;
- if abs(V_ref1 - v_DG1) <= Volt_Trhd * V_ref1 then
- dIqdt := 0.0;
-
- //if abs(dIqdt)> flmt*Iqmax_phase then dIqdt := sign(dIqdt)*flmt*Iqmax_phase;
- Iq1 := Iq1 + dIqdt;
- Q_DG1 := V_DG1 * Iq1;
- if droop = 2 then
- /////////////////}
- Q_DG1 := kcq_drp2 * (V_ref1 - V_DG1) * 1000 * machinedata.kVArating / 0.05 / V_ref1;
- end
- {gradient control}
- else
- begin // cooperative control
- //dIqdt := Qmax_phase/ V_DG1 * ( - kcq*Gradient1);
- //dIqdt := dIqdt / ActiveCircuit.Solution.Iteration;
- //dIqdt := dIqdt + Qmax_phase/ V_DG1 * (pV_f_CC[2]);//dIqdt1
- //second method
- //calc alpha
- //calc Q
- //Iq1 := Iq1 + dIqdt;
- //Q_DG1 := V_DG1 * Iq1;
- Alpha1 := pV_f_CC^[2];
- Q_DG1 := Qmax_phase * Alpha1;
- end;
- {----------------}
- //if abs(dIqdt)> flmt*Iqmax_phase then dIqdt := sign(dIqdt)*flmt*Iqmax_phase;
-
- //If (Q_DG1 >= Qmax_phase) then Q_DG1 := Qmax_phase; //limit check only one phase
- //If Q_DG1 <= Qmin_phase then Q_DG1:= Qmin_phase;
-
- //phase B
- if cc_switch = FALSE then //droop
- begin
- ///////////integral droop
- dIqdt := kcq * (V_ref2 - V_DG2) / ActiveCircuit.Solution.Iteration; // ref control
- if abs(V_ref2 - v_DG2) <= Volt_Trhd * V_ref2 then
- dIqdt := 0.0;
- Iq2 := Iq2 + dIqdt; //In power flow, Iq starts from 0;
- Q_DG2 := V_DG2 * Iq2;
- if droop = 2 then
- /////////////////}
- Q_DG2 := kcq_drp2 * (V_ref2 - V_DG2) * 1000 * machinedata.kVArating / 0.05 / V_ref1;
- end
- {gradient control}
- else
- begin
- //dIqdt := Qmax_phase/ V_DG2 * ( - kcq*gradient2); //self gradient
- //dIqdt := dIqdt / ActiveCircuit.Solution.Iteration;
- //dIqdt := dIqdt + Qmax_phase/ V_DG2 * (pV_f_CC[4]);//dIqdt2
- //Iq2 := Iq2 + dIqdt; //In power flow, Iq starts from 0;
- //Q_DG2 := V_DG2 * Iq2;
- Alpha2 := pV_f_CC^[4];
- Q_DG2 := Qmax_phase * Alpha2;
- end;
- //if abs(dIqdt)> flmt*Iqmax_phase then dIqdt := sign(dIqdt)*flmt*Iqmax_phase;
- {----------------}
-
- //If Q_DG2 >= Qmax_phase then Q_DG2:= Qmax_phase; //limit check only one phase
- //If Q_DG2 <= Qmin_phase then Q_DG2:= Qmin_phase;
-
- //phase C
- if cc_switch = FALSE then //droop
- begin
- ///////////integral droop
- dIqdt := kcq * (V_ref3 - V_DG3) / ActiveCircuit.Solution.Iteration;
- if abs(V_ref3 - v_DG3) <= Volt_Trhd * V_ref3 then
- dIqdt := 0.0;
- Iq3 := Iq3 + dIqdt; //In power flow, Iq starts from 0;
- Q_DG3 := V_DG3 * Iq3;
- if droop = 2 then /////////////////}
- Q_DG3 := kcq_drp2 * (V_ref3 - V_DG3) * 1000 * machinedata.kVArating / 0.05 / V_ref1;
- end
- else
- begin
- {gradient control}
- //dIqdt := Qmax_phase/ V_DG3 * ( - kcq*gradient3);
- //dIqdt := dIqdt / ActiveCircuit.Solution.Iteration;
- //dIqdt := dIqdt + Qmax_phase/ V_DG3 * (pV_f_CC[6]);//dIqdt3
- //Iq3 := Iq3 + dIqdt; //In power flow, Iq starts from 0;
- //Q_DG3 := V_DG3 * Iq3;
- Alpha3 := pV_f_CC^[6];
- Q_DG3 := Qmax_phase * Alpha3;
- end;
-
- /// code bellow is for each phase working seperately
- if (Q_DG1 > Qmax_phase) then
- begin
- Q_DG1 := Qmax_phase;
- end
- else
- if (Q_DG1 < Qmin_phase) then
- begin
- Q_DG1 := Qmin_phase;
- end;
- Curr1 := Conjg(Cdiv(Cmplx(P_dg1, Q_DG1), tempV1));
- if (Q_DG2 > Qmax_phase) then
- begin
- Q_DG2 := Qmax_phase;
- end
- else
- if (Q_DG2 < Qmin_phase) then
- begin
- Q_DG2 := Qmin_phase;
- end;
- Curr2 := Conjg(Cdiv(Cmplx(P_dg2, Q_DG2), tempV2));
- if (Q_DG3 > Qmax_phase) then
- begin
- Q_DG3 := Qmax_phase;
- end
- else
- if (Q_DG3 < Qmin_phase) then
- begin
- Q_DG3 := Qmin_phase;
- end;
- Curr3 := Conjg(Cdiv(Cmplx(P_dg3, Q_DG3), tempV3));
- /////////////////////////////////////////////////////
- end;
- Q_DG := Q_DG1 + Q_DG2 + Q_DG3;//
-
- Iabc[1] := Curr1; // Save for variable calcs
- Iabc[2] := Curr2;
- Iabc[3] := Curr3;
- {change direction}//added by dahei
- Iabc[1] := cnegate(Iabc[1]);
- Iabc[2] := cnegate(Iabc[2]);
- Iabc[3] := cnegate(Iabc[3]);
-
- end
- else
- if fnphases = 1 then
- begin
- //1-phase ctrl
-
- //tempV1 := Vabc[1]; // Save for variable calcs //assume Vabc[1][2][3] is ABC!
-
- V_DG2 := V_DG1; // Save for variable calcs, just in case of other use
- V_DG3 := V_DG1;
- // V_Theta1 := cang(V1);
-
- InDynamics := FALSE;
- // Guess at a new var output value
- case ctrl_mode of
- 1:
- begin
- temp_pref := P_dg1;//
- temp_qref := q_ref1;
- temp_vref := v_ref1;
- Alpha1 := pV_f_CC^[2]; //1 phase, only first one. coincident with dynamic calc
- temp_alpha := alpha1;
- end;
- 2:
- begin
- temp_pref := P_dg2;
- temp_qref := q_ref2;
- temp_vref := v_ref2;
- Alpha2 := pV_f_CC^[2];
- temp_alpha := alpha2;
- end;
- 3:
- begin
- temp_pref := P_dg3;
- temp_qref := q_ref3;
- temp_vref := v_ref3;
- Alpha3 := pV_f_CC^[2];
- temp_alpha := alpha3;
- end;
- end;
- if QV_flag = 0 then //P_ref, Q_ref
- begin
- Curr1 := Conjg(Cdiv(Cmplx(temp_pref, temp_qref), tempV1)); //currents A,B,C
- end
- else //P_ref, V_ref
- begin // QV_flag=1
-
- //phase 1
- //1 st ireration Iq := 0;
- if ActiveCircuit.Solution.Iteration = 1 then
- begin
- Iq1 := 0; //In power flow, start value of Iq for each power flow
- end;
- if cc_switch = FALSE then //droop
- begin
- ///////////integral droop
- dIqdt := kcq * (temp_vref - V_DG1) / ActiveCircuit.Solution.Iteration;
- if abs(V_ref1 - v_DG1) <= Volt_Trhd * V_ref1 then
- dIqdt := 0.0;
- Iq1 := Iq1 + dIqdt; //In power flow, Iq starts from 0;
- temp_qref := V_DG1 * Iq1;
- if droop = 2 then
- /////////////////}
- temp_qref := kcq_drp2 * (temp_vref - V_DG1) * 1000 * machinedata.kVArating / 0.05 / V_ref1;
- end
- else
- begin
- {gradient control}
-
- temp_qref := Qmax_phase * temp_alpha;
- end;
- {----------------}
-
- if (temp_qref > Qmax_phase) then // switch control mode to Q_ref control
- temp_qref := Qmax_phase
- else
- if (temp_qref < Qmin_phase) then
- temp_qref := Qmin_phase;
- Curr1 := Conjg(Cdiv(Cmplx(temp_pref, temp_qref), tempV1));
- case ctrl_mode of
- 1:
- begin
- //P_ref1 := temp_pref ;
- Q_DG1 := temp_qref;
- //v_ref1 := temp_vref;
- alpha1 := temp_alpha;
- end;
- 2:
- begin
- //P_ref2 := temp_pref;
- Q_DG2 := temp_qref;
- //v_ref2 := temp_vref ;
- alpha2 := temp_alpha;
- end;
- 3:
- begin
- //P_ref3 := temp_pref ;
- Q_DG3 := temp_qref;
- //v_ref3 := temp_vref ;
- alpha3 := temp_alpha;
- end;
- end;
-
- // else
- // begin
- //no consideration for 2-phase DG
- end;
- Iabc[1] := Curr1; // Save for variable calcs
- {change direction}//added by dahei
- Iabc[1] := cnegate(Iabc[1]);
- end; //phase =1
- end; //direct phase ctrl
- //InfoPublish; // publish data into fmonitor
-end;
-//*(*)//----------------------------------------------------------------------------
-procedure TGeneric5Obj.Randomize(Opt: Integer);
-//----------------------------------------------------------------------------
-
-// typical proc for handling randomization in DSS fashion
-
-begin
- case Opt of
- 0:
- RandomMult := 1.0;
- // GAUSSIAN: RandomMult := Gauss(YearlyShapeObj.Mean, YearlyShapeObj.StdDev);
- UNIfORM:
- RandomMult := Random; // number between 0 and 1.0
- // LOGNORMAL: RandomMult := QuasiLognormal(YearlyShapeObj.Mean);
- end;
-end;
-
-
-{-------------------------------------------------------------------------------------------------------------}
-procedure TGeneric5Obj.InitModel(V012, I012: TSymCompArray5);
-{-------------------------------------------------------------------------------------------------------------}
-
-// Init for Dynamics mode
-begin
- if ctrl_mode = 0 then //duplicate all codes as avg ctrl
- begin
- Id := P_DG / V_DG; //make sure V_DG has been calc beforehand
- Iq := Q_DG / V_DG; //change P_ref/Q_ref to P_DG/Q_DG dahei 1-16-18
- Idn := Id;
- Iqn := Iq;
- Id_ref := Id;// local; may need to be changed in futher
- Iq_ref := Iq;//
- // P_ref := Id_ref *v_DG;//local
- // V_ref := v_DG;//local
- {-initiate ABCD XY-}
- X_var^[1] := Id;
- X_var^[2] := Iq;
-
- dIddt := 0;
- dIqdt := 0;
- dIddtn := 0;
- dIqdtn := 0;
- dvi1dt := 0;
- dvi1dtn := 0;
- dvi2dt := 0;
- dvi2dtn := 0;
- // the global part
- end;
-end;
-
-procedure TGeneric5Obj.InitModelVIabc;
-{-------------------------------------------------------------------------------------------------------------}
-var
- cBuffer: pComplexArray;
-begin
- cBuffer := Allocmem(sizeof(cBuffer^[1]) * fnPhases);//define cBuffer
- GetPhasePower(cBuffer);
-
- P_DG1 := cBuffer^[1].re; //first phase or the only one
- Q_DG1 := cBuffer^[1].im;
- ///
- //InitModelVIabc(@Vabc, @Iabc); //
- Id1 := P_DG1 / V_DG1;
- Iq1 := Q_DG1 / V_DG1;
- {-initiate ABCD XY-}
- X_var^[1] := Id1;
- X_var^[2] := Iq1;
- if fnphases = 3 then //for 3 phase control the bellow is needed
- begin
- P_DG2 := cBuffer^[2].re;
- Q_DG2 := cBuffer^[2].im;
- P_DG3 := cBuffer^[3].re;
- Q_DG3 := cBuffer^[3].im;
- Id2 := P_DG2 / V_DG2; //
- Iq2 := Q_DG2 / V_DG2;
- Id3 := P_DG3 / V_DG3; //
- Iq3 := Q_DG3 / V_DG3;
- X_var^[3] := Id2;
- X_var^[4] := Iq2;
- X_var^[5] := Id3;
- X_var^[6] := Iq3;
- end;
- Reallocmem(cBuffer, 0);//free cBuffer
-end;
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.InitStateVars;
-//----------------------------------------------------------------------------
-
-var
- i: Integer;
- V012,
- I012: TSymCompArray5;
- Vabc, Iabc: array[1..3] of Complex;
- cBuffer: pComplexArray;
-
-begin
-
- YPrimInvalid := TRUE; // Force rebuild of YPrims
-
- with MachineData do
- begin
-
- {Compute nominal Positive sequence voltage behind transient reactance}
-
- if MachineON then
- with ActiveCircuit.Solution do
- begin
-
- Yeq := Cinv(Zsp);
-
- ComputeIterminal;
-
- case Fnphases of
-
- 1:
- begin
- //E1 := Csub( CSub(NodeV^[NodeRef^[1]], NodeV^[NodeRef^[2]]) , Cmul(ITerminal^[1], Zsp));
- for i := 1 to FNphases do
- Vabc[i] := NodeV^[NodeRef^[i]]; // Wye Voltage
- end;
-
- 3:
- begin
- // Calculate E1 based on Pos Seq only
- Phase2SymComp(ITerminal, @I012); // terminal currents
-
- // Voltage behind Zsp (transient reactance), volts
-
- for i := 1 to FNphases do
- Vabc[i] := NodeV^[NodeRef^[i]]; // Wye Voltage
- Phase2SymComp(@Vabc, @V012);
- //E1 := Csub( V012[1] , Cmul(I012[1], Zsp)); // Pos sequence
- end;
- else
- DoSimpleMsg(Format('Dynamics mode is implemented only for 1- or 3-phase Motors. IndMach012.' + name + ' has %d phases.', [Fnphases]), 5672);
- SolutionAbort := TRUE;
- end;
-
- dTheta := 0.0;
- w0 := Twopi * ActiveCircuit.Solution.Frequency;
- // recalc Mmass and D in case the frequency has changed
- with MachineData do
- begin
- Mmass := 2.0 * Hmass * kVArating * 1000.0 / (w0); // M = W-sec
- D := Dpu * kVArating * 1000.0 / (w0);
- end;
- Pshaft := 0 - Power[1].re;//P_DG;//Power[1].re; // Initialize Pshaft to present power consumption of motor
-
- //Speed := -LocalSlip * w0; // relative to synch speed
- dSpeed := 0.0;
- {}
- if DebugTrace then // Put in a separator record
- begin
- FSWriteln(TraceFile);
- FSWriteln(TraceFile, '*************** Entering Dynamics Mode ***********************');
- FSWriteln(TraceFile);
- FSFlush(Tracefile);
- end;
-
- end
- else
- begin
- Theta := 0.0;
- dTheta := 0.0;
- w0 := 0;
- Speed := 0.0;
- dSpeed := 0.0;
- {}
- //Id
- end;
- end; {With}
- ///
- /// from here, let us deal with ctrl_mode and everything related to control
-
- if ctrl_mode = 0 then //Pos seq contrl
- begin
- {}
- V_DG := Cabs(V012[1]);// Pos Seq Control
- Theta_DG := Cang(V012[1]);
- //P_DG := 0-Power[1].re/3.0; //1-terminal, for gen has only one terminal
- // div 3.0 ---% 1-11-2018. each phase
- P_DG := 0 - Power[1].re;
- //Q_DG := 0-Power[1].im/3.0;
- Q_DG := 0 - Power[1].im;
- //V_ref := V_DG ; //1;//
- P_ref := P_DG / 3;
- Q_ref := Q_DG / 3;
-
- InitModel(V012, I012); // E2, etc , Id Iq etc
- //init alpha array
-// if fmonobj <> nil then
-// fmonobj.Init_delay_array(ndNumincluster);
- // Shaft variables
- //Theta := Cang(E1) ;
- end
- else
- begin //ctrl_mode <> 0 =1,2,3,4
- //Vabc
- V_DG1 := cabs(Vabc[1]); // Save for variable calcs
- V_DG2 := cabs(Vabc[2]);
- V_DG3 := cabs(Vabc[3]);
- cBuffer := Allocmem(sizeof(cBuffer^[1]) * fnPhases);//define cBuffer
- GetPhasePower(cBuffer);
-
- P_DG1 := 0.0 - cBuffer^[1].re; //first phase or the only one
- Q_DG1 := 0.0 - cBuffer^[1].im;
- ///
- //InitModelVIabc(@Vabc, @Iabc); //
- Id1 := P_DG1 / V_DG1;
- Iq1 := Q_DG1 / V_DG1;
- {-initiate ABCD XY-}
- X_var^[1] := Id1;
- X_var^[2] := Iq1;
- if fnphases = 3 then //for 3 phase control the bellow is needed
- begin
- P_DG2 := 0.0 - cBuffer^[2].re;
- Q_DG2 := 0.0 - cBuffer^[2].im;
- P_DG3 := 0.0 - cBuffer^[3].re;
- Q_DG3 := 0.0 - cBuffer^[3].im;
- Id2 := P_DG2 / V_DG2; //
- Iq2 := Q_DG2 / V_DG2;
- Id3 := P_DG3 / V_DG3; //
- Iq3 := Q_DG3 / V_DG3;
- X_var^[3] := Id2;
- X_var^[4] := Iq2;
- X_var^[5] := Id3;
- X_var^[6] := Iq3;
- end;
- Reallocmem(cBuffer, 0);//free cBuffer
- end;
- //if QV_switch = 1 then //
- // begin
- //QV_flag := QV_flag_0;
- //QV_switch := 0;// wait next limit break
- // end;
- Update_PQlimits;
-end;
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
-
-{A typical helper function for PC elements to assist in the computation
- of Yprim
-}
-
-var
- Y, Yij, Yadder: Complex;
- i, j: Integer;
- FreqMultiplier: Double;
-
-begin
-
- FYprimFreq := ActiveCircuit.Solution.Frequency;
- FreqMultiplier := FYprimFreq / BaseFrequency; // ratio to adjust reactances for present solution frequency
-
- with ActiveCircuit.solution do
- if IsDynamicModel or IsHarmonicModel then
- // for Dynamics and Harmonics modes use constant equivalent Y
- begin
- if MachineON then
- Y := Yeq // L-N value computed in initialization routines
- else
- Y := Cmplx(EPSILON, 0.0);
-
- if Connection = 1 then
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
- Y.im := Y.im / FreqMultiplier; // adjust for frequency
- Yij := Cnegate(Y);
- for i := 1 to Fnphases do
- begin
- case Connection of
- 0:
- begin
- Ymatrix.SetElement(i, i, Y); // sets the element
- {
- Ymatrix.AddElement(Fnconds, Fnconds, Y); // sums the element
- Ymatrix.SetElemsym(i, Fnconds, Yij);
- }
- end;
- 1:
- begin {Delta connection}
- Yadder := CmulReal(Y, 1.000001); // to prevent floating delta
- Ymatrix.SetElement(i, i, Cadd(Y, Yadder)); // add a little bit to diagonal
- Ymatrix.AddElement(i, i, Y); // put it in again
- for j := 1 to i - 1 do
- Ymatrix.SetElemsym(i, j, Yij);
- end;
- end;
- end;
- end
-
- else
- begin
-
- // Typical code for a regular power flow model
- // Borrowed from Generator object
-
- {Yeq is typically expected as the equivalent line-neutral admittance}
-
- Y := Yeq; // Yeq is L-N quantity
-
- // ****** Need to modify the base admittance for real harmonics calcs
- Y.im := Y.im / FreqMultiplier;
-
- case Connection of
-
- 0:
- with YMatrix do
- begin // WYE
- for i := 1 to Fnphases do
- begin
- SetElement(i, i, Y);
- {
- AddElement(Fnconds, Fnconds, Y);
- SetElemsym(i, Fnconds, Yij);
- }
- end;
- end;
-
- 1:
- with YMatrix do
- begin // Delta or L-L
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
- Yij := Cnegate(Y);
- for i := 1 to Fnphases do
- begin
- j := i + 1;
- if j > Fnconds then
- j := 1; // wrap around for closed connections
- AddElement(i, i, Y);
- AddElement(j, j, Y);
- AddElemSym(i, j, Yij);
- end;
- end;
- end;
- end; {ELSE IF Solution.mode}
-
-end;
-
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.CalcYPrim;
-var
- i: Integer;
-
-begin
-
- if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Shunt = NIL) OR (Yprim_Series = NIL) {YPrimInvalid} then
- begin
- if YPrim_Shunt <> NIL then
- YPrim_Shunt.Free;
- YPrim_Shunt := TcMatrix.CreateMatrix(Yorder);
- if YPrim_Series <> NIL then
- Yprim_Series.Free;
- YPrim_Series := TcMatrix.CreateMatrix(Yorder);
- if YPrim <> NIL then
- YPrim.Free;
- YPrim := TcMatrix.CreateMatrix(Yorder);
- end
-
- else
- begin
- YPrim_Shunt.Clear;
- YPrim_Series.Clear;
- YPrim.Clear;
- end;
-
-
- // call helper routine to compute YPrim_Shunt
- CalcYPrimMatrix(YPrim_Shunt);
-
- // Set YPrim_Series based on a small fraction of the diagonals of YPrim_shunt
- // so that CalcVoltages doesn't fail
- // This is just one of a number of possible strategies but seems to work most of the time
- for i := 1 to Yorder do
- Yprim_Series.SetElement(i, i, CmulReal(Yprim_Shunt.Getelement(i, i), 1.0e-10));
-
- // copy YPrim_shunt into YPrim; That's all that is needed for most PC Elements
- YPrim.CopyFrom(YPrim_Shunt);
-
- // Account for Open Conductors -- done in base class
- inherited CalcYPrim;
-
-end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.DoGeneric5Model;
-//----------------------------------------------------------------------------
-{Compute total terminal Current }
-var
- i: Integer;
-
-begin
-
- CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
-
- CalcModel(Vterminal, Iterminal);
-
- //IterminalUpdated := TRUE;
- set_ITerminalUpdated(TRUE);
-
- for i := 1 to Nphases do
- Caccum(InjCurrent^[i], Cnegate(Iterminal^[i]));
- if (DebugTrace) then
- WriteTraceRecord;
-
-end;
-
-procedure TGeneric5Obj.CalcModel(V, I: pComplexArray); // given voltages returns currents
-
-var
- V012, I012: TSymCompArray5;
-begin
- if ctrl_mode = 0 then
- begin
- // Convert abc voltages to 012
- Phase2SymComp(V, @V012);
-
- // compute I012
-
- case ActiveCircuit.Solution.DynaVars.SolutionMode of
- DYNAMICMODE:
- begin
- CalcDynamic(V012, I012);
- end;
- else {All other modes are power flow modes}
- begin
- CalcPflow(V012, I012);
- end;
- end;
-
- SymComp2Phase(I, @I012); // convert back to I abc
- end // avg ctrl
- else //direct phase ctrl
- begin
- if fnphases = 3 then
- begin
- //3-phase ctrl
- // use Vterminal Iterminal directly instead of computing 120
-
- case ActiveCircuit.Solution.DynaVars.SolutionMode of
- DYNAMICMODE:
- begin
- CalcDynamicVIabc(V, I); //if ((ctrl_mode=4)and (fnphases=3))
- end;
- else {All other modes are power flow modes}
- begin
- CalcPflowVIabc(V, I); // //if ((ctrl_mode=4)and (fnphases=3))
- end;
- end;
- end
- else
- if fnphases = 1 then
- begin
- //1-phase ctrl
- // use Vterminal Iterminal directly instead of computing 120
- // actually there is no 120 for single phase
-
- case ActiveCircuit.Solution.DynaVars.SolutionMode of
- DYNAMICMODE:
- begin
- CalcDynamicVIabc(V, I); //if (fnphases=1)
- end;
- else {All other modes are power flow modes}
- begin
- CalcPflowVIabc(V, I); // //if (fnphases=1)
- end;
- end;
- end
- else
- begin
- //no consideration for 2-phase DG
- end;
- end;
- {--------pullish info--------}
- if ActiveCircuit.Solution.DynaVars.SolutionMode = DYNAMICMODE then
- begin
- //dynamode
- //if ActiveCircuit.Issolved = true then
- if fmonobj <> NIL then
- infoPublish;
- end
- else //power flow
- begin
- if fmonobj <> NIL then
- infoPublish;
- end;
-
- {----------------------------}
-end;
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.DoDynamicMode;
-//----------------------------------------------------------------------------
-
-{ This is an example taken from Generator illustrating how a PC element might
- handle Dynamics mode with a Thevenin equivalent
-
- Also illustrates the computation of symmetrical component values
-}
-
-{Compute Total Current and add into InjTemp}
-
-var
- i: Integer;
-
-begin
-
- // Start off by getting the current in the admittance branch of the model
- CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
-
- {Inj = -Itotal (in) - Yprim*Vtemp}
-
- CalcModel(Vterminal, Iterminal);
-
- //IterminalUpdated := TRUE;
- set_ITerminalUpdated(TRUE);
- for i := 1 to Nphases do
- Caccum(InjCurrent^[i], Cnegate(ITerminal^[i]));
-
-end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-{Do not support Harmonic for now}
-procedure TGeneric5Obj.DoHarmonicMode;
-
-{
- Example taken from Generator illustrating how a PC element might handle
- current calcs for Harmonics mode
-
- Note: Generator objects assume a Thevenin model (voltage behind and impedance)
- while Load objects assume the Spectrum applies to a Norton model injection current
-}
-
-{Compute Injection Current Only when in harmonics mode}
-
-{Assumes spectrum is a voltage source behind subtransient reactance and YPrim has been built}
-{Vd is the fundamental frequency voltage behind Xd" for phase 1}
-
-var
- i: Integer;
- E: Complex;
- GenHarmonic: Double;
-
-begin
-
- // Set the VTerminal array
- ComputeVterminal;
-
- with ActiveCircuit.Solution do
- begin
- GenHarmonic := Frequency / BaseFrequency; // harmonic based on the fundamental for this object
- // get the spectrum multiplier and multiply by the V thev (or Norton current for load objects)
- // ??? E := CmulReal(SpectrumObj.GetMult(GenHarmonic), VThevHarm); // Get base harmonic magnitude
- // ??? RotatePhasorRad(E, GenHarmonic, ThetaHarm); // Time shift by fundamental frequency phase shift
-
- // Put the values in a temp complex buffer
- for i := 1 to Fnphases do
- begin
- cBuffer[i] := E;
- if i < Fnphases then
- RotatePhasorDeg(E, GenHarmonic, -120.0); // Assume 3-phase IndMach012
- end;
- end;
-
- {Handle Wye Connection}
- if Connection = 0 then
- cbuffer[Fnconds] := Vterminal^[Fnconds]; // assume no neutral injection voltage
-
- // In this case the injection currents are simply Yprim(frequency) times the voltage buffer
- // Refer to Load.Pas for load-type objects
- {Inj currents = Yprim (E) }
- YPrim.MVMult(InjCurrent, @cBuffer);
-
-end;
-
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TGeneric5Obj.CalcGeneric5ModelContribution;
-
-// Main dispatcher for computing PC Element currnts
-
-// Calculates IndMach012 current and adds it properly into the injcurrent array
-// routines may also compute ITerminal (ITerminalUpdated flag)
-
-begin
- //IterminalUpdated := FALSE;
- set_ITerminalUpdated(FALSE);
- with ActiveCircuit, ActiveCircuit.Solution do
- begin
- if IsDynamicModel then
- DoDynamicMode
- else
- if IsHarmonicModel and (Frequency <> Fundamental) then
- DoHarmonicMode
- else
- DoGeneric5Model;
-
- end; {WITH}
-
- {When this is done, ITerminal is up to date}
-
-end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TGeneric5Obj.CalcInjCurrentArray;
-//----------------------------------------------------------------------------
-
-// Main procedure for controlling computation of InjCurrent array
-
-// InjCurrent is difference between currents in YPrim and total terminal current
-
-
-begin
-
-// You usually will want some logic like this
-
- // If the element is open, just zero the array and return
- if Generic5SwitchOpen then
- ZeroInjCurrent
-
- // otherwise, go to a routine that manages the calculation
- else
- CalcGeneric5ModelContribution;
-
-end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TGeneric5Obj.GetTerminalCurrents(Curr: pComplexArray);
-//----------------------------------------------------------------------------
-
-// This function controls the calculation of the total terminal currents
-
-// Note that it only does something if the solution count has changed.
-// Otherwise, Iterminal array already contains the currents
-
-
-begin
-
- with ActiveCircuit.Solution do
- begin
- if IterminalSolutionCount <> ActiveCircuit.Solution.SolutionCount then
- begin // recalc the contribution
- // You will likely want some logic like this
- if not Generic5SwitchOpen then
- CalcGeneric5ModelContribution; // Adds totals in Iterminal as a side effect
- end;
- inherited GetTerminalCurrents(Curr); // add in inherited contribution
- end;
-
-end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-function TGeneric5Obj.InjCurrents: Integer;
-//----------------------------------------------------------------------------
-
-// Required function for managing computing of InjCurrents
-
-begin
-
- with ActiveCircuit.Solution do
- begin
-
- // Generators and Loads use logic like this:
- if LoadsNeedUpdating then
- SetNominalPower; // Set the nominal kW, etc for the type of solution being done
-
- // call the main function for doing calculation
- CalcInjCurrentArray; // Difference between currents in YPrim and total terminal current
-
- // If (DebugTrace) Then WriteTraceRecord;
-
- // Add into System Injection Current Array
- Result := inherited InjCurrents;
-
- end;
-
-end;
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.SetNominalPower;
-//----------------------------------------------------------------------------
-// Set shaft power
-var
- Factor: Double;
- MachineOn_Saved: Boolean;
-
-begin
- MachineOn_Saved := MachineON;
- ShapeFactor := CDOUBLEONE;
- // Check to make sure the generation is ON
- with ActiveCircuit, ActiveCircuit.Solution do
- begin
- if not (IsDynamicModel or IsHarmonicModel) then // Leave machine in whatever state it was prior to entering Dynamic mode
- begin
- MachineON := TRUE; // Init to on then check if it should be off
- end;
-
-
- if not MachineON then
- begin
- // If Machine is OFF enter as tiny resistive load (.0001 pu) so we don't get divide by zero in matrix
- MachineData.Pnominalperphase := -0.1 * kWBase / Fnphases;
- // Pnominalperphase := 0.0;
- MachineData.Qnominalperphase := 0.0; // This really doesn't matter
- end
- else
- begin // Generator is on, compute it's nominal watts and vars
- with Solution do
-
- case Mode of
- SNAPSHOT:
- Factor := 1.0;
- DAILYMODE:
- begin
- Factor := 1.0;
- CalcDailyMult(DynaVars.dblHour) // Daily dispatch curve
- end;
- YEARLYMODE:
- begin
- Factor := 1.0;
- CalcYearlyMult(DynaVars.dblHour);
- end;
- DUTYCYCLE:
- begin
- Factor := 1.0;
- CalcDutyMult(DynaVars.dblHour);
- end;
- GENERALTIME, // General sequential time simulation
- DYNAMICMODE:
- begin
- Factor := 1.0;
- // This mode allows use of one class of load shape
- case ActiveCircuit.ActiveLoadShapeClass of
- USEDAILY:
- CalcDailyMult(DynaVars.dblHour);
- USEYEARLY:
- CalcYearlyMult(DynaVars.dblHour);
- USEDUTY:
- CalcDutyMult(DynaVars.dblHour);
- else
- ShapeFactor := CDOUBLEONE // default to 1 + j1 if not known
- end;
- end;
- MONTECARLO1,
- MONTEFAULT,
- FAULTSTUDY:
- Factor := 1.0;
- MONTECARLO2,
- MONTECARLO3,
- LOADDURATION1,
- LOADDURATION2:
- begin
- Factor := 1.0;
- CalcDailyMult(DynaVars.dblHour);
- end;
- PEAKDAY:
- begin
- Factor := 1.0;
- CalcDailyMult(DynaVars.dblHour);
- end;
- AUTOADDFLAG:
- Factor := 1.0;
- else
- Factor := 1.0
- end;
-
- if not (IsDynamicModel or IsHarmonicModel) then //******
- begin
- if ShapeIsActual then
- MachineData.Pnominalperphase := 1000.0 * ShapeFactor.re / Fnphases
- else
- MachineData.Pnominalperphase := 1000.0 * kWBase * Factor * ShapeFactor.re / Fnphases;
-
- // cannot dispatch vars in induction machine
- // you get what you get
-
- end;
- end; {ELSE GenON}
-
- end; {With ActiveCircuit}
-
- // If machine state changes, force re-calc of Y matrix
- if MachineON <> MachineOn_Saved then
- YPrimInvalid := TRUE;
-
-end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TGeneric5Obj.CalcDailyMult(Hr: Double);
-//----------------------------------------------------------------------------
-
-begin
- if (DailyDispShapeObj <> NIL) then
- begin
- ShapeFactor := DailyDispShapeObj.GetMult(Hr);
- ShapeIsActual := DailyDispShapeObj.UseActual;
- end
- else
- ShapeFactor := CDOUBLEONE; // Default to no daily variation
-end;
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.CalcDutyMult(Hr: Double);
-//----------------------------------------------------------------------------
-
-begin
- if DutyShapeObj <> NIL then
- begin
- ShapeFactor := DutyShapeObj.GetMult(Hr);
- ShapeIsActual := DutyShapeObj.UseActual;
- end
- else
- CalcDailyMult(Hr); // Default to Daily Mult if no duty curve specified
-end;
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.CalcYearlyMult(Hr: Double);
-//----------------------------------------------------------------------------
-
-begin
-{Yearly curve is assumed to be hourly only}
- if YearlyShapeObj <> NIL then
- begin
- ShapeFactor := YearlyShapeObj.GetMult(Hr);
- ShapeIsActual := YearlyShapeObj.UseActual;
- end
- else
- ShapeFactor := CDOUBLEONE; // Defaults to no variation
-
-end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TGeneric5Obj.GetInjCurrents(Curr: pComplexArray);
-//----------------------------------------------------------------------------
-
-// Gets the currents for the last solution performed
-
-// Do not call anything that may change the basic element values from the last solution
-
-var
- i: Integer;
-
-begin
-
- CalcInjCurrentArray; // Difference between currents in YPrim and total current
-
- try // an exception here generally means an array boundary overrun
- // Copy into buffer array
- for i := 1 to Yorder do
- Curr^[i] := InjCurrent^[i];
-
- except
- ON E: Exception do
- DoErrorMsg('IndMach012 Object: "' + Name + '" in GetInjCurrents function.',
- E.Message,
- 'Current buffer not big enough.', 568);
- end;
-
-end;
-//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TGeneric5Obj.DumpProperties(F: TFileStream; Complete: Boolean);
-//----------------------------------------------------------------------------
-{
- This procedure is require to respond to various commands such as Dump that
- write all the device's property values to a file.
-}
-
-var
- i, idx: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- {Write out any specials here, usually preceded by a "!"}
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- idx := PropertyIdxMap[i]; // Map to get proper index into property value array
- case idx of
- {Trap any specials here, such as values that are array properties, for example}
- 34, 36:
- FSWriteln(F, '~ ' + PropertyName^[i] + '=(' + PropertyValue[idx] + ')')
- else
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[idx]);
- end;
- end;
-
- FSWriteln(F);
-
-end;
-
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.InitHarmonics;
-//----------------------------------------------------------------------------
-
-{Procedure to initialize for Harmonics solution}
-
-{This example is extracted from Generator and constructs a Thevinen equivalent.
- Refer to Load for how to do a Norton equivalent
- }
-
-//Var
-// E, Va:complex;
-begin
-
- YPrimInvalid := TRUE; // Force rebuild of YPrims
-
-(****
- GenFundamental := ActiveCircuit.Solution.Frequency ; // Whatever the frequency is when we enter here.
-
- With GenVars Do Begin
-
- // Xd" is used for harmonics analysis for generators
- Yeq := Cinv(Cmplx(0.0, Xdpp)); // used for current calcs Always L-N
-
- {Compute reference Thevinen voltage from phase 1 current}
-
- IF GenON Then
- Begin
-
- ComputeIterminal; // Get present value of current
-
- With ActiveCircuit.solution Do
- Case Connection of
- 0: Begin {wye - neutral is explicit}
- Va := Csub(NodeV^[NodeRef^[1]], NodeV^[NodeRef^[Fnconds]]);
- End;
- 1: Begin {delta -- assume neutral is at zero}
- Va := NodeV^[NodeRef^[1]];
- End;
- End;
-
- E := Csub(Va, Cmul(Iterminal^[1], cmplx(0.0, Xdpp)));
- Vthevharm := Cabs(E); // establish base mag and angle
- ThetaHarm := Cang(E);
- End
- ELSE Begin
- // If Generator is off, just set to zero
- Vthevharm := 0.0;
- ThetaHarm := 0.0;
- End;
- End;
- ***)
-end;
-
-// ******************* PROPERTY VALUES *******************
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.InitPropertyValues(ArrayOffset: Integer);
-//----------------------------------------------------------------------------
-
-// required procedure to initialize the string value of the properties
-
-begin
- // Some examples
- PropertyValue[1] := '3'; //'phases';
- PropertyValue[2] := Getbus(1); //'bus1';
- PropertyValue[3] := '12.47';
- PropertyValue[4] := '100';
- PropertyValue[5] := '.80';
- PropertyValue[6] := 'Delta';
- PropertyValue[7] := Format('%-g', [MachineData.kVARating]);
- PropertyValue[8] := Format('%-g', [MachineData.Hmass]);
- PropertyValue[9] := Format('%-g', [MachineData.D]);
- PropertyValue[10] := '0.0053';
- PropertyValue[11] := '0.106';
- PropertyValue[12] := '0.007';
- PropertyValue[13] := '0.12';
- PropertyValue[14] := '4.0';
-
- PropertyValue[15] := '0.007';
- PropertyValue[16] := '0.1';
- PropertyValue[17] := 'variable';
-
- PropertyValue[18] := '';
- PropertyValue[19] := '';
- PropertyValue[20] := ''; {...}
- PropertyValue[21] := 'NO';
- PropertyValue[24] := '1';//GrpNum
- PropertyValue[25] := '1';//V_ref
- PropertyValue[26] := '0';//control mode
-
-{Call inherited function to init inherited property values}
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-
-//----------------------------------------------------------------------------
-function TGeneric5Obj.GetPropertyValue(Index: Integer): String;
-//----------------------------------------------------------------------------
-
-// Return i-th property value as a string
-
-begin
-
- Result := ''; // Init the string
- case Index of
- // Put special cases here
- // often a good idea to convert numeric values to strings, for example
- 4:
- Result := Format('%.6g', [kWBase]);
- 5:
- Result := Format('%.6g', [PowerFactor(Power[1])]);
- 7:
- Result := Format('%.6g', [MachineData.kVArating]);
- 8:
- Result := Format('%.6g', [MachineData.Hmass]);
- 9:
- Result := Format('%.6g', [MachineData.D]);
- //15: Result := Format('%.6g', [localslip]);
- 18:
- Result := YearlyShape;
- 19:
- Result := DailyDispShape;
- 20:
- Result := DutyShape;
- {}
- 24:
- Result := Format('%d', [Cluster_Num]);
- {...}
- else
-
- // The default is to just return the current string value of the property
- Result := inherited GetPropertyValue(index);
-
- end;
-end;
-
-// ******************* END PROPERTY VALUES *******************
-
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.IntegrateStates;
-//----------------------------------------------------------------------------
-
-{
- This is a virtual function. You do not need to write this routine
- if you are not integrating state variables in dynamics mode.
-}
-
-// Integrate state variables for Dynamics analysis
-// Example from Generator
-
-// Illustrates use of debug tracing
-
-// Present technique is a predictor-corrector trapezoidal rule
-
-var
- TracePower: Complex;
-
-
-begin
- // Compute Derivatives and then integrate
-
- ComputeIterminal;
-
- with ActiveCircuit.Solution, MachineData do
- begin
-
- with DynaVars do
- if (IterationFlag = 0) then
- begin {First iteration of new time step}
- //ThetaHistory := Theta + 0.5*h*dTheta;
- //SpeedHistory := Speed + 0.5*h*dSpeed;
- end;
-
- // Compute shaft dynamics
- TracePower := TerminalPowerIn(Vterminal, Iterminal, FnPhases); // in watts
- //dSpeed := (TracePower.re - Pshaft - abs(D*Speed)) / Mmass;
- //dTheta := Speed ;
- Pshaft := P_DG; // P_DG is calculated in CalcDynamic or CalcDynamicVIabc
- //
- // Trapezoidal method
- with DynaVars do
- begin
- //Speed := SpeedHistory + 0.5*h*dSpeed;
- //Theta := ThetaHistory + 0.5*h*dTheta;
- end;
-
- if DebugTrace then
- WriteTraceRecord;
-
- IntegrateABCD;
- //Integrate;
- end;
-end;
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.Get_DynamicModelCurrent;
-//----------------------------------------------------------------------------
-var
- temp: Double;
-begin
- if Id = 0.0 then
- temp := pi / 2
- else
- temp := arctan(Iq / Id);
- Is1 := PCLX(sqrt(Iq * Iq + Id * Id), Theta_DG - temp);//with respect to Q_axis
- //Is1 := cmul(Is1,cmplex());// Put this into XY domine
- Is1 := Cdivreal(Is1, 3.0); //here we need to divide all values back to Network
- //Is1 := Cdiv(Csub(V1, E1),Zsp); // I = (V-E')/Z'
- //Is2 := Cdiv(Csub(V2, E2),Zsp); // I = (V-E')/Z'
- Is2 := cmplx(0, 0); //force balance
- // rotor current Ir1= Is1-Vm/jXm
- Ir1 := Is1;
- //Ir1 := Csub(Is1 ,Cdiv( Csub(V1, cmul(Is1, Zsp)), Zm ));
- //Ir2 := Csub(Is2 ,Cdiv( Csub(V2, cmul(Is2, Zsp)), Zm ));
- Ir2 := cmplx(0, 0);
-end;
-
-
-//----------------------------------------------------------------------------
-// ********************** VARIABLES ***************************************
-//----------------------------------------------------------------------------
-
-//----------------------------------------------------------------------------
-function TGeneric5Obj.NumVariables: Integer;
-//----------------------------------------------------------------------------
-{
- Return the number of state variables
-
- This is a virtual function. You do not need to write this routine
- if you are not defining state variables.
- Note: it is not necessary to define any state variables
-}
-
-begin
- Result := NumGeneric5Variables;
-end;
-
-
-//----------------------------------------------------------------------------
-function TGeneric5Obj.VariableName(i: Integer): String;
-//----------------------------------------------------------------------------
-
-{
- Returns the i-th state variable in a string
-
- This is a virtual function. You do not need to write this routine
- if you are not defining state variables.
-}
-
-begin
- if i < 1 then
- Exit; // This means Someone goofed
- case i of
- 1:
- Result := 'V_DG';//pos seq value
- 2:
- Result := 'P_DG';
- 3:
- Result := 'Q_DG';
- 4:
- Result := 'V_DG1';//Phase A or the first phase if there are less than 3
- 5:
- Result := 'P_DG1';
- 6:
- Result := 'Q_DG1';
- 7:
- Result := 'V_DG2';//Phase B if exists
- 8:
- Result := 'P_DG2';
- 9:
- Result := 'Q_DG2';
- 10:
- Result := 'V_DG3';//phase C if exists
- 11:
- Result := 'P_DG3';
- 12:
- Result := 'Q_DG3';
- 13:
- Result := 'Qmax';
- 14:
- Result := 'Qmax_Phase';
- 15:
- Result := 'Pmax';
- 16:
- Result := 'Pmax_Phase';
- 17:
- Result := 'Alpha';
- 18:
- Result := 'Alpha1';
- 19:
- Result := 'Alpha2';
- 20:
- Result := 'Alpha3';
- 21:
- Result := 'AlphaP';
- 22:
- Result := 'AlphaP1';
- 23:
- Result := 'AlphaP2';
- 24:
- Result := 'AlphaP3';
- 25:
- Result := 'V_ref'; //Voltage object
- 26:
- Result := 'kVA'; //kVArating
- 27:
- Result := 'kW'; //kVArating
- 28:
- Result := 'cluster_num';
- 29:
- Result := 'NdNumInCluster';
- 30:
- Result := 'ctrl_mode';
- 31:
- Result := 'Gradient';
- 32:
- Result := 'Id';
- 33:
- Result := 'Iq';
- 34:
- Result := 'P_set';
- 35:
- Result := 'Frequency';
- 36:
- Result := 'Defense';
- else
- end;
-
-end;
-
-//----------------------------------------------------------------------------
-function TGeneric5Obj.Get_Variable(i: Integer): Double;
-//----------------------------------------------------------------------------
-begin
-
- Result := -9999.99; // Error Value
-
- //With MachineData Do
- case i of
- 1:
- Result := V_DG;
- 2:
- Result := P_DG / 1000;//kW
- 3:
- Result := Q_DG / 1000;
- 4:
- Result := V_DG1;//Phase A or the first phase if there are less than 3 phases
- 5:
- Result := P_DG1 / 1000;
- 6:
- Result := Q_DG1 / 1000;
- 7:
- Result := V_DG2;//Phase B if exists
- 8:
- Result := P_DG2 / 1000;
- 9:
- Result := Q_DG2 / 1000;
- 10:
- Result := V_DG3;//Phase c if exists
- 11:
- Result := P_DG3 / 1000;
- 12:
- Result := Q_DG3 / 1000;
- 13:
- Result := Qmax / 1000;
- 14:
- Result := Qmax_Phase / 1000;
- 15:
- Result := Pmax / 1000;
- 16:
- Result := Pmax_Phase / 1000;
- 17:
- Result := alpha;
- 18:
- Result := Alpha1;
- 19:
- Result := Alpha2;
- 20:
- Result := Alpha3;
- 21:
- Result := alphaP;
- 22:
- Result := AlphaP1;
- 23:
- Result := AlphaP2;
- 24:
- Result := AlphaP3;
- 25:
- Result := V_ref;
- 26:
- Result := MachineData.kVArating / 1000;
- 27:
- Result := kWbase / 1000;
- 28:
- Result := cluster_num;
- 29:
- Result := NdNumInCluster;
- 30:
- Result := ctrl_mode;
- 31:
- Result := Gradient;
- 32:
- Result := Id;
- 33:
- Result := Iq;
- 34:
- Result := P_ref * 3.0;
- 35:
- begin
- freq := ActiveCircuit.solution.Frequency;
- if fmonobj <> NIL then
- freq := freq + fmonobj.omg_fm;//fmonobj.comp_omg;//fmonobj.omg_fm; //
- Result := freq;
- end;
- 36:
- begin
- result := 0.0;
- if fmonobj <> NIL then
- Result := z_dfs_plot;
- end;
- else
-
- end;
-
-end;
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.Set_Variable(i: Integer; Value: Double);
-//----------------------------------------------------------------------------
-
-begin
- case i of
- 1:
- V_DG := Value;
- 2:
- P_DG := Value;
- 3:
- Q_DG := Value;
- 4:
- V_DG1 := Value;//Phase A or the first phase if there are less than 3 phases
- 5:
- P_DG1 := Value;
- 6:
- Q_DG1 := Value;
- 7:
- V_DG2 := Value;//Phase B if exists
- 8:
- P_DG2 := Value;
- 9:
- Q_DG2 := Value;
- 10:
- V_DG3 := Value;//Phase c if exists
- 11:
- P_DG3 := Value;
- 12:
- Q_DG3 := Value;
- 13:
- Qmax := Value;
- 14:
- Qmax_Phase := Value;
- 15:
- Pmax := Value;
- 16:
- Pmax_Phase := Value;
- 17:
- alpha := Value;
- 18:
- Alpha1 := Value;
- 19:
- Alpha2 := Value;
- 20:
- Alpha3 := Value;
- 21:
- alphaP := Value;
- 22:
- alphaP1 := Value;
- 23:
- alphaP2 := Value;
- 24:
- alphaP3 := Value;
- 25:
- V_ref := Value;
- 26:
- MachineData.kVArating := Value;
- 27:
- kWbase := Value;
- 28:
- begin
-
- TPCElement(self).cluster_num := trunc(Value);
-
-
- //if cluster_num >= 1 then // assign the virtue leader to this DG
- //begin
- //FMonObj := ActiveCircuit.Fmonitors.Get(cluster_num); // it works only if cluster_num starts from 1 and being consecutively
-
- // move this piece of codes to Fmonitor, InitFM
- //ActiveCircuit.Fmonitors.First;
- //FMonObj := ActiveCircuit.Fmonitors.Active;
- //if FMonObj. then
-
-
- //end;
-
- //if function 'get' fails , return nil
- end;
- 29:
- TPCElement(self).NdNumInCluster := trunc(Value);
- 30:
- TPCElement(self).nVLeaders := trunc(Value);
- 31:
- TPCElement(self).cluster_num2 := trunc(Value);
- 32:
- TPCElement(self).NdNumInCluster2 := trunc(Value);
- else
- {Do Nothing for other variables: they are read only}
- end;
-end;
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.GetAllVariables(States: pDoubleArray);
-//----------------------------------------------------------------------------
-{
- Return all state variables in double array (allocated by calling function)
-
- This is a virtual function. You do not need to write this routine
- if you are not defining state variables.
-}
-var
- i: Integer;
- //N:Integer;
-begin
-// N := 0;
- for i := 1 to NumGeneric5Variables do
- States^[i] := Variable[i];
-end;
-
-// ********************** END VARIABLES ***************************************
-
-
-//----------------------------------------------------------------------------
-function TGeneric5Obj.GetRotorLosses: Double;
-//----------------------------------------------------------------------------
-begin
- Result := 3.0 * (Sqr(Ir1.re) + Sqr(Ir1.im) + Sqr(Ir2.re) + Sqr(Ir2.im)) * Zr.re;
-end;
-
-//----------------------------------------------------------------------------
-function TGeneric5Obj.GetStatorLosses: Double;
-//----------------------------------------------------------------------------
-begin
- Result := 3.0 * (Sqr(Is1.re) + Sqr(Is1.im) + Sqr(Is2.re) + Sqr(Is2.im)) * Zs.re;
-end;
-
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.MakePosSequence;
-//----------------------------------------------------------------------------
-
-{
- This is a virtual function. You do not need to write this routine
- if the base class function will suffice.
-}
-
-// Routine to convert existing three-phase models to a single-phase positive-
-// sequence model
-
-var
- S: String;
-// V :Double;
-
-begin
-
-{
- The usual technique is to create a new property editing string
- based on the present values of properties. Once the string is
- created, it is pushed into the Parser and the Edit routine for this
- class is invoked.
-
- Thus, the positive sequence model is created in memory. Do a
- "Save Circuit" command to save the model that is created. Some
- editing of the resulting scripts will likely be required. Not all
- elements have an obvious positive sequence equivalent.
-}
-
-
- // example from Generator class
- // Modify as necessary
-
- S := 'Phases=1 conn=wye'; // Positive sequence model is 1-phase wye
-
- (****
-
- // Make sure voltage is line-neutral
- If (Fnphases>1) or (connection<>0) Then V := GenVars.kVGeneratorBase/SQRT3
- Else V := GenVars.kVGeneratorBase;
-
- S := S + Format(' kV=%-.5g',[V]);
-
- // Divide the load by no. phases
- If Fnphases>1 Then
- Begin
- S := S + Format(' kW=%-.5g PF=%-.5g',[kWbase/Fnphases, PFNominal]);
- If (PrpSequence^[19]<>0) or (PrpSequence^[20]<>0) Then S := S + Format(' maxkvar=%-.5g minkvar=%-.5g',[kvarmax/Fnphases, kvarmin/Fnphases]);
- If PrpSequence^[26]>0 Then S := S + Format(' kva=%-.5g ',[genvars.kvarating/Fnphases]);
- If PrpSequence^[27]>0 Then S := S + Format(' MVA=%-.5g ',[genvars.kvarating/1000.0/Fnphases]);
- End;
-
- Parser.CmdString := S; // Push the string into the Parser object
- Edit; // Invoke the Edit method for this class
-
- inherited; // sets the terminal bus references, must do after editing number of phases
-
- ***)
-
-end;
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.Set_ConductorClosed(Index: Integer; Value: Boolean);
-//----------------------------------------------------------------------------
-
-// Routine for handling Open/Close procedures
-
-begin
- inherited;
-
- if Value then
- Generic5SwitchOpen := FALSE
- else
- Generic5SwitchOpen := TRUE;
-
-end;
-
-
-//----------------------------------------------------------------------------
-{procedure TGeneric5Obj.set_Localslip(const Value: Double);
-//----------------------------------------------------------------------------
-
- Function Sign(const x:Double):Double;
- Begin If x<0.0 then Result := -1.0 Else Result := 1.0; End;
-
-begin
- S1 := Value;
- If Not InDynamics Then If Abs(S1)>MaxSlip Then S1 := Sign(S1)*MaxSlip; // Put limits on the slip unless dynamics
- S2 := 2.0 - S1;
-end;
- }
-//----------------------------------------------------------------------------
-{procedure TGeneric5Obj.Set_Slip(const Value: Double);
-//----------------------------------------------------------------------------
-begin
- LocalSlip := Value;
- MachineData.Speed := MachineData.w0 * (-S1); // make motor speed agree
-end;
-}
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.InitTraceFile;
-//----------------------------------------------------------------------------
-begin
- FreeAndNil(TraceFile);
- TraceFile := TFileStream.Create(OutputDirectory + Format('%s_IndMach012_Trace.CSV', [Name]), fmCreate);
-
- FSWrite(TraceFile, 'Time, Iteration, S1, |IS1|, |IS2|, |E1|, |dE1dt|, |E2|, |dE2dt|, |V1|, |V2|, Pshaft, Pin, Speed, dSpeed');
- FSWriteln(TraceFile);
-
- FSFlush(TraceFile);
-end;
-
-//----------------------------------------------------------------------------
-procedure TGeneric5Obj.WriteTraceRecord;
-//----------------------------------------------------------------------------
-begin
- with ActiveCircuit.Solution do
- //Write(TraceFile, Format('%-.6g, %d, %-.6g, ',[Dynavars.dblHour*3600.0, Iteration, S1]));
-
- FSWrite(TraceFile, Format('%-.6g, %-.6g, ', [Cabs(Is1), Cabs(Is2)]));
- //Write(TraceFile, Format('%-.6g, %-.6g, %-.6g, %-.6g, ', [Cabs(E1), Cabs(dE1dt), Cabs(E2), Cabs(dE2dt)]));
- FSWrite(TraceFile, Format('%-.6g, %-.6g, ', [Cabs(V1), Cabs(V2)]));
- FSWrite(TraceFile, Format('%-.6g, %-.6g, ', [MachineData.Pshaft, power[1].re]));
- FSWrite(TraceFile, Format('%-.6g, %-.6g, ', [MachineData.speed, MachineData.dSpeed]));
-
- FSWriteln(TraceFile);
-
- FSFlush(TraceFile);
-end;
-
-initialization
-
-// Initialize any variables here
-
-
- // For Example: 1 + j 1
-
- CDOUBLEONE := CMPLX(1.0, 1.0);
-
-
-end.
diff --git a/src/PCElements/IndMach012.pas b/src/PCElements/IndMach012.pas
index 8aab34107..c3d83225a 100644
--- a/src/PCElements/IndMach012.pas
+++ b/src/PCElements/IndMach012.pas
@@ -11,97 +11,77 @@
----------------------------------------------------------
}
-{ Change Log
-
- November 10, 2016
-
- Created by
- Andres Ovalle
- Celso Rocha
-
-}
-
-{
- Description
-
- This is a Power Converstion (PC) element.
-
- PC elements are Load, Generator, Vsource, Isource, etc. PC elements are
- used to model devices that convert the power delivered by the Power Delivery (PD)
- elements into some other form. PC elements are generally considered to be
- in shunt with the power system and are the terminations of the power flow
- while PD elements are considered to be in series with the power flow path.
-
- Both PC and PD elements are represpented by their primitive Y matrices. PC elements
- are also used to model the nonlinear devices in the system (see the Load model). They
- differ from PD elements in that they have a current injection source in parallel with
- the primitive Y matrix.
-
-}
-
+// Change Log
+//
+// November 10, 2016
+//
+// Created by
+// Andres Ovalle
+// Celso Rocha
+//
interface
-{Add other modules accessed by this class}
-
uses
Classes,
- DSSClass, // Base class for most DSS objects
- PCClass, // Base class for collection manager for PC elements
- PCElement, // Base class for PC Elements
- ucmatrix, // Unit for managing complex matrice (for Yprim, etc)
- ucomplex, // Complex math functions, type definitions
- ArrayDef, // definitions of basic DSS arrays
-
- // common modules used in PC elements
- LoadShape, // class for supporting/representing loadshapes
- GrowthShape, // Class for holding growth shapes
- Spectrum, // Definitions for harmonic spectra
+ DSSClass,
+ PCClass,
+ PCElement,
+ ucmatrix,
+ UComplex, DSSUcomplex,
+ ArrayDef,
+ LoadShape,
+ GrowthShape,
+ Spectrum,
Dynamics,
- GeneratorVars; // for elements that interact with dynamics variables
-
+ Generator;
type
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
-
-{ Collection manager for this class of element }
- TIndMach012 = class(TPCClass) { Notes Andres: -- definition of the class -- }
- PRIVATE
-
- {These private functions are generally helper functions for Edit procedure}
-
- { A typical function }
- procedure SetNcondsForConnection;
-
+{$SCOPEDENUMS ON}
+ TIndMach012Prop = (
+ INVALID = 0,
+ phases = 1,
+ bus1 = 2,
+ kv = 3,
+ kW = 4,
+ pf = 5,
+ conn = 6,
+ kVA = 7,
+ H = 8,
+ D = 9,
+ puRs = 10,
+ puXs = 11,
+ puRr = 12,
+ puXr = 13,
+ puXm = 14,
+ Slip = 15,
+ MaxSlip = 16,
+ SlipOption = 17,
+ Yearly = 18,
+ Daily = 19,
+ Duty = 20,
+ Debugtrace = 21
+ );
+{$SCOPEDENUMS OFF}
+
+ TIndMach012 = class(TPCClass)
PROTECTED
- procedure DefineProperties; // Define the property names and help strings
- function MakeLike(const OtherIndMach012Name: String): Integer; OVERRIDE; // copy properties of another similar object
+ cBuffer: TCBuffer24; // Temp buffer for complex math calcs; allows up to 24-phase models.
+ procedure DefineProperties; override; // Define the property names and help strings
PUBLIC
-
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // Definition of the main property editing function
-
-
- function NewObject(const ObjName: String): Integer; OVERRIDE; // This function is called by the DSS New command
-
- {any public functions that might be called from other elements}
-
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE; // This function is called by the DSS New command
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
-
-{ Class definition for this class of element}
TSymCompArray = array[0..2] of Complex;
- //pTDynamicsRec = ^TDynamicsRec;
- //pTGeneratorVars = ^TGeneratorVars;
TIndMach012Obj = class(TPCElement)
PRIVATE
-
- {Private variables of this class}
+ {Private variables of this class}
Connection: Integer; {0 = line-neutral; 1=Delta}
Yeq: Complex; // Y at nominal voltage
@@ -128,35 +108,30 @@ TIndMach012Obj = class(TPCElement)
E2, E2n, dE2dt, dE2dtn,
Zsp: Complex;
- FirstIteration, FixedSlip: Boolean;
+ FirstIteration: Boolean;
+ FixedSlip: LongBool;
RandomMult: Double;
IndMach012SwitchOpen: Boolean;
// Debugging
TraceFile: TFileStream;
- DebugTrace: Boolean;
+ DebugTrace: LongBool;
MachineData: TGeneratorVars; // Use generator variable structure
- // Andres: NEW variables from generator
MachineON: Boolean;
ShapeFactor: Complex;
-
ShapeIsActual: Boolean;
- // Andres: end NEW variables from generator
VBase: Double;
kWBase: Double;
- procedure InterpretOption(s: String);
-
procedure set_Localslip(const Value: Double);
procedure Get_PFlowModelCurrent(const V: Complex; const S: Double; var Istator, Irotor: Complex);
procedure Get_DynamicModelCurrent;
- procedure Set_Slip(const Value: Double);
-
+
function GetRotorLosses: Double;
function GetStatorLosses: Double;
function Compute_dSdP: Double;
@@ -165,48 +140,32 @@ TIndMach012Obj = class(TPCElement)
procedure CalcYPrimMatrix(Ymatrix: TcMatrix);
procedure CalcIndMach012ModelContribution;
- procedure CalcInjCurrentArray;
procedure DoIndMach012Model;
procedure CalcModel(V, I: pComplexArray);
- // Andres: NEW procedures from generator
procedure CalcDailyMult(Hr: Double);
procedure CalcYearlyMult(Hr: Double);
procedure CalcDutyMult(Hr: Double);
- // Andres: NEW procedures from generator
procedure InitTraceFile;
procedure WriteTraceRecord;
- function Get_PresentkV: Double;
- procedure Set_PresentkV(const Value: Double);
-
procedure SetPowerkW(const PkW: Double);
PROTECTED
-
- {A couple of virtual procedures you can override}
procedure GetTerminalCurrents(Curr: pComplexArray); OVERRIDE;
-
procedure DoDynamicMode;
procedure DoHarmonicMode;
-
PUBLIC
-
- {Variables and functions accessed by DSS and other objects}
-
- // Andres: new variables from generator
- DailyDispShape: String; // Daily (24 HR) Generator shape
DailyDispShapeObj: TLoadShapeObj; // Daily Generator Shape for this load
DutyShapeObj: TLoadShapeObj; // Shape for this generator
- DutyShape: String; //
- YearlyShape: String; // ='fixed' means no variation on all the time
YearlyShapeObj: TLoadShapeObj; // Shape for this Generator
- // Andres: New variables from generator
constructor Create(ParClass: TDSSClass; const IndMach012ObjName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure Set_ConductorClosed(Index: Integer; Value: Boolean); OVERRIDE;
procedure RecalcElementData; OVERRIDE; // Generally called after Edit is complete to recompute variables
@@ -216,14 +175,7 @@ TIndMach012Obj = class(TPCElement)
procedure CalcPFlow(var V012, I012: TSymCompArray);
procedure SetNominalPower;
- // Injection current management functions (unique to PC Elements)
- // This is how the DSS represents elements with nonlinear characteristics
- // Inj currents are the difference between the desired total terminal currents and the
- // currents that result from the linear admittance matrix of the element
function InjCurrents: Integer; OVERRIDE;
- // State variable management functions, if any
- // You can omit these if your PC element model is not using these
- // Default behavior is to basically do nothing
function NumVariables: Integer; OVERRIDE;
procedure GetAllVariables(States: pDoubleArray); OVERRIDE;
function Get_Variable(i: Integer): Double; OVERRIDE;
@@ -233,251 +185,149 @@ TIndMach012Obj = class(TPCElement)
// Support for Dynamics Mode
procedure InitStateVars; OVERRIDE;
procedure IntegrateStates; OVERRIDE;
-
// Support for Harmonics Mode
procedure InitHarmonics; OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model, if possible
-
- // Functions required for managing values of properties
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
-
- property LocalSlip: Double READ S1 WRITE set_Localslip;
- property Slip: Double WRITE Set_Slip;
- property PresentkV: Double READ Get_PresentkV WRITE Set_PresentkV;
-
- //Property Variable[i:Integer]:Double Read Get_Variable Write Set_Variable;
-
- {Put any class properties here}
- {Use properties when some method must be executed when a value is set or retrieved}
-
- { Example (from Load)
- Property ConnectedkVA :Double Read FConnectedkVA Write Set_ConnectedkVA;
- }
-
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model, if possible
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
implementation
-{Typical Uses Clause -- not all may not be needed}
uses
- ParserDel, // DSS parser
- DSSClassDefs, // Where class is instantiated
- DSSGlobals, // Global DSS variables
- Circuit, // If access to circuit variables is needed
- Command, // DSS command and property support module
- Sysutils, // Delphi misc utility functions
- Math, // Delphi Math functions
- MathUtil, // DSS Math utilities
- Utilities, // DSS misc utility functions
+ BufStream,
+ DSSClassDefs,
+ DSSGlobals,
+ Circuit,
+ Command,
+ Sysutils,
+ Math,
+ MathUtil,
+ Utilities,
DSSHelper,
DSSObjectHelper,
TypInfo;
+
+type
+ TObj = TIndMach012Obj;
+ TProp = TIndMach012Prop;
const
- NumPropsThisClass = 21; // Set this constant to the actual number of properties you define
+ NumPropsThisClass = Ord(High(TProp));
NumIndMach012Variables = 22;
+var
+ PropInfo: Pointer = NIL;
+ SlipOptionEnum : TDSSEnum;
-
-var // Define any useful module vars here, for example:
- cBuffer: array[1..24] of Complex; // Temp buffer for complex math calcs; allows up to 24-phase models.
- CDOUBLEONE: Complex; // 1 + j1 (see Initialization section below)
-
-
-function CmplxArrayToString(cpxarray: pComplexArray; count: Integer): String;
-// Put array values in brackets separated by commas.
-// Special version that appends magnitude and angle.
-
-var
- i: Integer;
-
- procedure AppendMagAngle;
- begin
- Result := Result + Format(' (%.6g, %.5g)', [Cabs(cpxarray^[i]), Cdang(cpxarray^[i])]);
- end;
-
+constructor TIndMach012.Create(dssContext: TDSSContext);
begin
-
- Result := '[NULL]';
- if count > 0 then
+ if PropInfo = NIL then
begin
- Result := Format('[%.6g +j %.6g', [cpxarray^[1].re, cpxarray^[1].im]);
- i := 1;
- AppendMagAngle;
- for i := 2 to count do
- begin
- Result := Result + Format(', %.6g +j %.6g', [cpxarray^[i].re, cpxarray^[i].im]);
- AppendMagAngle;
- end;
- Result := Result + ']';
+ PropInfo := TypeInfo(TProp);
+ SlipOptionEnum := TDSSEnum.Create('IndMach012: Slip Option', True, 1, 1,
+ ['VariableSlip', 'FixedSlip'], [0, Integer(True)]);
+ SlipOptionEnum.DefaultValue := 0;
end;
+ inherited Create(dssContext, INDMACH012_ELEMENT, 'IndMach012');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TIndMach012.Create(dssContext: TDSSContext); // Creates main collection handler for all IndMach012 objects
+destructor TIndMach012.Destroy;
begin
- inherited Create(dssContext); // make the base class and init DSSClassType
-
- // Specify class name and bit mask ID for this class type
- // IndMach012_ELEMENT must be defined in DSSClassDefs as nn*8
- // First 3 bits are used for base class type (DSSClassType)
- Class_Name := 'IndMach012';
- DSSClassType := DSSClassType + INDMACH012_ELEMENT;
-
- ActiveElement := 0; // no active elements yet; init to 0
-
- {Initialize any other special variables here}
-
- DefineProperties; // This is where the properties for this class are defined
-
- // Use the Command processor to manage property names
- // PropertyName is an array of String defined in DefineProperties
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-destructor TIndMach012.Destroy;
-
+function PowerFactorProperty(obj: TObj): Double;
begin
+ Result := PowerFactor(obj.Power[1]);
+end;
- // ElementList and CommandList freed in inherited destroy
- inherited Destroy;
-
+procedure SetLocalSlip(Obj: TObj; Value: Double);
+begin
+ obj.set_Localslip(Value);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TIndMach012.DefineProperties;
-
-// This is where the properties are defined, assigned names, indexes, and help strings
-// The Help strings will automatically show up when the Help is invoked
-
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays; {see DSSClass}
-
- // Refer to other classes for alternative methods of assigning properties
- // This example uses the AddProperty function to assign Name, Index, and Help string
- // in one statement.
-
- // First argument is string name of the property
- // Second argument is the index for the CASE statement
- // Third argument is help string
-
- // DSS properties are accessed in sequence if the property name is not explicitly specified.
- // The advantage of using the AddProperty function is that you may change the sequence simply
- // by shuffling the order of the definitions and you do not have to change the index in the CASE
- // statement in the EDIT function
-
-
- PropertyName[1] := 'phases';
- PropertyName[2] := 'bus1';
- PropertyName[3] := 'kv';
- PropertyName[4] := 'kW';
- PropertyName[5] := 'pf';
- PropertyName[6] := 'conn';
- PropertyName[7] := 'kVA';
- PropertyName[8] := 'H';
- PropertyName[9] := 'D';
- PropertyName[10] := 'puRs';
- PropertyName[11] := 'puXs';
- PropertyName[12] := 'puRr';
- PropertyName[13] := 'puXr';
- PropertyName[14] := 'puXm';
- PropertyName[15] := 'Slip';
- PropertyName[16] := 'MaxSlip';
- PropertyName[17] := 'SlipOption';
- PropertyName[18] := 'Yearly';
- PropertyName[19] := 'Daily';
- PropertyName[20] := 'Duty';
- PropertyName[21] := 'Debugtrace';
-
- PropertyHelp[1] := 'Number of Phases, this Induction Machine. ';
- PropertyHelp[2] := 'Bus to which the Induction Machine is connected. May include specific node specification.';
- PropertyHelp[3] := 'Nominal rated (1.0 per unit) voltage, kV. For 2- and 3-phase machines, specify phase-phase kV. ' +
- 'Otherwise, specify actual kV across each branch of the machine. ' +
- 'If wye (star), specify phase-neutral kV. ' +
- 'If delta or phase-phase connected, specify phase-phase kV.'; // line-neutral voltage// base voltage
- PropertyHelp[4] := 'Shaft Power, kW, for the Induction Machine. A positive value denotes power for a load. ' + CRLF +
- 'Negative value denotes an induction generator. ';
- PropertyHelp[5] := '[Read Only] Present power factor for the machine. ';
- PropertyHelp[6] := 'Connection of stator: Delta or Wye. Default is Delta.';
- PropertyHelp[7] := 'Rated kVA for the machine.';
- PropertyHelp[8] := 'Per unit mass constant of the machine. MW-sec/MVA. Default is 1.0.';
- PropertyHelp[9] := 'Damping constant. Usual range is 0 to 4. Default is 1.0. Adjust to get damping in Dynamics mode,';
- PropertyHelp[10] := 'Per unit stator resistance. Default is 0.0053.';
- PropertyHelp[11] := 'Per unit stator leakage reactance. Default is 0.106.';
- PropertyHelp[12] := 'Per unit rotor resistance. Default is 0.007.';
- PropertyHelp[13] := 'Per unit rotor leakage reactance. Default is 0.12.';
- PropertyHelp[14] := 'Per unit magnetizing reactance.Default is 4.0.';
- PropertyHelp[15] := 'Initial slip value. Default is 0.007';
- PropertyHelp[16] := 'Max slip value to allow. Default is 0.1. Set this before setting slip.';
- PropertyHelp[17] := 'Option for slip model. One of {fixedslip | variableslip* }';
- PropertyHelp[18] := 'LOADSHAPE object to use for yearly simulations. Must be previously defined ' +
- 'as a Loadshape object. Is set to the Daily load shape ' +
- ' when Daily is defined. The daily load shape is repeated in this case. ' +
- 'Set Status=Fixed to ignore Loadshape designation. ' +
- 'Set to NONE to reset to no loadahape. ' +
- 'The default is no variation.';
- PropertyHelp[19] := 'LOADSHAPE object to use for daily simulations. Must be previously defined ' +
- 'as a Loadshape object of 24 hrs, typically. ' +
- 'Set Status=Fixed to ignore Loadshape designation. ' +
- 'Set to NONE to reset to no loadahape. ' +
- 'Default is no variation (constant) if not defined. ' +
- 'Side effect: Sets Yearly load shape if not already defined.';
- PropertyHelp[20] := 'LOADSHAPE object to use for duty cycle simulations. Must be previously defined ' +
- 'as a Loadshape object. Typically would have time intervals less than 1 hr. ' +
- 'Designate the number of points to solve using the Set Number=xxxx command. ' +
- 'If there are fewer points in the actual shape, the shape is assumed to repeat.' +
- 'Set to NONE to reset to no loadahape. ' +
- 'Set Status=Fixed to ignore Loadshape designation. ' +
- ' Defaults to Daily curve If not specified.';
- PropertyHelp[21] := '[Yes | No*] Write DebugTrace file.';
-
-
- { add properties here }
-
- // Finally, we have to pick up any properties that were inherited
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- // You can optionally override default help string of an inherited property, for example
- PropertyHelp[NumPropsThisClass + 1] := 'Name of harmonic voltage or current spectrum for this IndMach012. ' +
- 'Voltage behind Xd" for machine - default. Current injection for inverter. ' +
- 'Default value is "default", which is defined when the DSS starts.';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // integer
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // special function properties
+ PropertyFlags[ord(TProp.pf)] := [TPropertyFlag.SilentReadOnly, TPropertyFlag.ReadByFunction];
+ PropertyReadFunction[ord(TProp.pf)] := @PowerFactorProperty;
+
+ // enum properties
+ PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.conn)] := ptruint(@obj.Connection);
+ PropertyOffset2[ord(TProp.conn)] := PtrInt(DSS.ConnectionEnum);
+
+ PropertyType[ord(TProp.SlipOption)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.SlipOption)] := ptruint(@obj.Fixedslip); // LongBool as Integer
+ PropertyOffset2[ord(TProp.SlipOption)] := PtrInt(SlipOptionEnum);
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+
+ // boolean properties
+ PropertyType[ord(TProp.Debugtrace)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.Debugtrace)] := ptruint(@obj.DebugTrace);
+
+ // object properties
+ PropertyType[ord(TProp.yearly)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.daily)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.duty)] := TPropertyType.DSSObjectReferenceProperty;
+
+ PropertyOffset[ord(TProp.yearly)] := ptruint(@obj.YearlyShapeObj);
+ PropertyOffset[ord(TProp.daily)] := ptruint(@obj.DailyDispShapeObj);
+ PropertyOffset[ord(TProp.duty)] := ptruint(@obj.DutyShapeObj);
+
+ PropertyOffset2[ord(TProp.yearly)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.daily)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.duty)] := ptruint(DSS.LoadShapeClass);
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.kW)] := ptruint(@obj.kWBase);
+ PropertyOffset[ord(TProp.puRs)] := ptruint(@obj.puRs);
+ PropertyOffset[ord(TProp.puXs)] := ptruint(@obj.puXs);
+ PropertyOffset[ord(TProp.puRr)] := ptruint(@obj.puRr);
+ PropertyOffset[ord(TProp.puXr)] := ptruint(@obj.puXr);
+ PropertyOffset[ord(TProp.puXm)] := ptruint(@obj.puXm);
+ PropertyOffset[ord(TProp.MaxSlip)] := ptruint(@obj.MaxSlip);
+ PropertyOffset[ord(TProp.H)] := ptruint(@obj.MachineData.Hmass);
+ PropertyOffset[ord(TProp.D)] := ptruint(@obj.MachineData.D);
+ PropertyOffset[ord(TProp.kVA)] := ptruint(@obj.MachineData.kVArating);
+ PropertyOffset[ord(TProp.kV)] := ptruint(@obj.MachineData.kVGeneratorBase);
+
+ // advanced double
+ PropertyOffset[ord(TProp.slip)] := ptruint(@obj.S1);
+ PropertyWriteFunction[ord(TProp.slip)] := @SetLocalSlip;
+ PropertyFlags[ord(TProp.slip)] := [TPropertyFlag.WriteByFunction];
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TIndMach012.NewObject(const ObjName: String): Integer;
-
-// This function is called by the DSS whenever a New IndMach012... command is encountered
-
+function TIndMach012.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new IndMach012 and add it to IndMach012 class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TIndMach012Obj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
-
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TIndMach012.SetNcondsForConnection;
-
-// This is a typical helper function found in many DSS circuit element class
-// for defining the number of conductors per terminal (Nconds) based on Y or delta connection
-
+procedure SetNcondsForConnection(Obj: TObj);
begin
-
- with DSS.ActiveIndMach012Obj do
- begin
+ with Obj do
case Connection of
0:
NConds := Fnphases; // Neutral is not connected for induction machine
@@ -489,246 +339,97 @@ procedure TIndMach012.SetNcondsForConnection;
NConds := Fnphases; // no neutral for this connection
end;
end;
- end;
-
end;
-
-//- - - - - - - - - - - - - MAIN EDIT FUNCTION - - - - - - - - - - - - - - -
-//----------------------------------------------------------------------------
-function TIndMach012.Edit: Integer;
-//----------------------------------------------------------------------------
-
-// This function is the heart of the property managment for this class
-
-var // Define some local vars for handling parser results
-
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
-// The Edit function starts where the Parser is presently pointing and
-// manages the parsing of the rest of the command line in the parser.
-
-// The DSS executive processes the command verb on the front of the line and
-// then passes control to the appropriate Edit function
-
+procedure TIndMach012Obj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
-
- // set the present element active
- // and continue parsing with contents of Parser
- DSS.ActiveIndMach012Obj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveIndMach012Obj;
-
- Result := 0;
-
- with DSS.ActiveIndMach012Obj do
- begin
- // peel off the next token on the edit line
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
-
- while Length(Param) > 0 do
- begin
- // Find the index for the CASE statement
- // If property is not named, just increment the index to the next property
- if (Length(ParamName) = 0) then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- // Update the PropertyValy for this property
- // Actual index is mapped via PropertyIdxMap array for this class
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[PropertyIdxMap[ParamPointer]] := Param
- else
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for IndMach012 "' + Name + '"', 560);
-
- // --------------- MAIN CASE STATEMENT ----------------------
- if ParamPointer > 0 then
- // since we used AddProperty function to define properties, have to
- // use PropertyIdxMap to map to the correct Case index
- case PropertyIdxMap[ParamPointer] of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 561);
- 1:
- NPhases := Parser.Intvalue; // num phases
- 2:
- SetBus(1, param);
- 3:
- PresentkV := Parser.DblValue;
- 4:
- kWBase := Parser.DblValue;
- 5: ; // Do nothing; read only power factor := Parser.DblValue;
- 6:
- InterpretConnection(Parser.StrValue);
- 7:
- MachineData.kVArating := Parser.DblValue;
- 8:
- MachineData.Hmass := Parser.DblValue;
- 9:
- MachineData.D := Parser.DblValue;
- 10:
- puRs := Parser.DblValue;
- 11:
- puXs := Parser.DblValue;
- 12:
- puRr := Parser.DblValue;
- 13:
- puXr := Parser.DblValue;
- 14:
- puXm := Parser.DblValue;
- 15:
- Slip := Parser.DblValue;
- 16:
- MaxSlip := Parser.DblValue;
- 17:
- InterpretOption(Parser.StrValue);
- 18:
- YearlyShape := Param;
- 19:
- DailyDispShape := Param;
- 20:
- DutyShape := Param;
- 21:
- DebugTrace := InterpretYesNo(Param);
-
+ case Idx of
+ ord(TProp.phases):
+ SetNCondsForConnection(self); // Force Reallocation of terminal info
+ ord(TProp.kV):
+ with MachineData do
+ case FNphases of
+ 2, 3:
+ VBase := kVGeneratorBase * InvSQRT3x1000;
else
- // Handle Inherited properties
- ClassEdit(DSS.ActiveIndMach012Obj, ParamPointer - NumPropsThisClass)
+ VBase := kVGeneratorBase * 1000.0;
end;
+ ord(TProp.slip):
+ MachineData.Speed := MachineData.w0 * (-S1); // make motor speed agree
+ 18:
+ if Assigned(YearlyShapeObj) then
+ with YearlyShapeObj do
+ if UseActual then
+ SetPowerkW(MaxP);
+ 19:
+ if Assigned(DailyDispShapeObj) then
+ with DailyDispShapeObj do
+ if UseActual then
+ SetPowerkW(MaxP);
+ 20:
+ if Assigned(DutyShapeObj) then
+ with DutyShapeObj do
+ if UseActual then
+ SetPowerkW(MaxP);
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
- // ---------------- SIDE EFFECTS CASE STATEMENT ---------------------
- // This case statment handles any side effects from setting a property
- // (for example, from Generator)
- if ParamPointer > 0 then
- case PropertyIdxMap[ParamPointer] of
- 1:
- SetNcondsForConnection; // Force Reallocation of terminal info
- 18:
- begin
- YearlyShapeObj := DSS.LoadShapeClass.Find(YearlyShape);
- if Assigned(YearlyShapeObj) then
- with YearlyShapeObj do
- if UseActual then
- SetPowerkW(MaxP);
- end;
- 19:
- begin
- DailyDispShapeObj := DSS.LoadShapeClass.Find(DailyDispShape);
- if Assigned(DailyDispShapeObj) then
- with DailyDispShapeObj do
- if UseActual then
- SetPowerkW(MaxP);
- end;
- 20:
- begin
- DutyShapeObj := DSS.LoadShapeClass.Find(DutyShape);
- if Assigned(DutyShapeObj) then
- with DutyShapeObj do
- if UseActual then
- SetPowerkW(MaxP);
- end;
- else
- end;
-
- // Get next token off Parser and continue editing properties
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
- // After editing is complete, the typical next step is to call the RecalcElementData function
+function TIndMach012.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
+ begin
RecalcElementData;
- YPrimInvalid := TRUE; // Setting this flag notifies the DSS that something has changed
- // and the Yprim will have to be rebuilt
+ YPrimInvalid := TRUE;
+ Exclude(Flags, Flg.EditionActive);
end;
-
+ Result := True;
end;
-//----------------------------------------------------------------------------
-function TIndMach012.MakeLike(const OtherIndMach012Name: String): Integer;
-
-// This function should be defined to handle the Like property inherited from
-// the base class.
-
-// The function copies the essential properties of another object of this class
-
+procedure TIndMach012Obj.MakeLike(OtherPtr: Pointer);
var
- OtherIndMach012: TIndMach012Obj;
- i: Integer;
-
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this IndMach012 name in the present collection}
- OtherIndMach012 := Find(OtherIndMach012Name);
- if (OtherIndMach012 <> NIL) // skip if not found
- then
- with DSS.ActiveIndMach012Obj do
- begin
- // You should first set the basic circuit element properties, for example
- if (Fnphases <> OtherIndMach012.Fnphases) then
- begin
- Nphases := OtherIndMach012.Fnphases;
- NConds := Fnphases; // Forces reallocation of terminal stuff
-
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
- end;
-
- PresentkV := OtherIndMach012.PresentkV;
- kWBase := OtherIndMach012.kWBase;
+ inherited MakeLike(OtherPtr);
- puRs := OtherIndMach012.puRs;
- puRr := OtherIndMach012.puRr;
- puXr := OtherIndMach012.puXr;
- puXm := OtherIndMach012.puXm;
- puXs := OtherIndMach012.puXs;
- MaxSlip := OtherIndMach012.MaxSlip;
-
- MachineData.kVArating := OtherIndMach012.MachineData.kVArating;
- MachineData.Hmass := OtherIndMach012.MachineData.Hmass;
- MachineData.D := OtherIndMach012.MachineData.D;
-
- // Do inherited properties
- ClassMakeLike(OtherIndMach012);
+ Other := TObj(OtherPtr);
+ if (Fnphases <> Other.Fnphases) then
+ begin
+ FNphases := Other.Fnphases;
+ NConds := Fnphases; // Forces reallocation of terminal stuff
- // Finally initialize all the property value strings to be the same as
- // the copied element
- for i := 1 to ParentClass.NumProperties do
- // Skip read only properties
- if i <> 5 then
- FPropertyValue^[i] := OtherIndMach012.FPropertyValue^[i];
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
+ end;
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Load MakeLike: "' + OtherIndMach012Name + '" Not Found.', 562);
+ MachineData := Other.MachineData; // record, copy everything at once
+ VBase := Other.VBase;
+ kWBase := Other.kWBase;
+ puRs := Other.puRs;
+ puRr := Other.puRr;
+ puXr := Other.puXr;
+ puXm := Other.puXm;
+ puXs := Other.puXs;
+ MaxSlip := Other.MaxSlip;
end;
-//------------------------- MAIN OBJECT CONSTRUCTOR ---------------------
constructor TIndMach012Obj.Create(ParClass: TDSSClass; const IndMach012ObjName: String);
-//----------------------------------------------------------------------------
begin
inherited create(ParClass);
- Name := LowerCase(IndMach012ObjName);
+ Name := AnsiLowerCase(IndMach012ObjName);
DSSObjType := ParClass.DSSClassType; // Same as Parent Class
TraceFile := nil;
- // Set some basic circuit element properties
- Nphases := 3; // typical DSS default for a circuit element
- Fnconds := 3; // defaults to delta
- Yorder := 0; // To trigger an initial allocation
- Nterms := 1; // forces allocations of terminal quantities
+ FNphases := 3;
+ Fnconds := 3;
+ Yorder := 0;
+ Nterms := 1;
kWBase := 1000.0;
- YearlyShape := '';
YearlyShapeObj := NIL; // if YearlyShapeobj = nil then the load alway stays nominal * global multipliers
- DailyDispShape := '';
DailyDispShapeObj := NIL; // if DaillyShapeobj = nil then the load alway stays nominal * global multipliers
- DutyShape := '';
DutyShapeObj := NIL; // if DutyShapeobj = nil then the load alway stays nominal * global multipliers
Debugtrace := FALSE;
@@ -758,8 +459,6 @@ constructor TIndMach012Obj.Create(ParClass: TDSSClass; const IndMach012ObjName:
NumConductors := Fnconds;
end;
- {---- end note Andres: from dll model ----}
-
{Typical machine impedance data}
puRs := 0.0053;
puXs := 0.106;
@@ -767,49 +466,33 @@ constructor TIndMach012Obj.Create(ParClass: TDSSClass; const IndMach012ObjName:
puXr := 0.12;
puXm := 4.0;
- // Set slip local and make generator model agree
+ // Set slip local and make generator model agree
MaxSlip := 0.1; // 10% slip limit - set this before setting slip
- Slip := 0.007; // About 1 pu power
+ set_LocalSlip(0.007); // About 1 pu power
+ PropertySideEffects(ord(TProp.slip));
+
FixedSlip := FALSE; // Allow Slip to float to match specified power
InDynamics := FALSE;
- // call the procedure to set the initial property string values
- InitPropertyValues(0);
-
- // Update anything that has to be calculated from property values
RecalcElementData;
-
end;
-
-//----------------------------------------------------------------------------
destructor TIndMach012Obj.Destroy;
-//----------------------------------------------------------------------------
-
// Free everything here that needs to be freed
// If you allocated anything, dispose of it here
-
begin
-
FreeAndNil(TraceFile);
inherited Destroy; // This will take care of most common circuit element arrays, etc.
-
end;
-
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.RecalcElementData;
-//----------------------------------------------------------------------------
-
var
Rs, Xs,
Rr, Xr,
Xm, ZBase: Double;
-
begin
-
with MachineData do
begin
ZBase := Sqr(kVGeneratorBase) / kVArating * 1000.0;
@@ -818,7 +501,6 @@ procedure TIndMach012Obj.RecalcElementData;
NumConductors := Fnconds;
end;
-
Rs := puRs * ZBase;
Xs := puXs * ZBase;
Rr := puRr * ZBase;
@@ -849,78 +531,20 @@ procedure TIndMach012Obj.RecalcElementData;
SetNominalPower;
- if CompareText(YearlyShape, 'none') = 0 then
- YearlyShape := '';
- if CompareText(DailyDispShape, 'none') = 0 then
- DailyDispShape := '';
- if CompareText(DutyShape, 'none') = 0 then
- DutyShape := '';
-
- if YearlyShapeObj = NIL then
- if Length(YearlyShape) > 0 then
- DoSimpleMsg('WARNING! Yearly load shape: "' + YearlyShape + '" Not Found.', 563);
- if DailyDispShapeObj = NIL then
- if Length(DailyDispShape) > 0 then
- DoSimpleMsg('WARNING! Daily load shape: "' + DailyDispShape + '" Not Found.', 564);
- if DutyShapeObj = NIL then
- if Length(DutyShape) > 0 then
- DoSimpleMsg('WARNING! Duty load shape: "' + DutyShape + '" Not Found.', 565);
-
- SpectrumObj := DSS.SpectrumClass.Find(Spectrum);
- if SpectrumObj = NIL then
- DoSimpleMsg('ERROR! Spectrum "' + Spectrum + '" Not Found.', 566);
-
if DebugTrace then
InitTraceFile
else
FreeAndNil(TraceFile);
end;
-
-function TIndMach012obj.Get_PresentkV: Double;
-begin
- Result := MachineData.kVGeneratorBase;
-end;
-
-procedure TIndMach012obj.Set_PresentkV(const Value: Double);
-begin
- with MachineData do
- begin
- kVGeneratorBase := Value;
- case FNphases of
- 2, 3:
- VBase := kVGeneratorBase * InvSQRT3x1000;
- else
- VBase := kVGeneratorBase * 1000.0;
- end;
- end;
-end;
-
-procedure TIndMach012obj.InterpretOption(s: String);
-begin
- case Uppercase(s)[1] of
- 'F':
- Fixedslip := TRUE;
- 'V':
- Fixedslip := FALSE;
- else
-
- end;
-end;
-
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.SetPowerkW(const PkW: Double);
begin
kWBase := PkW;
end;
-//--------------------- MAIN CALC ROUTINES -----------------------------------
-
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.Integrate;
var
h2: Double;
-
begin
with ActiveCircuit.Solution.Dynavars do
begin
@@ -932,45 +556,39 @@ procedure TIndMach012Obj.Integrate;
dE2dtn := dE2dt;
end;
- // Derivative of E
- // dEdt = -jw0SE' - (E' - j(X-X')I')/T0'
- dE1dt := Csub(cmul(cmplx(0.0, -MachineData.w0 * S1), E1), Cdivreal(Csub(E1, cmul(cmplx(0.0, (Xopen - Xp)), Is1)), T0p));
- dE2dt := Csub(cmul(cmplx(0.0, -MachineData.w0 * S2), E2), Cdivreal(Csub(E2, cmul(cmplx(0.0, (Xopen - Xp)), Is2)), T0p));
+ // Derivative of E
+ // dEdt = -jw0SE' - (E' - j(X-X')I')/T0'
+ dE1dt := cmplx(0.0, -MachineData.w0 * S1) * E1 - (E1 - cmplx(0.0, Xopen - Xp) * Is1) / T0p;
+ dE2dt := cmplx(0.0, -MachineData.w0 * S2) * E2 - (E2 - cmplx(0.0, Xopen - Xp) * Is2) / T0p;
- // Trapezoidal Integration
+ // Trapezoidal Integration
h2 := h * 0.5;
- E1 := Cadd(E1n, CmulReal(Cadd(dE1dt, dE1dtn), h2));
- E2 := Cadd(E2n, CmulReal(Cadd(dE2dt, dE2dtn), h2));
+ E1 := E1n + (dE1dt + dE1dtn) * h2;
+ E2 := E2n + (dE2dt + dE2dtn) * h2;
end;
-
end;
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.CalcDynamic(var V012, I012: TSymCompArray);
-//----------------------------------------------------------------------------
begin
{In dynamics mode, slip is allowed to vary}
InDynamics := TRUE;
V1 := V012[1]; // Save for variable calcs
V2 := V012[2];
- {Gets slip from shaft speed}
+
+ {Gets slip from shaft speed}
with MachineData do
- LocalSlip := (-Speed) / w0;
+ set_LocalSlip((-Speed) / w0);
Get_DynamicModelCurrent;
// Get_ModelCurrent(V2, S2, Is2, Ir2);
I012[1] := Is1; // Save for variable calcs
I012[2] := Is2;
I012[0] := cmplx(0.0, 0.0);
-
end;
-
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.CalcPFlow(var V012, I012: TSymCompArray);
var
P_Error: Double;
-
begin
V1 := V012[1]; // Save for variable calcs
V2 := V012[2];
@@ -986,8 +604,8 @@ procedure TIndMach012Obj.CalcPFlow(var V012, I012: TSymCompArray);
{If Fixed slip option set, then use the value set by the user}
if not FixedSlip then
begin
- P_Error := MachineData.PnominalperPhase - Cmul(V1, Conjg(Is1)).re;
- LocalSlip := S1 + dSdP * P_Error; // make new guess at slip
+ P_Error := MachineData.PnominalperPhase - (V1 * cong(Is1)).re;
+ set_LocalSlip(S1 + dSdP * P_Error); // make new guess at slip
end;
Get_PFlowModelCurrent(V1, S1, Is1, Ir1);
@@ -996,70 +614,51 @@ procedure TIndMach012Obj.CalcPFlow(var V012, I012: TSymCompArray);
I012[1] := Is1; // Save for variable calcs
I012[2] := Is2;
I012[0] := cmplx(0.0, 0.0);
-
end;
-
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.Randomize(Opt: Integer);
-//----------------------------------------------------------------------------
-
// typical proc for handling randomization in DSS fashion
-
begin
case Opt of
0:
RandomMult := 1.0;
- // GAUSSIAN: RandomMult := Gauss(YearlyShapeObj.Mean, YearlyShapeObj.StdDev);
- UNIfORM:
+ // GAUSSIAN: RandomMult := Gauss(YearlyShapeObj.Mean, YearlyShapeObj.StdDev);
+ UNIFORM:
RandomMult := Random; // number between 0 and 1.0
- // LOGNORMAL: RandomMult := QuasiLognormal(YearlyShapeObj.Mean);
+ // LOGNORMAL: RandomMult := QuasiLognormal(YearlyShapeObj.Mean);
end;
end;
-
-{-------------------------------------------------------------------------------------------------------------}
procedure TIndMach012Obj.InitModel(V012, I012: TSymCompArray);
// Init for Dynamics mode
-
begin
-
- // Compute Voltage behind transient reactance and set derivatives to zero
- // *** already done *** E1 := csub(V012[1], cmul(I012[1], Zsp));
+ // Compute Voltage behind transient reactance and set derivatives to zero
+ // *** already done *** E1 := V012[1] - I012[1] * Zsp;
dE1dt := czero;
E1n := E1;
dE1dtn := dE1dt;
- E2 := csub(V012[2], cmul(I012[2], Zsp));
+ E2 := V012[2] - I012[2] * Zsp;
dE2dt := czero;
E2n := E2;
dE2dtn := dE2dt;
-
end;
-
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.InitStateVars;
-//----------------------------------------------------------------------------
-
var
i: Integer;
V012,
I012: TSymCompArray;
Vabc: array[1..3] of Complex;
-
begin
-
YPrimInvalid := TRUE; // Force rebuild of YPrims
with MachineData do
begin
-
{Compute nominal Positive sequence voltage behind transient reactance}
if MachineON then
with ActiveCircuit.Solution do
begin
-
Yeq := Cinv(Zsp);
ComputeIterminal;
@@ -1068,33 +667,32 @@ procedure TIndMach012Obj.InitStateVars;
1:
begin
- E1 := Csub(CSub(NodeV^[NodeRef^[1]], NodeV^[NodeRef^[2]]), Cmul(ITerminal^[1], Zsp));
+ E1 := NodeV^[NodeRef^[1]] - NodeV^[NodeRef^[2]] - ITerminal^[1] * Zsp;
end;
3:
begin
- // Calculate E1 based on Pos Seq only
+ // Calculate E1 based on Pos Seq only
Phase2SymComp(ITerminal, pComplexArray(@I012)); // terminal currents
- // Voltage behind Zsp (transient reactance), volts
-
+ // Voltage behind Zsp (transient reactance), volts
for i := 1 to FNphases do
Vabc[i] := NodeV^[NodeRef^[i]]; // Wye Voltage
Phase2SymComp(pComplexArray(@Vabc), pComplexArray(@V012));
- E1 := Csub(V012[1], Cmul(I012[1], Zsp)); // Pos sequence
+ E1 := V012[1] - I012[1] * Zsp; // Pos sequence
end;
else
- DoSimpleMsg(Format('Dynamics mode is implemented only for 1- or 3-phase Motors. IndMach012.' + name + ' has %d phases.', [Fnphases]), 5672);
+ DoSimpleMsg('Dynamics mode is implemented only for 1- or 3-phase Motors. %s has %d phases.', [FullName, Fnphases], 5672);
DSS.SolutionAbort := TRUE;
end;
InitModel(V012, I012); // E2, etc
- // Shaft variables
+ // Shaft variables
Theta := Cang(E1);
dTheta := 0.0;
w0 := Twopi * ActiveCircuit.Solution.Frequency;
- // recalc Mmass and D in case the frequency has changed
+ // recalc Mmass and D in case the frequency has changed
with MachineData do
begin
Mmass := 2.0 * Hmass * kVArating * 1000.0 / (w0); // M = W-sec
@@ -1102,7 +700,7 @@ procedure TIndMach012Obj.InitStateVars;
end;
Pshaft := Power[1].re; // Initialize Pshaft to present power consumption of motor
- Speed := -LocalSlip * w0; // relative to synch speed
+ Speed := -S1 * w0; // relative to synch speed
dSpeed := 0.0;
if DebugTrace then // Put in a separator record
@@ -1122,29 +720,22 @@ procedure TIndMach012Obj.InitStateVars;
Speed := 0.0;
dSpeed := 0.0;
end;
- end; {With}
+ end;
end;
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
-
-{A typical helper function for PC elements to assist in the computation
- of Yprim
-}
-
+// A typical helper function for PC elements to assist in the computation of Yprim
var
Y, Yij, Yadder: Complex;
i, j: Integer;
FreqMultiplier: Double;
-
begin
-
FYprimFreq := ActiveCircuit.Solution.Frequency;
FreqMultiplier := FYprimFreq / BaseFrequency; // ratio to adjust reactances for present solution frequency
- with ActiveCircuit.solution do
+ with ActiveCircuit.solution do
if IsDynamicModel or IsHarmonicModel then
- // for Dynamics and Harmonics modes use constant equivalent Y
+ // for Dynamics and Harmonics modes use constant equivalent Y
begin
if MachineON then
Y := Yeq // L-N value computed in initialization routines
@@ -1152,24 +743,22 @@ procedure TIndMach012Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
Y := Cmplx(EPSILON, 0.0);
if Connection = 1 then
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
+ Y := Y / 3.0; // Convert to delta impedance
Y.im := Y.im / FreqMultiplier; // adjust for frequency
- Yij := Cnegate(Y);
+ Yij := -Y;
for i := 1 to Fnphases do
begin
case Connection of
0:
begin
Ymatrix.SetElement(i, i, Y); // sets the element
- {
- Ymatrix.AddElement(Fnconds, Fnconds, Y); // sums the element
- Ymatrix.SetElemsym(i, Fnconds, Yij);
- }
+ // Ymatrix.AddElement(Fnconds, Fnconds, Y); // sums the element
+ // Ymatrix.SetElemsym(i, Fnconds, Yij);
end;
1:
begin {Delta connection}
- Yadder := CmulReal(Y, 1.000001); // to prevent floating delta
- Ymatrix.SetElement(i, i, Cadd(Y, Yadder)); // add a little bit to diagonal
+ Yadder := Y * 1.000001; // to prevent floating delta
+ Ymatrix.SetElement(i, i, Y + Yadder); // add a little bit to diagonal
Ymatrix.AddElement(i, i, Y); // put it in again
for j := 1 to i - 1 do
Ymatrix.SetElemsym(i, j, Yij);
@@ -1177,10 +766,8 @@ procedure TIndMach012Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
end;
end;
end
-
else
begin
-
// Typical code for a regular power flow model
// Borrowed from Generator object
@@ -1199,18 +786,18 @@ procedure TIndMach012Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
for i := 1 to Fnphases do
begin
SetElement(i, i, Y);
- {
- AddElement(Fnconds, Fnconds, Y);
- SetElemsym(i, Fnconds, Yij);
- }
+ {
+ AddElement(Fnconds, Fnconds, Y);
+ SetElemsym(i, Fnconds, Yij);
+ }
end;
end;
1:
with YMatrix do
begin // Delta or L-L
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
- Yij := Cnegate(Y);
+ Y := Y / 3.0; // Convert to delta impedance
+ Yij := -Y;
for i := 1 to Fnphases do
begin
j := i + 1;
@@ -1223,7 +810,6 @@ procedure TIndMach012Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
end;
end;
end; {ELSE IF Solution.mode}
-
end;
{--- Notes Andres: Added according to IndMach012.dll model }
@@ -1233,10 +819,9 @@ function TIndMach012Obj.Compute_dSdP: Double;
V1 := Cmplx(MachineData.kvGeneratorBase * 1000.0 / 1.732, 0.0);
if S1 <> 0.0 then
Get_PFlowModelCurrent(V1, S1, Is1, Ir1);
- Result := S1 / Cmul(V1, Conjg(Is1)).Re;
+ Result := S1 / (V1 * cong(Is1)).Re;
end;
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.CalcYPrim;
// Required routine to calculate the primitive Y matrix for this element
@@ -1248,7 +833,6 @@ procedure TIndMach012Obj.CalcYPrim;
i: Integer;
begin
-
{
There are three Yprim matrices that could be computed:
@@ -1291,46 +875,38 @@ procedure TIndMach012Obj.CalcYPrim;
// so that CalcVoltages doesn't fail
// This is just one of a number of possible strategies but seems to work most of the time
for i := 1 to Yorder do
- Yprim_Series.SetElement(i, i, CmulReal(Yprim_Shunt.Getelement(i, i), 1.0e-10));
+ Yprim_Series.SetElement(i, i, Yprim_Shunt.Getelement(i, i) * 1.0e-10);
// copy YPrim_shunt into YPrim; That's all that is needed for most PC Elements
YPrim.CopyFrom(YPrim_Shunt);
// Account for Open Conductors -- done in base class
inherited CalcYPrim;
-
end;
// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.DoIndMach012Model;
{Compute total terminal Current }
var
i: Integer;
-
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcModel(Vterminal, Iterminal);
IterminalUpdated := TRUE;
- for i := 1 to Nphases do
- Caccum(InjCurrent^[i], Cnegate(Iterminal^[i]));
+ for i := 1 to FNphases do
+ InjCurrent^[i] -= Iterminal^[i];
if (DebugTrace) then
WriteTraceRecord;
-
end;
procedure TIndMach012Obj.CalcModel(V, I: pComplexArray); // given voltages returns currents
-
var
V012, I012: TSymCompArray;
-
begin
-
// Convert abc voltages to 012
Phase2SymComp(V, pComplexArray(@V012));
@@ -1351,22 +927,16 @@ procedure TIndMach012Obj.CalcModel(V, I: pComplexArray); // given voltages retur
end;
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.DoDynamicMode;
-
{ This is an example taken from Generator illustrating how a PC element might
handle Dynamics mode with a Thevenin equivalent
Also illustrates the computation of symmetrical component values
}
-
{Compute Total Current and add into InjTemp}
-
var
i: Integer;
-
begin
-
// Start off by getting the current in the admittance branch of the model
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
@@ -1375,15 +945,11 @@ procedure TIndMach012Obj.DoDynamicMode;
CalcModel(Vterminal, Iterminal);
IterminalUpdated := TRUE;
- for i := 1 to Nphases do
- Caccum(InjCurrent^[i], Cnegate(ITerminal^[i]));
-
+ for i := 1 to FNphases do
+ InjCurrent^[i] -= ITerminal^[i];
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TIndMach012Obj.DoHarmonicMode;
-
{
Example taken from Generator illustrating how a PC element might handle
current calcs for Harmonics mode
@@ -1396,14 +962,13 @@ procedure TIndMach012Obj.DoHarmonicMode;
{Assumes spectrum is a voltage source behind subtransient reactance and YPrim has been built}
{Vd is the fundamental frequency voltage behind Xd" for phase 1}
-
var
i: Integer;
E: Complex;
GenHarmonic: Double;
-
+ pBuffer: PCBuffer24;
begin
-
+ pBuffer := @TIndMach012(ParentClass).cBuffer;
// Set the VTerminal array
ComputeVterminal;
@@ -1411,13 +976,13 @@ procedure TIndMach012Obj.DoHarmonicMode;
begin
GenHarmonic := Frequency / BaseFrequency; // harmonic based on the fundamental for this object
// get the spectrum multiplier and multiply by the V thev (or Norton current for load objects)
- // ??? E := CmulReal(SpectrumObj.GetMult(GenHarmonic), VThevHarm); // Get base harmonic magnitude
+ // ??? E := SpectrumObj.GetMult(GenHarmonic) * VThevHarm; // Get base harmonic magnitude
// ??? RotatePhasorRad(E, GenHarmonic, ThetaHarm); // Time shift by fundamental frequency phase shift
// Put the values in a temp complex buffer
for i := 1 to Fnphases do
begin
- cBuffer[i] := E;
+ pBuffer[i] := E;
if i < Fnphases then
RotatePhasorDeg(E, GenHarmonic, -120.0); // Assume 3-phase IndMach012
end;
@@ -1425,24 +990,19 @@ procedure TIndMach012Obj.DoHarmonicMode;
{Handle Wye Connection}
if Connection = 0 then
- cbuffer[Fnconds] := Vterminal^[Fnconds]; // assume no neutral injection voltage
+ pBuffer[Fnconds] := Vterminal^[Fnconds]; // assume no neutral injection voltage
// In this case the injection currents are simply Yprim(frequency) times the voltage buffer
// Refer to Load.Pas for load-type objects
{Inj currents = Yprim (E) }
- YPrim.MVMult(InjCurrent, pComplexArray(@cBuffer));
-
+ YPrim.MVMult(InjCurrent, pComplexArray(pBuffer));
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TIndMach012Obj.CalcIndMach012ModelContribution;
-
// Main dispatcher for computing PC Element currnts
// Calculates IndMach012 current and adds it properly into the injcurrent array
// routines may also compute ITerminal (ITerminalUpdated flag)
-
begin
IterminalUpdated := FALSE;
with ActiveCircuit, ActiveCircuit.Solution do
@@ -1456,46 +1016,15 @@ procedure TIndMach012Obj.CalcIndMach012ModelContribution;
DoIndMach012Model;
end; {WITH}
-
{When this is done, ITerminal is up to date}
-
-end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TIndMach012Obj.CalcInjCurrentArray;
-//----------------------------------------------------------------------------
-
-// Main procedure for controlling computation of InjCurrent array
-
-// InjCurrent is difference between currents in YPrim and total terminal current
-
-
-begin
-
-// You usually will want some logic like this
-
- // If the element is open, just zero the array and return
- if IndMach012SwitchOpen then
- ZeroInjCurrent
-
- // otherwise, go to a routine that manages the calculation
- else
- CalcIndMach012ModelContribution;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TIndMach012Obj.GetTerminalCurrents(Curr: pComplexArray);
-//----------------------------------------------------------------------------
-
// This function controls the calculation of the total terminal currents
// Note that it only does something if the solution count has changed.
// Otherwise, Iterminal array already contains the currents
-
-
begin
-
with ActiveCircuit.Solution do
begin
if IterminalSolutionCount <> ActiveCircuit.Solution.SolutionCount then
@@ -1506,44 +1035,38 @@ procedure TIndMach012Obj.GetTerminalCurrents(Curr: pComplexArray);
end;
inherited GetTerminalCurrents(Curr); // add in inherited contribution
end;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
function TIndMach012Obj.InjCurrents: Integer;
-//----------------------------------------------------------------------------
-
// Required function for managing computing of InjCurrents
-
begin
-
with ActiveCircuit.Solution do
begin
-
// Generators and Loads use logic like this:
if LoadsNeedUpdating then
SetNominalPower; // Set the nominal kW, etc for the type of solution being done
- // call the main function for doing calculation
- CalcInjCurrentArray; // Difference between currents in YPrim and total terminal current
+ // call the main function for doing calculation
+ // Difference between currents in YPrim and total terminal current
+ if IndMach012SwitchOpen then
+ // If the element is open, just zero the array and return
+ ZeroInjCurrent
+ else
+ // otherwise, go to a routine that manages the calculation
+ CalcIndMach012ModelContribution;
// If (DebugTrace) Then WriteTraceRecord;
// Add into System Injection Current Array
Result := inherited InjCurrents;
-
end;
-
end;
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.SetNominalPower;
-//----------------------------------------------------------------------------
// Set shaft power
var
Factor: Double;
MachineOn_Saved: Boolean;
-
begin
MachineOn_Saved := MachineON;
ShapeFactor := CDOUBLEONE;
@@ -1555,7 +1078,6 @@ procedure TIndMach012Obj.SetNominalPower;
MachineON := TRUE; // Init to on then check if it should be off
end;
-
if not MachineON then
begin
// If Machine is OFF enter as tiny resistive load (.0001 pu) so we don't get divide by zero in matrix
@@ -1642,13 +1164,9 @@ procedure TIndMach012Obj.SetNominalPower;
// If machine state changes, force re-calc of Y matrix
if MachineON <> MachineOn_Saved then
YPrimInvalid := TRUE;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TIndMach012Obj.CalcDailyMult(Hr: Double);
-//----------------------------------------------------------------------------
-
begin
if (DailyDispShapeObj <> NIL) then
begin
@@ -1659,10 +1177,7 @@ procedure TIndMach012Obj.CalcDailyMult(Hr: Double);
ShapeFactor := CDOUBLEONE; // Default to no daily variation
end;
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.CalcDutyMult(Hr: Double);
-//----------------------------------------------------------------------------
-
begin
if DutyShapeObj <> NIL then
begin
@@ -1673,10 +1188,7 @@ procedure TIndMach012Obj.CalcDutyMult(Hr: Double);
CalcDailyMult(Hr); // Default to Daily Mult if no duty curve specified
end;
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.CalcYearlyMult(Hr: Double);
-//----------------------------------------------------------------------------
-
begin
{Yearly curve is assumed to be hourly only}
if YearlyShapeObj <> NIL then
@@ -1689,174 +1201,12 @@ procedure TIndMach012Obj.CalcYearlyMult(Hr: Double);
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TIndMach012Obj.DumpProperties(F: TFileStream; Complete: Boolean);
-//----------------------------------------------------------------------------
-{
- This procedure is require to respond to various commands such as Dump that
- write all the device's property values to a file.
-}
-
-var
- i, idx: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- {Write out any specials here, usually preceded by a "!"}
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- idx := PropertyIdxMap[i]; // Map to get proper index into property value array
- case idx of
- {Trap any specials here, such as values that are array properties, for example}
- 34, 36:
- FSWriteln(F, '~ ' + PropertyName^[i] + '=(' + PropertyValue[idx] + ')')
- else
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[idx]);
- end;
- end;
-
- FSWriteln(F);
-
-end;
-
-
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.InitHarmonics;
-//----------------------------------------------------------------------------
-
{Procedure to initialize for Harmonics solution}
-
-{This example is extracted from Generator and constructs a Thevinen equivalent.
- Refer to Load for how to do a Norton equivalent
- }
-
-{Var
- E, Va:complex; }
begin
-
YPrimInvalid := TRUE; // Force rebuild of YPrims
-
-(****
- GenFundamental := ActiveCircuit.Solution.Frequency ; // Whatever the frequency is when we enter here.
-
- With GenVars Do Begin
-
- // Xd" is used for harmonics analysis for generators
- Yeq := Cinv(Cmplx(0.0, Xdpp)); // used for current calcs Always L-N
-
- {Compute reference Thevinen voltage from phase 1 current}
-
- IF GenON Then
- Begin
-
- ComputeIterminal; // Get present value of current
-
- With ActiveCircuit.solution Do
- Case Connection of
- 0: Begin {wye - neutral is explicit}
- Va := Csub(NodeV^[NodeRef^[1]], NodeV^[NodeRef^[Fnconds]]);
- End;
- 1: Begin {delta -- assume neutral is at zero}
- Va := NodeV^[NodeRef^[1]];
- End;
- End;
-
- E := Csub(Va, Cmul(Iterminal^[1], cmplx(0.0, Xdpp)));
- Vthevharm := Cabs(E); // establish base mag and angle
- ThetaHarm := Cang(E);
- End
- ELSE Begin
- // If Generator is off, just set to zero
- Vthevharm := 0.0;
- ThetaHarm := 0.0;
- End;
- End;
- ***)
end;
-// ******************* PROPERTY VALUES *******************
-
-//----------------------------------------------------------------------------
-procedure TIndMach012Obj.InitPropertyValues(ArrayOffset: Integer);
-//----------------------------------------------------------------------------
-
-// required procedure to initialize the string value of the properties
-
-begin
- // Some examples
- PropertyValue[1] := '3'; //'phases';
- PropertyValue[2] := Getbus(1); //'bus1';
- PropertyValue[3] := '12.47';
- PropertyValue[4] := '100';
- PropertyValue[5] := '.80';
- PropertyValue[6] := 'Delta';
- PropertyValue[7] := Format('%-g', [MachineData.kVARating]);
- PropertyValue[8] := Format('%-g', [MachineData.Hmass]);
- PropertyValue[9] := Format('%-g', [MachineData.D]);
- PropertyValue[10] := '0.0053';
- PropertyValue[11] := '0.106';
- PropertyValue[12] := '0.007';
- PropertyValue[13] := '0.12';
- PropertyValue[14] := '4.0';
-
- PropertyValue[15] := '0.007';
- PropertyValue[16] := '0.1';
- PropertyValue[17] := 'variable';
-
- PropertyValue[18] := '';
- PropertyValue[19] := '';
- PropertyValue[20] := ''; {...}
- PropertyValue[21] := 'NO';
-
-{Call inherited function to init inherited property values}
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-
-//----------------------------------------------------------------------------
-function TIndMach012Obj.GetPropertyValue(Index: Integer): String;
-//----------------------------------------------------------------------------
-
-// Return i-th property value as a string
-
-begin
-
- Result := ''; // Init the string
- case Index of
- // Put special cases here
- // often a good idea to convert numeric values to strings, for example
- 4:
- Result := Format('%.6g', [kWBase]);
- 5:
- Result := Format('%.6g', [PowerFactor(Power[1])]);
- 7:
- Result := Format('%.6g', [MachineData.kVArating]);
- 8:
- Result := Format('%.6g', [MachineData.Hmass]);
- 9:
- Result := Format('%.6g', [MachineData.D]);
- 15:
- Result := Format('%.6g', [localslip]);
- 18:
- Result := YearlyShape;
- 19:
- Result := DailyDispShape;
- 20:
- Result := DutyShape;
- {...}
- else
-
- // The default is to just return the current string value of the property
- Result := inherited GetPropertyValue(index);
-
- end;
-end;
-
-
procedure TIndMach012Obj.IntegrateStates;
{
This is a virtual function. You do not need to write this routine
@@ -1881,7 +1231,6 @@ procedure TIndMach012Obj.IntegrateStates;
with ActiveCircuit.Solution, MachineData do
begin
-
with DynaVars do
if (IterationFlag = 0) then
begin {First iteration of new time step}
@@ -1909,50 +1258,34 @@ procedure TIndMach012Obj.IntegrateStates;
end;
end;
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.Get_DynamicModelCurrent;
-//----------------------------------------------------------------------------
begin
-
- Is1 := Cdiv(Csub(V1, E1), Zsp); // I = (V-E')/Z'
- Is2 := Cdiv(Csub(V2, E2), Zsp); // I = (V-E')/Z'
+ Is1 := (V1 - E1) / Zsp; // I = (V-E')/Z'
+ Is2 := (V2 - E2) / Zsp; // I = (V-E')/Z'
// rotor current Ir1= Is1-Vm/jXm
- Ir1 := Csub(Is1, Cdiv(Csub(V1, cmul(Is1, Zsp)), Zm));
- Ir2 := Csub(Is2, Cdiv(Csub(V2, cmul(Is2, Zsp)), Zm));
-
+ Ir1 := Is1 - (V1 - Is1 * Zsp) / Zm;
+ Ir2 := Is2 - (V2 - Is2 * Zsp) / Zm;
end;
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.Get_PFlowModelCurrent(const V: Complex; const S: Double; var Istator, Irotor: Complex);
-//----------------------------------------------------------------------------
var
RL: Double;
ZRotor, Numerator, Zmotor: Complex;
-
begin
-
if s <> 0.0 then
RL := Zr.re * (1.0 - s) / s
else
RL := Zr.re * 1.0e6;
- ZRotor := Cadd(Cmplx(RL, 0.0), Zr);
- Numerator := Cmul(Zm, Zrotor);
- Zmotor := Cadd(Zs, Cdiv(Numerator, Cadd(ZRotor, Zm)));
- Istator := Cdiv(V, Zmotor);
- {Ir = Is -(V-ZsIs)/Zm}
- Irotor := Csub(Istator, Cdiv(Csub(V, Cmul(Zs, Istator)), Zm));
-
+ ZRotor := RL + Zr;
+ Numerator := Zm * Zrotor;
+ Zmotor := Zs + Numerator / (ZRotor + Zm);
+ Istator := V / Zmotor;
+ Irotor := Istator - (V - (Zs * Istator)) / Zm;
end;
-//----------------------------------------------------------------------------
-// ********************** VARIABLES ***************************************
-//----------------------------------------------------------------------------
-
-//----------------------------------------------------------------------------
function TIndMach012Obj.NumVariables: Integer;
-//----------------------------------------------------------------------------
{
Return the number of state variables
@@ -1960,28 +1293,22 @@ function TIndMach012Obj.NumVariables: Integer;
if you are not defining state variables.
Note: it is not necessary to define any state variables
}
-
begin
Result := NumIndMach012Variables;
end;
-//----------------------------------------------------------------------------
function TIndMach012Obj.VariableName(i: Integer): String;
-//----------------------------------------------------------------------------
-
{
Returns the i-th state variable in a string
This is a virtual function. You do not need to write this routine
if you are not defining state variables.
}
-
begin
if i < 1 then
Exit; // This means Someone goofed
case i of
-
1:
Result := 'Frequency';
2:
@@ -2026,16 +1353,11 @@ function TIndMach012Obj.VariableName(i: Integer): String;
Result := 'Power Factor';
22:
Result := 'Efficiency (%)';
- else
end;
-
end;
-//----------------------------------------------------------------------------
function TIndMach012Obj.Get_Variable(i: Integer): Double;
-//----------------------------------------------------------------------------
begin
-
Result := -9999.99; // Error Value
with MachineData do
@@ -2053,7 +1375,7 @@ function TIndMach012Obj.Get_Variable(i: Integer): Double;
6:
Result := dTheta;
7:
- Result := LocalSlip;
+ Result := S1;
8:
Result := puRs;
9:
@@ -2086,20 +1408,17 @@ function TIndMach012Obj.Get_Variable(i: Integer): Double;
Result := PowerFactor(Power[1]);
22:
Result := (1.0 - (GetStatorLosses + GetRotorLosses) / power[1].re) * 100.0; // Efficiency
- else
-
end;
-
end;
-//----------------------------------------------------------------------------
-procedure TIndMach012Obj.Set_Variable(i: Integer; Value: Double);
-//----------------------------------------------------------------------------
+procedure TIndMach012Obj.Set_Variable(i: Integer; Value: Double); // TODO: remove -- this is completely redundant with the properties but doesn't call RecalcElementData
begin
case i of
-
7:
- Slip := Value;
+ begin
+ set_LocalSlip(Value);
+ PropertySideEffects(ord(TProp.slip));
+ end;
8:
puRs := Value;
9:
@@ -2110,15 +1429,11 @@ procedure TIndMach012Obj.Set_Variable(i: Integer; Value: Double);
puXr := Value;
12:
puXm := Value;
-
- else
- {Do Nothing for other variables: they are read only}
end;
+ // Do Nothing for other variables: they are read only
end;
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.GetAllVariables(States: pDoubleArray);
-//----------------------------------------------------------------------------
{
Return all state variables in double array (allocated by calling function)
@@ -2127,97 +1442,27 @@ procedure TIndMach012Obj.GetAllVariables(States: pDoubleArray);
}
var
i: Integer;
-
begin
for i := 1 to NumIndMach012Variables do
States^[i] := Variable[i];
end;
-// ********************** END VARIABLES ***************************************
-
-
-//----------------------------------------------------------------------------
function TIndMach012Obj.GetRotorLosses: Double;
-//----------------------------------------------------------------------------
begin
Result := 3.0 * (Sqr(Ir1.re) + Sqr(Ir1.im) + Sqr(Ir2.re) + Sqr(Ir2.im)) * Zr.re;
end;
-//----------------------------------------------------------------------------
function TIndMach012Obj.GetStatorLosses: Double;
-//----------------------------------------------------------------------------
begin
Result := 3.0 * (Sqr(Is1.re) + Sqr(Is1.im) + Sqr(Is2.re) + Sqr(Is2.im)) * Zs.re;
end;
-
-//----------------------------------------------------------------------------
-procedure TIndMach012Obj.MakePosSequence;
-//----------------------------------------------------------------------------
-
-{
- This is a virtual function. You do not need to write this routine
- if the base class function will suffice.
-}
-
-// Routine to convert existing three-phase models to a single-phase positive-
-// sequence model
-
-// var
-// S: String;
-// V :Double;
-
+procedure TIndMach012Obj.MakePosSequence();
begin
-
-{
- The usual technique is to create a new property editing string
- based on the present values of properties. Once the string is
- created, it is pushed into the Parser and the Edit routine for this
- class is invoked.
-
- Thus, the positive sequence model is created in memory. Do a
- "Save Circuit" command to save the model that is created. Some
- editing of the resulting scripts will likely be required. Not all
- elements have an obvious positive sequence equivalent.
-}
-
-
- // example from Generator class
- // Modify as necessary
-
-// S := 'Phases=1 conn=wye'; // Positive sequence model is 1-phase wye
-
- (****
-
- // Make sure voltage is line-neutral
- If (Fnphases>1) or (connection<>0) Then V := GenVars.kVGeneratorBase/SQRT3
- Else V := GenVars.kVGeneratorBase;
-
- S := S + Format(' kV=%-.5g',[V]);
-
- // Divide the load by no. phases
- If Fnphases>1 Then
- Begin
- S := S + Format(' kW=%-.5g PF=%-.5g',[kWbase/Fnphases, PFNominal]);
- If (PrpSequence^[19]<>0) or (PrpSequence^[20]<>0) Then S := S + Format(' maxkvar=%-.5g minkvar=%-.5g',[kvarmax/Fnphases, kvarmin/Fnphases]);
- If PrpSequence^[26]>0 Then S := S + Format(' kva=%-.5g ',[genvars.kvarating/Fnphases]);
- If PrpSequence^[27]>0 Then S := S + Format(' MVA=%-.5g ',[genvars.kvarating/1000.0/Fnphases]);
- End;
-
- Parser.CmdString := S; // Push the string into the Parser object
- Edit; // Invoke the Edit method for this class
-
- inherited; // sets the terminal bus references, must do after editing number of phases
-
- ***)
-
end;
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.Set_ConductorClosed(Index: Integer; Value: Boolean);
-
// Routine for handling Open/Close procedures
-
begin
inherited;
@@ -2225,41 +1470,27 @@ procedure TIndMach012Obj.Set_ConductorClosed(Index: Integer; Value: Boolean);
IndMach012SwitchOpen := FALSE
else
IndMach012SwitchOpen := TRUE;
-
end;
-
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.set_Localslip(const Value: Double);
-
- function Sign(const x: Double): Double;
- begin
- if x < 0.0 then
- Result := -1.0
- else
- Result := 1.0;
- end;
-
begin
S1 := Value;
if not InDynamics then
if Abs(S1) > MaxSlip then
- S1 := Sign(S1) * MaxSlip; // Put limits on the slip unless dynamics
+ begin
+ // Put limits on the slip unless dynamics
+ if S1 < 0 then
+ S1 := -MaxSlip
+ else
+ S1 := MaxSlip;
+ end;
S2 := 2.0 - S1;
end;
-//----------------------------------------------------------------------------
-procedure TIndMach012Obj.Set_Slip(const Value: Double);
-begin
- LocalSlip := Value;
- MachineData.Speed := MachineData.w0 * (-S1); // make motor speed agree
-end;
-
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.InitTraceFile;
begin
FreeAndNil(TraceFile);
- TraceFile := TFileStream.Create(DSS.OutputDirectory + Format('%s_IndMach012_Trace.CSV', [Name]), fmCreate);
+ TraceFile := TBufferedFileStream.Create(DSS.OutputDirectory + Format('%s_IndMach012_Trace.csv', [Name]), fmCreate);
FSWrite(TraceFile, 'Time, Iteration, S1, |IS1|, |IS2|, |E1|, |dE1dt|, |E2|, |dE2dt|, |V1|, |V2|, Pshaft, Pin, Speed, dSpeed');
FSWriteln(TraceFile);
@@ -2267,7 +1498,6 @@ procedure TIndMach012Obj.InitTraceFile;
FSFlush(TraceFile);
end;
-//----------------------------------------------------------------------------
procedure TIndMach012Obj.WriteTraceRecord;
begin
with ActiveCircuit.Solution do
@@ -2284,14 +1514,5 @@ procedure TIndMach012Obj.WriteTraceRecord;
FSFlush(TraceFile);
end;
-initialization
-
-// Initialize any variables here
-
-
- // For Example: 1 + j 1
-
- CDOUBLEONE := CMPLX(1.0, 1.0);
-
-
+finalization SlipOptionEnum.Free;
end.
diff --git a/src/PCElements/Isource.pas b/src/PCElements/Isource.pas
index 226df8aa0..7c1efe038 100644
--- a/src/PCElements/Isource.pas
+++ b/src/PCElements/Isource.pas
@@ -7,19 +7,12 @@
----------------------------------------------------------
}
-{ Ideal current source
-
- Stick'em on wherever you want as many as you want
-
- ISource maintains a positive sequence for harmonic scans. If you want zero sequence,
- use three single-phase ISource.
-
-
- 10-25-00 Created from Vsource
- 5-17-02 Moved spectrum to base class
- 2-19-03 Added Phaseshift variable for n-phase elements
-
-}
+// Ideal current source
+//
+// Stick'em on wherever you want as many as you want
+//
+// ISource maintains a positive sequence for harmonic scans. If you want zero sequence,
+// use three single-phase ISource.
interface
@@ -29,27 +22,39 @@ interface
PCClass,
PCElement,
ucmatrix,
- ucomplex,
+ UComplex, DSSUcomplex,
Spectrum,
Loadshape;
type
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+{$SCOPEDENUMS ON}
+ TIsourceProp = (
+ INVALID = 0,
+ bus1 = 1,
+ amps = 2,
+ angle = 3,
+ frequency = 4,
+ phases = 5,
+ scantype = 6,
+ sequence = 7,
+ Yearly = 8,
+ Daily = 9,
+ Duty = 10,
+ Bus2 = 11
+ );
+{$SCOPEDENUMS OFF}
+
TIsource = class(TPCClass)
- PRIVATE
- procedure IsourceSetBus1(const S: String);
PROTECTED
- procedure DefineProperties;
- function MakeLike(const OtherSource: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TIsourceObj = class(TPCElement)
PRIVATE
@@ -72,34 +77,27 @@ TIsourceObj = class(TPCElement)
ScanType,
SequenceType: Integer;
PerUnit: Double;
- DailyShape: String; // Daily (24 HR) load shape
- DailyShapeObj: TLoadShapeObj; // Daily load Shape FOR this load
- DutyShape: String; // Duty cycle load shape FOR changes typically less than one hour
- DutyShapeObj: TLoadShapeObj; // Shape for this load
- YearlyShape: String; // ='fixed' means no variation exempt from variation
+ DailyShapeObj: TLoadShapeObj; // Daily (24 HR) load shape
+ DutyShapeObj: TLoadShapeObj; // Duty cycle load shape FOR changes typically less than one hour
YearlyShapeObj: TLoadShapeObj; // Shape for this load
constructor Create(ParClass: TDSSClass; const SourceName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
function InjCurrents: Integer; OVERRIDE;
procedure GetCurrents(Curr: pComplexArray); OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
implementation
-
uses
- ParserDel,
Circuit,
DSSClassDefs,
DSSGlobals,
@@ -111,369 +109,213 @@ implementation
DSSObjectHelper,
TypInfo;
-
+type
+ TObj = TISourceObj;
+ TProp = TIsourceProp;
+const
+ NumPropsThisClass = Ord(High(TProp));
var
- NumPropsThisClass: Integer;
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TIsource.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TIsource.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'Isource';
- DSSClassType := SOURCE + NON_PCPD_ELEM; // Don't want this in PC Element List
-
- ActiveElement := 0;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
+ inherited Create(dssContext, SOURCE or NON_PCPD_ELEM, 'Isource');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TIsource.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TIsource.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
- NumPropsThisClass := 11;
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
- PropertyName[1] := 'bus1';
- PropertyName[2] := 'amps';
- PropertyName[3] := 'angle';
- PropertyName[4] := 'frequency';
- PropertyName[5] := 'phases';
- PropertyName[6] := 'scantype';
- PropertyName[7] := 'sequence';
- PropertyName[8] := 'Yearly';
- PropertyName[9] := 'Daily';
- PropertyName[10] := 'Duty';
- PropertyName[11] := 'Bus2';
-
- // define Property help values
- PropertyHelp[1] := 'Name of bus to which source is connected.' + CRLF + 'bus1=busname' + CRLF + 'bus1=busname.1.2.3';
- PropertyHelp[2] := 'Magnitude of current source, each phase, in Amps.';
- PropertyHelp[3] := 'Phase angle in degrees of first phase: e.g.,Angle=10.3.' + CRLF +
- 'Phase shift between phases is assumed 120 degrees when ' +
- 'number of phases <= 3';
- PropertyHelp[4] := 'Source frequency. Defaults to circuit fundamental frequency.';
- PropertyHelp[5] := 'Number of phases. Defaults to 3. For 3 or less, phase shift is 120 degrees.';
- PropertyHelp[6] := '{pos*| zero | none} Maintain specified sequence for harmonic solution. Default is positive sequence. ' +
- 'Otherwise, angle between phases rotates with harmonic.';
- PropertyHelp[7] := '{pos*| neg | zero} Set the phase angles for the specified symmetrical component sequence for non-harmonic solution modes. ' +
- 'Default is positive sequence. ';
- PropertyHelp[8] := 'LOADSHAPE object to use for the per-unit current for YEARLY-mode simulations. Set the Mult property of the LOADSHAPE ' +
- 'to the pu curve. Qmult is not used. If UseActual=Yes then the Mult curve should be actual Amp.' + CRLF + CRLF +
- 'Must be previously defined as a LOADSHAPE object. ' + CRLF + CRLF +
- 'Is set to the Daily load shape when Daily is defined. The daily load shape is repeated in this case. ' +
- 'Set to NONE to reset to no loadahape for Yearly mode. ' +
- 'The default is no variation.';
- PropertyHelp[9] := 'LOADSHAPE object to use for the per-unit current for DAILY-mode simulations. Set the Mult property of the LOADSHAPE ' +
- 'to the pu curve. Qmult is not used. If UseActual=Yes then the Mult curve should be actual A.' + CRLF + CRLF +
- 'Must be previously defined as a LOADSHAPE object. ' + CRLF + CRLF +
- 'Sets Yearly curve if it is not already defined. ' +
- 'Set to NONE to reset to no loadahape for Yearly mode. ' +
- 'The default is no variation.';
- PropertyHelp[10] := 'LOADSHAPE object to use for the per-unit current for DUTYCYCLE-mode simulations. Set the Mult property of the LOADSHAPE ' +
- 'to the pu curve. Qmult is not used. If UseActual=Yes then the Mult curve should be actual A.' + CRLF + CRLF +
- 'Must be previously defined as a LOADSHAPE object. ' + CRLF + CRLF +
- 'Defaults to Daily load shape when Daily is defined. ' +
- 'Set to NONE to reset to no loadahape for Yearly mode. ' +
- 'The default is no variation.';
- PropertyHelp[11] := 'Name of bus to which 2nd terminal is connected.' + CRLF + 'bus2=busname' + CRLF + 'bus2=busname.1.2.3' +
- CRLF + CRLF +
- 'Default is Bus1.0.0.0 (grounded-wye connection)';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // integer properties
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyType[ord(TProp.bus2)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+ PropertyOffset[ord(TProp.bus2)] := 2;
+
+ // enum properties
+ PropertyType[ord(TProp.ScanType)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.ScanType)] := ptruint(@obj.ScanType);
+ PropertyOffset2[ord(TProp.ScanType)] := PtrInt(DSS.ScanTypeEnum);
+
+ PropertyType[ord(TProp.Sequence)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.Sequence)] := ptruint(@obj.Sequencetype);
+ PropertyOffset2[ord(TProp.Sequence)] := PtrInt(DSS.SequenceEnum);
+
+ // object properties
+ PropertyType[ord(TProp.yearly)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.daily)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.duty)] := TPropertyType.DSSObjectReferenceProperty;
+
+ PropertyOffset[ord(TProp.yearly)] := ptruint(@obj.YearlyShapeObj);
+ PropertyOffset[ord(TProp.daily)] := ptruint(@obj.DailyShapeObj);
+ PropertyOffset[ord(TProp.duty)] := ptruint(@obj.DutyShapeObj);
+
+ PropertyOffset2[ord(TProp.yearly)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.daily)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.duty)] := ptruint(DSS.LoadShapeClass);
+
+ // double properties
+ PropertyOffset[ord(TProp.amps)] := ptruint(@obj.Amps);
+ PropertyOffset[ord(TProp.angle)] := ptruint(@obj.Angle);
+ PropertyOffset[ord(TProp.frequency)] := ptruint(@obj.SrcFrequency);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- // Override help string
- PropertyHelp[NumPropsThisClass + 1] := 'Harmonic spectrum assumed for this source. Default is "default".';
-
+ inherited DefineProperties;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TIsource.NewObject(const ObjName: String): Integer;
+function TIsource.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new voltage source and add it to Isource class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TIsourceObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TIsource.Edit: Integer;
+procedure TIsourceObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- ParamPointer: Integer;
- ParamName,
- Param: String;
-
+ S, S2: String;
+ i, dotpos: Integer;
begin
- // continue parsing with contents of Parser
- DSS.ActiveIsourceObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveIsourceObj;
-
- Result := 0;
-
- with DSS.ActiveIsourceObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
+ case Idx of
+ ord(TProp.Phases):
begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 330);
+ case FNphases of
1:
- IsourceSetBus1(param);
- 2:
- Amps := Parser.DblValue;
- 3:
- Angle := Parser.DblValue; // Ang
- 4:
- SrcFrequency := Parser.DblValue; // freq
- 5:
- begin
- Nphases := Parser.IntValue; // num phases
- case FNphases of
- 1:
- FphaseShift := 0.0;
- 2, 3:
- FphaseShift := 120.0;
- else // higher order systems
- FphaseShift := 360.0 / FNphases;
- end;
- NConds := Fnphases; // Force Reallocation of terminal info
- end;
- 6:
- case Uppercase(Param)[1] of
- 'P':
- ScanType := 1;
- 'Z':
- ScanType := 0;
- 'N':
- ScanType := -1;
- else
- DoSimpleMsg('Unknown Scan Type for "' + Class_Name + '.' + Name + '": ' + Param, 331);
- end;
- 7:
- case Uppercase(Param)[1] of
- 'P':
- SequenceType := 1;
- 'Z':
- SequenceType := 0;
- 'N':
- SequenceType := -1;
- else
- DoSimpleMsg('Unknown Sequence Type for "' + Class_Name + '.' + Name + '": ' + Param, 331);
- end;
- 8:
- YearlyShape := Param;
- 9:
- DailyShape := Param;
- 10:
- DutyShape := Param;
- 11:
- SetBus(2, Param);
- else
- ClassEdit(DSS.ActiveIsourceObj, ParamPointer - NumPropsThisClass);
+ FphaseShift := 0.0;
+ 2, 3:
+ FphaseShift := 120.0;
+ else // higher order systems
+ FphaseShift := 360.0 / FNphases;
end;
+ NConds := Fnphases; // Force Reallocation of terminal info
+ end;
+ ord(TProp.bus1):
+ // Special handling for Bus 1
+ // Set Bus2 = Bus1.0.0.0
+ if not Bus2Defined then // Default Bus2 to zero node of Bus1. (Grounded-Y connection)
+ begin
+ // Strip node designations from S
+ S := GetBus(1);
+ dotpos := Pos('.', S);
+ if dotpos > 0 then
+ S2 := Copy(S, 1, dotpos - 1)
+ else
+ S2 := Copy(S, 1, Length(S)); // copy up to Dot
+ for i := 1 to Fnphases do
+ S2 := S2 + '.0'; // append series of ".0"'s
- case ParamPointer of
- {Set shape objects; returns nil if not valid}
- {Sets the kW and kvar properties to match the peak kW demand from the Loadshape}
- 8:
- YearlyShapeObj := DSS.LoadShapeClass.Find(YearlyShape);
- 9:
- begin
- DailyShapeObj := DSS.LoadShapeClass.Find(DailyShape);
- {If Yearly load shape is not yet defined, make it the same as Daily}
- if YearlyShapeObj = NIL then
- YearlyShapeObj := DailyShapeObj;
- end;
- 10:
- DutyShapeObj := DSS.LoadShapeClass.Find(DutyShape);
+ SetBus(2, S2); // default setting for Bus2
end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
+ 9:
+ // If Yearly load shape is not yet defined, make it the same as Daily
+ if YearlyShapeObj = NIL then
+ YearlyShapeObj := DailyShapeObj;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
+function TIsource.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
+ begin
RecalcElementData;
YPrimInvalid := TRUE;
+ Exclude(Flags, Flg.EditionActive);
end;
-
+ Result := True;
end;
-//----------------------------------------------------------------------------
-function TIsource.MakeLike(const OtherSource: String): Integer;
+procedure TIsourceObj.MakeLike(OtherPtr: Pointer);
var
- OtherIsource: TIsourceObj;
- i: Integer;
-
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this line name in the present collection}
- OtherIsource := Find(OtherSource);
- if OtherIsource <> NIL then
- with DSS.ActiveIsourceObj do
- begin
-
- if Fnphases <> OtherIsource.Fnphases then
- begin
- Nphases := OtherIsource.Fnphases;
- NConds := Fnphases; // Forces reallocation of terminal stuff
-
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
- end;
-
- Amps := OtherIsource.Amps;
- Angle := OtherIsource.Angle;
- SrcFrequency := OtherIsource.SrcFrequency;
- Scantype := OtherIsource.Scantype;
- Sequencetype := OtherIsource.Sequencetype;
-
- ShapeIsActual := OtherIsource.ShapeIsActual;
- DailyShape := OtherIsource.DailyShape;
- DailyShapeObj := OtherIsource.DailyShapeObj;
- DutyShape := OtherIsource.DutyShape;
- DutyShapeObj := OtherIsource.DutyShapeObj;
- YearlyShape := OtherIsource.YearlyShape;
- YearlyShapeObj := OtherIsource.YearlyShapeObj;
-
- Bus2Defined := OtherIsource.Bus2Defined;
-
- ClassMakeLike(OtherIsource); // set spectrum, base frequency
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherIsource.PropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Isource MakeLike: "' + OtherSource + '" Not Found.', 332);
-
-end;
-
-//----------------------------------------------------------------------------
-procedure TIsource.IsourceSetBus1(const S: String);
-var
- s2: String;
- i, dotpos: Integer;
+ inherited MakeLike(OtherPtr); // set spectrum, base frequency
- // Special handling for Bus 1
- // Set Bus2 = Bus1.0.0.0
-
-begin
- with DSS.ActiveISourceObj do
+ Other := TObj(OtherPtr);
+ if Fnphases <> Other.Fnphases then
begin
- SetBus(1, S);
-
- if not Bus2Defined then // Default Bus2 to zero node of Bus1. (Grounded-Y connection)
- begin
- // Strip node designations from S
- dotpos := Pos('.', S);
- if dotpos > 0 then
- S2 := Copy(S, 1, dotpos - 1)
- else
- S2 := Copy(S, 1, Length(S)); // copy up to Dot
- for i := 1 to Fnphases do
- S2 := S2 + '.0'; // append series of ".0"'s
+ FNphases := Other.Fnphases;
+ NConds := Fnphases; // Forces reallocation of terminal stuff
- SetBus(2, S2); // default setting for Bus2
- end;
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
end;
+ Amps := Other.Amps;
+ Angle := Other.Angle;
+ SrcFrequency := Other.SrcFrequency;
+ Scantype := Other.Scantype;
+ Sequencetype := Other.Sequencetype;
+
+ ShapeIsActual := Other.ShapeIsActual;
+ DailyShapeObj := Other.DailyShapeObj;
+ DutyShapeObj := Other.DutyShapeObj;
+ YearlyShapeObj := Other.YearlyShapeObj;
+
+ Bus2Defined := Other.Bus2Defined;
end;
-//----------------------------------------------------------------------------
constructor TIsourceObj.Create(ParClass: TDSSClass; const SourceName: String);
begin
inherited create(ParClass);
- Name := LowerCase(SourceName);
+ Name := AnsiLowerCase(SourceName);
DSSObjType := ParClass.DSSClassType; // SOURCE + NON_PCPD_ELEM; // Don't want this in PC Element List
- Nphases := 3;
+ FNphases := 3;
Fnconds := 3;
Nterms := 2; // 4/27/2018 made a 2-terminal I source
Amps := 0.0;
Angle := 0.0;
+ // TODO: remember to check if PerUnit is correctly applied if is exposed in the future
PerUnit := 1.0; // for future use if pu property added,
SrcFrequency := BaseFrequency;
FphaseShift := 120.0;
ScanType := 1; // Pos Sequence
Sequencetype := 1;
Bus2Defined := FALSE;
- InitPropertyValues(0);
ShapeIsActual := FALSE;
- YearlyShape := '';
- YearlyShapeObj := NIL; // IF YearlyShapeobj = nil THEN the Vsource alway stays nominal
- DailyShape := '';
- DailyShapeObj := NIL; // IF DaillyShapeobj = nil THEN the Vsource alway stays nominal
- DutyShape := '';
- DutyShapeObj := NIL; // IF DutyShapeobj = nil THEN the Vsource alway stays nominal
+ YearlyShapeObj := NIL;
+ DailyShapeObj := NIL;
+ DutyShapeObj := NIL;
Yorder := Fnterms * Fnconds;
RecalcElementData;
-
end;
-
-//----------------------------------------------------------------------------
destructor TIsourceObj.Destroy;
begin
inherited Destroy;
end;
-//----------------------------------------------------------------------------
procedure TIsourceObj.RecalcElementData;
begin
-
- SpectrumObj := DSS.SpectrumClass.Find(Spectrum);
-
- if SpectrumObj = NIL then
- begin
- DoSimpleMsg('Spectrum Object "' + Spectrum + '" for Device Isource.' + Name + ' Not Found.', 333);
- end;
-
Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
-
end;
-//----------------------------------------------------------------------------
procedure TIsourceObj.CalcYPrim;
-
-
begin
-
- // Build only YPrim Series
+ // Build only YPrim Series
if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Series = NIL) {YPrimInvalid} then
begin
if YPrim_Series <> NIL then
@@ -489,39 +331,33 @@ procedure TIsourceObj.CalcYPrim;
YPrim.Clear;
end;
+ // Yprim = 0 for Ideal Current Source; just leave it zeroed
- {Yprim = 0 for Ideal Current Source; just leave it zeroed}
-
- {Now Account for Open Conductors}
- {For any conductor that is open, zero out row and column}
+ // Now Account for Open Conductors
+ // For any conductor that is open, zero out row and column
inherited CalcYPrim;
YPrimInvalid := FALSE;
-
end;
function TIsourceObj.GetBaseCurr: Complex;
-
var
SrcHarmonic: Double;
NAmps: Double;
-
begin
-
try
-
with ActiveCircuit.Solution do
- {Get first Phase Current}
+ // Get first Phase Current
if IsHarmonicModel then
begin
SrcHarmonic := Frequency / SrcFrequency;
- Result := CMulReal(SpectrumObj.GetMult(SrcHarmonic), Amps); // Base current for this harmonic
+ Result := SpectrumObj.GetMult(SrcHarmonic) * Amps; // Base current for this harmonic
RotatePhasorDeg(Result, SrcHarmonic, Angle);
end
else
begin
case Mode of
- {Uses same logic as LOAD}
+ // Uses same logic as LOAD
TSolveMode.DAILYMODE:
begin
CalcDailyMult(DynaVars.dblHour);
@@ -534,11 +370,26 @@ function TIsourceObj.GetBaseCurr: Complex;
begin
CalcDutyMult(DynaVars.dblHour);
end;
+ TSolveMode.DYNAMICMODE:
+ begin
+ // This mode allows use of one class of load shape in DYNAMIC mode
+ case ActiveCircuit.ActiveLoadShapeClass of
+ USEDAILY:
+ CalcDailyMult(DynaVars.dblHour);
+ USEYEARLY:
+ CalcYearlyMult(DynaVars.dblHour);
+ USEDUTY:
+ CalcDutyMult(DynaVars.dblHour);
+ else
+ ShapeFactor := Cmplx(1.0, 0.0); // default to 1 + j0 if not known
+ end;
+ end;
end;
NAmps := Amps;
- if (Mode = TSolveMode.DAILYMODE) or {If a loadshape mode simulation}
+ if (Mode = TSolveMode.DAILYMODE) or // If a loadshape mode simulation
(Mode = TSolveMode.YEARLYMODE) or
- (Mode = TSolveMode.DUTYCYCLE) then
+ (Mode = TSolveMode.DUTYCYCLE) or
+ (Mode = TSolveMode.DYNAMICMODE) then
NAmps := Amps * ShapeFactor.re;
if abs(Frequency - SrcFrequency) < EPSILON2 then
Result := pdegtocomplex(NAmps, Angle)
@@ -547,17 +398,14 @@ function TIsourceObj.GetBaseCurr: Complex;
end;
except
- DoSimpleMsg('Error computing current for Isource.' + Name + '. Check specification. Aborting.', 334);
+ DoSimpleMsg('Error computing current for "%s". Check specification. Aborting.', [FullName], 334);
if DSS.In_Redirect then
DSS.Redirect_Abort := TRUE;
end;
-
end;
function TIsourceObj.InjCurrents: Integer;
-
-{Sum Currents directly into solution array}
-
+// Sum Currents directly into solution array
begin
GetInjCurrents(InjCurrent);
@@ -566,37 +414,29 @@ function TIsourceObj.InjCurrents: Integer;
end;
procedure TIsourceObj.GetCurrents(Curr: pComplexArray);
-
-{Total currents into a device}
-
+// Total currents into a device
var
i: Integer;
-
begin
-
try
GetInjCurrents(ComplexBuffer); // Get present value of inj currents
- // Add Together with yprim currents
+ // Add Together with yprim currents
for i := 1 to Yorder do
- Curr^[i] := Cnegate(ComplexBuffer^[i]);
+ Curr^[i] := -ComplexBuffer^[i];
except
On E: Exception do
- DoErrorMsg(('GetCurrents for Isource Element: ' + Name + '.'), E.Message,
- 'Inadequate storage allotted for circuit element?', 335);
+ DoErrorMsg(Format(_('GetCurrents for Element: %s.'), [FullName]),
+ E.Message, _('Inadequate storage allotted for circuit element?'), 335);
end;
-
end;
procedure TIsourceObj.GetInjCurrents(Curr: pComplexArray);
-
-{Fill Up an array of injection currents}
-
+// Fill Up an array of injection currents
var
i: Integer;
BaseCurr: complex;
begin
-
with ActiveCircuit.solution do
begin
BaseCurr := GetBaseCurr; // this func applies spectrum if needed
@@ -604,10 +444,9 @@ procedure TIsourceObj.GetInjCurrents(Curr: pComplexArray);
for i := 1 to Fnphases do
begin
Curr^[i] := BaseCurr;
- Curr^[i + FnPhases] := Cnegate(BaseCurr); // 2nd Terminal
+ Curr^[i + FnPhases] := -BaseCurr; // 2nd Terminal
if (i < Fnphases) then
begin
-
if IsHarmonicModel then
case ScanType of
@@ -616,11 +455,10 @@ procedure TIsourceObj.GetInjCurrents(Curr: pComplexArray);
0: ; // Do not rotate for zero sequence
else
RotatePhasorDeg(BaseCurr, Harmonic, -FphaseShift) // rotate by frequency
- {Harmonic 1 will be pos; 2 is neg; 3 is zero, and so on.}
+ // Harmonic 1 will be pos; 2 is neg; 3 is zero, and so on.
end
else
-
case SequenceType of
-1:
RotatePhasorDeg(BaseCurr, 1.0, FphaseShift); // Neg seq
@@ -628,67 +466,19 @@ procedure TIsourceObj.GetInjCurrents(Curr: pComplexArray);
else
RotatePhasorDeg(BaseCurr, 1.0, -FphaseShift); // Maintain pos seq
end;
-
end;
end;
end;
end;
-procedure TIsourceObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
- if Complete then
- begin
- FSWriteln(F);
- FSWriteln(F);
- end;
-
-end;
-
-procedure TIsourceObj.InitPropertyValues(ArrayOffset: Integer);
+procedure TIsourceObj.MakePosSequence();
begin
-
- PropertyValue[1] := GetBus(1);
- PropertyValue[2] := '0';
- PropertyValue[3] := '0';
- PropertyValue[4] := Format('%-.6g', [SrcFrequency]);
- PropertyValue[5] := '3';
- PropertyValue[6] := 'pos';
- PropertyValue[7] := 'pos';
- PropertyValue[8] := '';
- PropertyValue[9] := '';
- PropertyValue[10] := '';
- PropertyValue[11] := '';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-procedure TIsourceObj.MakePosSequence;
-begin
-
if Fnphases > 1 then
- begin
- Parser.CmdString := 'phases=1';
- Edit;
- end;
+ SetInteger(ord(TProp.Phases), 1);
inherited;
-
end;
procedure TISourceObj.CalcDailyMult(Hr: Double);
-
begin
if DailyShapeObj <> NIL then
begin
@@ -700,7 +490,6 @@ procedure TISourceObj.CalcDailyMult(Hr: Double);
end;
procedure TISourceObj.CalcDutyMult(Hr: Double);
-
begin
if DutyShapeObj <> NIL then
begin
@@ -711,11 +500,9 @@ procedure TISourceObj.CalcDutyMult(Hr: Double);
CalcDailyMult(Hr); // Default to Daily Mult IF no duty curve specified
end;
-//----------------------------------------------------------------------------
procedure TISourceObj.CalcYearlyMult(Hr: Double);
-
begin
-{Yearly curve is assumed to be hourly only}
+ // Yearly curve is assumed to be hourly only
if YearlyShapeObj <> NIL then
begin
ShapeFactor := YearlyShapeObj.GetMultAtHour(Hr);
@@ -725,4 +512,4 @@ procedure TISourceObj.CalcYearlyMult(Hr: Double);
ShapeFactor := cmplx(PerUnit, 0.0); // CDOUBLEONE; // Defaults to no variation
end;
-end.
+end.
\ No newline at end of file
diff --git a/src/PCElements/Load.pas b/src/PCElements/Load.pas
index 61b855fc8..75d46dafe 100644
--- a/src/PCElements/Load.pas
+++ b/src/PCElements/Load.pas
@@ -16,28 +16,11 @@
// Dutycycle: Defaults to Daily shape
// Growth: Circuit default growth factor
-{ Change Log
- 10/7/99 RCD Tightened up default load shape code and corrected comments
- 11-22-99 Fixed bug in CalcYPrimMatrix
- 12-5-99 Changed PQ load limits to 95% to 105%
- 1-8-99 Made PQ load limits a variable (added vminpu, vmaxpu properties)
- 2-1-00 Added normal and emergency voltage ratings to override system settings when <> 0
- 4-17-00 Added XFKVA and AllocationFactor properties and associated code.
- 8-26-00 Added exemption from LoadMult code (exemptfromLDcurve)
- 9-19-00 Changed the way UE and EEN computed for low voltage
- 10-25-00 Added Spectrum
- 10-27-00 Implemented Harmonic current and Harmonic Mode stuff
- 3-27-01 Added check to prevent divide by zero on calculation of PFNominal
- 5-17-01 Moved Spectrum definition back to PCElement
- 2-18-03 Changed Rneut default to -1
- Created a Y_Series with small conductances on the diagonal so that calcV doesn't fail
- 9-23-08 Added CVR Factors
- 10-14-08 Added kWh and Cfactor. Modified behavior of AllocationFactor to simplify State Estimation
- 4/1/14 Added Vlowpu property to make solution converge better at very low voltages
- 1/7/15 Added puXHarm and XRHarm properties to help model motor load for harmonic studies
- 3/16/16 Added PFSpecified to account for problems when UseActual is specified and no Qmult specified
- 1/10/18 Celso/Paulo mods for low-voltage transition for Model 5
-}
+// Change Log
+// 4/1/14 Added Vlowpu property to make solution converge better at very low voltages
+// 1/7/15 Added puXHarm and XRHarm properties to help model motor load for harmonic studies
+// 3/16/16 Added PFSpecified to account for problems when UseActual is specified and no Qmult specified
+// 1/10/18 Celso/Paulo mods for low-voltage transition for Model 5
interface
@@ -47,7 +30,7 @@ interface
PCClass,
PCElement,
ucmatrix,
- ucomplex,
+ UComplex, DSSUcomplex,
LoadShape,
GrowthShape,
Spectrum,
@@ -55,7 +38,8 @@ interface
type
{$SCOPEDENUMS ON}
-
+{$PUSH}
+{$Z4} // keep enums as int32 values
TLoadModel = (
// INVALID = 0,
ConstPQ = 1, // Constant kVA (P,Q always in same ratio)
@@ -67,11 +51,15 @@ interface
ConstPFixedX = 7, // Constant P (Variable); Q is fixed Z (not variable)
ZIPV = 8 // ZIPV (3 real power coefficients, 3 reactive, Vcutoff)
);
-
- TLoadConnection = (
- Wye = 0, // wye, star, line-neutral connection
- Delta = 1 // delta, line-line connection
+
+ TLoadStatus = (
+ Variable = 0,
+ Fixed,
+ Exempt
);
+{$SCOPEDENUMS OFF}
+{$POP}
+ TLoadConnection = TGeneralConnection;
TLoadSpec = (
kW_PF = 0,
@@ -80,32 +68,62 @@ interface
ConnectedkVA_PF = 3,
kWh_PF = 4
);
+
+ TLoadProp = (
+ INVALID = 0,
+ phases = 1,
+ bus1 = 2,
+ kV = 3,
+ kW = 4,
+ pf = 5,
+ model = 6,
+ yearly = 7,
+ daily = 8,
+ duty = 9,
+ growth = 10,
+ conn = 11,
+ kvar = 12,
+ Rneut = 13, // IF entered -, assume open
+ Xneut = 14,
+ status = 15, // fixed or variable
+ cls = 16, // integer
+ Vminpu = 17, // Min pu voltage for which model applies
+ Vmaxpu = 18, // Max pu voltage for which model applies
+ Vminnorm = 19, // Min pu voltage normal load
+ Vminemerg = 20, // Min pu voltage emergency rating
+ xfkVA = 21, // Service transformer rated kVA
+ allocationfactor = 22, // allocation factor for xfkVA
+ kVA = 23, // specify load in kVA and PF
+ pctmean = 24, // per cent default mean
+ pctstddev = 25, // per cent default standard deviation
+ CVRwatts = 26, // Percent watts reduction per 1% reduction in voltage from nominal
+ CVRvars = 27, // Percent vars reduction per 1% reduction in voltage from nominal
+ kwh = 28, // kwh billing
+ kwhdays = 29, // kwh billing period (24-hr days)
+ Cfactor = 30, // multiplier from kWh avg to peak kW
+ CVRcurve = 31, // name of curve to use for yearly CVR simulations
+ NumCust = 32, // Number of customers, this load
+ ZIPV = 33, // array of 7 coefficients
+ pctSeriesRL = 34, // pct of Load that is series R-L
+ RelWeight = 35, // Weighting factor for reliability
+ Vlowpu = 36, // Below this value resort to constant Z model = Yeq
+ puXharm = 37, // pu Reactance for Harmonics, if specifies
+ XRharm = 38 // X/R at fundamental for series R-L model for hamonics
+ );
{$SCOPEDENUMS OFF}
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TLoad = class(TPCClass)
- PRIVATE
-
- procedure InterpretConnection(const S: String);
- procedure SetNcondsForConnection;
PROTECTED
- function MakeLike(const OtherLoadName: String): Integer; OVERRIDE;
- procedure DefineProperties; // Add Properties of this class to propName
+ procedure DefineProperties; override; // Add Properties of this class to propName
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TLoadObj = class(TPCElement)
-{$IFDEF DSS_CAPI}
PUBLIC
-{$ELSE}
- PRIVATE
-{$ENDIF}
- PFChanged: Boolean;
FAllocationFactor: Double; // For all types of allocation
FkVAAllocationFactor: Double; // for connected kVA specification
FConnectedkVA: Double;
@@ -158,10 +176,10 @@ TLoadObj = class(TPCElement)
M95: Complex; // complex slope of line between Low and 95
M95I: Complex; // complex slope of line between Low and 95 for Constant I **Added by Celso & Paulo
- ExemptFromLDCurve: Boolean;
- Fixed: Boolean; // IF Fixed, always at base value
+ status: TLoadStatus;
ShapeIsActual: Boolean;
PFSpecified: Boolean; // Added 3-16-16 to fix problem with UseActual
+ PFChanged: Boolean;
function AllTerminalsClosed: Boolean;
procedure CalcDailyMult(Hr: Double);
@@ -182,18 +200,15 @@ TLoadObj = class(TPCElement)
procedure DoZIPVModel;
procedure DoMotorTypeLoad;
function GrowthFactor(Year: Integer): Double;
- procedure StickCurrInTerminalArray(TermArray: pComplexArray; const Curr: Complex; i: Integer);
+ procedure StickCurrInTerminalArray(TermArray: pComplexArray; const Curr: Complex; i: Integer); inline;
function InterpolateY95_YLow(const Vmag: Double): Complex; inline;
function InterpolateY95I_YLow(const Vmag: Double): Complex; inline; // ***Added by Celso & Paulo
function Get_Unserved: Boolean;
function Get_ExceedsNormal: Boolean;
procedure Set_kVAAllocationFactor(const Value: Double);
- procedure Set_ConnectedkVA(const Value: Double);
procedure ComputeAllocatedLoad;
- {Set kWh properties ...}
+ // Set kWh properties ...
procedure Set_CFactor(const Value: Double);
- procedure Set_kWh(const Value: Double);
- procedure Set_kWhDays(const Value: Double);
procedure Set_AllocationFactor(const Value: Double);
procedure SetkWkvar(const PkW, Qkvar: Double);
@@ -204,12 +219,9 @@ TLoadObj = class(TPCElement)
PUBLIC
Connection: TLoadConnection;
- DailyShape: String; // Daily (24 HR) load shape
DailyShapeObj: TLoadShapeObj; // Daily load Shape FOR this load
- DutyShape: String; // Duty cycle load shape FOR changes typically less than one hour
DutyShapeObj: TLoadShapeObj; // Shape for this load
EEN_Factor: Double; // is overloaded Factor is the amount of overload
- GrowthShape: String; // (year, Multiplier from previous year)
GrowthShapeObj: TGrowthShapeObj; // Shape for this Growth Curve
HasBeenAllocated: Boolean;
kWBase: Double;
@@ -225,9 +237,7 @@ TLoadObj = class(TPCElement)
Rneut: Double;
UE_Factor: Double; // These are set to > 0 IF a line in the critical path
Xneut: Double; // Neutral impedance
- YearlyShape: String; // ='fixed' means no variation exempt from variation
YearlyShapeObj: TLoadShapeObj; // Shape for this load
- CVRshape: String;
CVRShapeObj: TLoadShapeObj;
ZIPV: Array[1..7] of Double; // Made public 5-20-2013
ZIPVset: Boolean;
@@ -238,36 +248,36 @@ TLoadObj = class(TPCElement)
constructor Create(ParClass: TDSSClass; const SourceName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
function InjCurrents: Integer; OVERRIDE;
procedure InitHarmonics; OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
procedure SetNominalLoad;
procedure Randomize(Opt: Integer);
// 0 = reset to 1.0
// 1 = Gaussian around mean and std Dev
// 2 = uniform
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
- procedure UpdateVoltageBases;
-
property Unserved: Boolean READ Get_Unserved;
property ExceedsNormal: Boolean READ Get_ExceedsNormal;
- {AllocationFactor adjusts either connected kVA allocation factor or kWh CFactor}
+ // AllocationFactor adjusts either connected kVA allocation factor or kWh CFactor
property AllocationFactor: Double READ FAllocationFactor WRITE Set_AllocationFactor;
- {Allocate load from connected kva or kWh billing}
+ // Allocate load from connected kva or kWh billing
+
+ //TODO: remove these properties, use plain DSS properties
+ // instead to ease the code transition?
property kVAAllocationFactor: Double READ FkVAAllocationFactor WRITE Set_kVAAllocationFactor;
- property ConnectedkVA: Double READ FConnectedkVA WRITE Set_ConnectedkVA;
- property kWh: Double READ FkWh WRITE Set_kWh;
- property kWhDays: Double READ FkWhDays WRITE Set_kWhDays;
+ property ConnectedkVA: Double READ FConnectedkVA;
+ property kWh: Double READ FkWh;// WRITE Set_kWh;
+ property kWhDays: Double READ FkWhDays;
property CFactor: Double READ FCFactor WRITE Set_CFactor;
+
property puMean: Double READ FpuMean;
property puStdDev: Double READ FpuStdDev;
property CVRwatts: Double READ FCVRwattFactor;
@@ -276,19 +286,14 @@ TLoadObj = class(TPCElement)
property MinEmerg: Double READ VminEmerg;
property MinNormal: Double READ VminNormal;
property MinPU: Double READ Vminpu;
- property ExemptLoad: Boolean READ ExemptFromLDCurve;
- property FixedLoad: Boolean READ Fixed;
Property IsPFSpecified: Boolean read PFSpecified;
const
nZIPV = 7;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
implementation
-
uses
- ParserDel,
Circuit,
DSSClassDefs,
DSSGlobals,
@@ -302,237 +307,124 @@ implementation
DSSHelper,
DSSObjectHelper;
+type
+ TObj = TLoadObj;
+ TProp = TLoadProp;
const
- NumPropsThisClass = 38;
-
+ NumPropsThisClass = Ord(High(TProp));
var
- CDOUBLEONE: Complex;
+ PropInfo: Pointer = NIL;
+ LoadStatusEnum, LoadModelEnum: TDSSEnum;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TLoad.Create(dssContext: TDSSContext); // Creates superstructure FOR all Line objects
+constructor TLoad.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'Load';
- DSSClassType := DSSClassType + LOAD_ELEMENT;
-
- ActiveElement := 0;
-
- DefineProperties;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ LoadModelEnum := TDSSEnum.Create('Load: Model', True, 0, 0, [
+ 'Constant PQ', 'Constant Z', 'Motor (constant P, quadratic Q)', 'CVR (linear P, quadratic Q)',
+ 'Constant I', 'Constant P, fixed Q', 'Constant P, fixed X', 'ZIPV'],
+ [1, 2, 3, 4, 5, 6, 7, 8]);
+ LoadStatusEnum := TDSSEnum.Create('Load: Status', True, 1, 1,
+ ['Variable', 'Fixed', 'Exempt'], [0, 1, 2]);
+ LoadStatusEnum.DefaultValue := 0;
+ end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, LOAD_ELEMENT, 'Load');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TLoad.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TLoad.DefineProperties;
+type
+ P = TProp;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
-
- PropertyName[1] := 'phases';
- PropertyName[2] := 'bus1';
- PropertyName[3] := 'kV'; //
- PropertyName[4] := 'kW';
- PropertyName[5] := 'pf';
- PropertyName[6] := 'model';
- PropertyName[7] := 'yearly';
- PropertyName[8] := 'daily';
- PropertyName[9] := 'duty';
- PropertyName[10] := 'growth';
- PropertyName[11] := 'conn';
- PropertyName[12] := 'kvar';
- PropertyName[13] := 'Rneut'; // IF entered -, assume open
- PropertyName[14] := 'Xneut';
- PropertyName[15] := 'status'; // fixed or variable
- PropertyName[16] := 'class'; // integer
- PropertyName[17] := 'Vminpu'; // Min pu voltage for which model applies
- PropertyName[18] := 'Vmaxpu'; // Max pu voltage for which model applies
- PropertyName[19] := 'Vminnorm'; // Min pu voltage normal load
- PropertyName[20] := 'Vminemerg'; // Min pu voltage emergency rating
- PropertyName[21] := 'xfkVA'; // Service transformer rated kVA
- PropertyName[22] := 'allocationfactor'; // allocation factor for xfkVA
- PropertyName[23] := 'kVA'; // specify load in kVA and PF
- PropertyName[24] := '%mean'; // per cent default mean
- PropertyName[25] := '%stddev'; // per cent default standard deviation
- PropertyName[26] := 'CVRwatts'; // Percent watts reduction per 1% reduction in voltage from nominal
- PropertyName[27] := 'CVRvars'; // Percent vars reduction per 1% reduction in voltage from nominal
- PropertyName[28] := 'kwh'; // kwh billing
- PropertyName[29] := 'kwhdays'; // kwh billing period (24-hr days)
- PropertyName[30] := 'Cfactor'; // multiplier from kWh avg to peak kW
- PropertyName[31] := 'CVRcurve'; // name of curve to use for yearly CVR simulations
- PropertyName[32] := 'NumCust'; // Number of customers, this load
- PropertyName[33] := 'ZIPV'; // array of 7 coefficients
- PropertyName[34] := '%SeriesRL'; // pct of Load that is series R-L
- PropertyName[35] := 'RelWeight'; // Weighting factor for reliability
- PropertyName[36] := 'Vlowpu'; // Below this value resort to constant Z model = Yeq
- PropertyName[37] := 'puXharm'; // pu Reactance for Harmonics, if specifies
- PropertyName[38] := 'XRharm'; // X/R at fundamental for series R-L model for hamonics
-(*
- Typical Motor Parameters for motor
- Xpu = 0.20
- X/r typically 3-6 for normal motors; higher for high-eff motors
-*)
-
- // define Property help values
- PropertyHelp[1] := 'Number of Phases, this load. Load is evenly divided among phases.';
- PropertyHelp[2] := 'Bus to which the load is connected. May include specific node specification.';
- PropertyHelp[3] := 'Nominal rated (1.0 per unit) voltage, kV, for load. For 2- and 3-phase loads, specify phase-phase kV. ' +
- 'Otherwise, specify actual kV across each branch of the load. ' +
- 'If wye (star), specify phase-neutral kV. ' +
- 'If delta or phase-phase connected, specify phase-phase kV.'; // line-neutral voltage
- PropertyHelp[4] := 'Total base kW for the load. Normally, you would enter the maximum kW for the load for the first year ' +
- 'and allow it to be adjusted by the load shapes, growth shapes, and global load multiplier.' + CRLF + CRLF +
- 'Legal ways to define base load:' + CRLF +
- 'kW, PF' + CRLF +
- 'kW, kvar' + CRLF +
- 'kVA, PF' + CRLF +
- 'XFKVA * Allocationfactor, PF' + CRLF +
- 'kWh/(kWhdays*24) * Cfactor, PF';
- PropertyHelp[5] := 'Load power factor. Enter negative for leading powerfactor (when kW and kvar have opposite signs.)';
- PropertyHelp[6] := 'Integer code for the model to use for load variation with voltage. ' +
- 'Valid values are:' + CRLF + CRLF +
- '1:Standard constant P+jQ load. (Default)' + CRLF +
- '2:Constant impedance load. ' + CRLF +
- '3:Const P, Quadratic Q (like a motor).' + CRLF +
- '4:Nominal Linear P, Quadratic Q (feeder mix). Use this with CVRfactor.' + CRLF +
- '5:Constant Current Magnitude' + CRLF +
- '6:Const P, Fixed Q' + CRLF +
- '7:Const P, Fixed Impedance Q' + CRLF +
- '8:ZIPV (7 values)' + CRLF + CRLF +
- 'For Types 6 and 7, only the P is modified by load multipliers.';
- PropertyHelp[7] := 'LOADSHAPE object to use for yearly simulations. Must be previously defined ' +
- 'as a Loadshape object. Is set to the Daily load shape ' +
- ' when Daily is defined. The daily load shape is repeated in this case. ' +
- 'Set Status=Fixed to ignore Loadshape designation. ' +
- 'Set to NONE to reset to no loadahape. ' +
- 'The default is no variation.';
- PropertyHelp[8] := 'LOADSHAPE object to use for daily simulations. Must be previously defined ' +
- 'as a Loadshape object of 24 hrs, typically. ' +
- 'Set Status=Fixed to ignore Loadshape designation. ' +
- 'Set to NONE to reset to no loadahape. ' +
- 'Default is no variation (constant) if not defined. ' +
- 'Side effect: Sets Yearly load shape if not already defined.';
- PropertyHelp[9] := 'LOADSHAPE object to use for duty cycle simulations. Must be previously defined ' +
- 'as a Loadshape object. Typically would have time intervals less than 1 hr. ' +
- 'Designate the number of points to solve using the Set Number=xxxx command. ' +
- 'If there are fewer points in the actual shape, the shape is assumed to repeat.' +
- 'Set to NONE to reset to no loadahape. ' +
- 'Set Status=Fixed to ignore Loadshape designation. ' +
- ' Defaults to Daily curve If not specified.';
- PropertyHelp[10] := 'Characteristic to use for growth factors by years. Must be previously defined ' +
- 'as a Growthshape object. Defaults to circuit default growth factor (see Set Growth command).';
- PropertyHelp[11] := '={wye or LN | delta or LL}. Default is wye.';
- PropertyHelp[12] := 'Specify the base kvar for specifying load as kW & kvar. Assumes kW has been already defined. Alternative to specifying the power factor. Side effect: ' +
- ' the power factor and kVA is altered to agree.';
- PropertyHelp[13] := 'Default is -1. Neutral resistance of wye (star)-connected load in actual ohms. ' +
- 'If entered as a negative value, the neutral can be open, or floating, or it can be connected to ' +
- 'node 0 (ground), which is the usual default. ' +
- 'If >=0 be sure to explicitly specify the node connection for the neutral, or last, conductor. ' +
- 'Otherwise, the neutral impedance will be shorted to ground.';
- PropertyHelp[14] := 'Neutral reactance of wye(star)-connected load in actual ohms. May be + or -.';
- PropertyHelp[15] := '={Variable | Fixed | Exempt}. Default is variable. If Fixed, no load multipliers apply; however, growth ' +
- 'multipliers do apply. All multipliers apply to Variable loads. Exempt loads are not ' +
- 'modified by the global load multiplier, such as in load duration curves, etc. Daily multipliers ' +
- 'do apply, so setting this property to Exempt is a good way to represent industrial load that stays the same' +
- ' day-after-day for the period study.'; // fixed or variable
- PropertyHelp[16] := 'An arbitrary integer number representing the class of load so that load values may ' +
- 'be segregated by load value. Default is 1; not used internally.';
- PropertyHelp[17] := 'Default = 0.95. Minimum per unit voltage for which the MODEL is assumed to apply. Lower end of normal voltage range.' +
- 'Below this value, the load model reverts to a constant impedance model that matches the model at the transition voltage. ' +
- 'See also "Vlowpu" which causes the model to match Model=2 below the transition voltage.';
- PropertyHelp[18] := 'Default = 1.05. Maximum per unit voltage for which the MODEL is assumed to apply. ' +
- 'Above this value, the load model reverts to a constant impedance model.';
- PropertyHelp[19] := 'Minimum per unit voltage for load EEN evaluations, Normal limit. Default = 0, which defaults to system "vminnorm" ' +
- 'property (see Set Command under Executive). If this property is specified, it ALWAYS ' +
- 'overrides the system specification. This allows you to have different criteria for different loads. ' +
- 'Set to zero to revert to the default system value.';
- PropertyHelp[20] := 'Minimum per unit voltage for load UE evaluations, Emergency limit. Default = 0, which defaults to system "vminemerg" ' +
- 'property (see Set Command under Executive). If this property is specified, it ALWAYS ' +
- 'overrides the system specification. This allows you to have different criteria for different loads. ' +
- 'Set to zero to revert to the default system value.';
- PropertyHelp[21] := 'Default = 0.0. Rated kVA of service transformer for allocating loads based on connected kVA ' +
- 'at a bus. Side effect: kW, PF, and kvar are modified. See help on kVA.';
- PropertyHelp[22] := 'Default = 0.5. Allocation factor for allocating loads based on connected kVA ' +
- 'at a bus. Side effect: kW, PF, and kvar are modified by multiplying this factor times the XFKVA (if > 0).';
- PropertyHelp[23] := 'Specify base Load in kVA (and power factor)' + CRLF + CRLF +
- 'Legal ways to define base load:' + CRLF +
- 'kW, PF' + CRLF +
- 'kW, kvar' + CRLF +
- 'kVA, PF' + CRLF +
- 'XFKVA * Allocationfactor, PF' + CRLF +
- 'kWh/(kWhdays*24) * Cfactor, PF';
- PropertyHelp[24] := 'Percent mean value for load to use for monte carlo studies if no loadshape is assigned to this load. Default is 50.';
- PropertyHelp[25] := 'Percent Std deviation value for load to use for monte carlo studies if no loadshape is assigned to this load. Default is 10.';
- PropertyHelp[26] := 'Percent reduction in active power (watts) per 1% reduction in voltage from 100% rated. Default=1. ' + CRLF +
- ' Typical values range from 0.4 to 0.8. Applies to Model=4 only.' + CRLF +
- ' Intended to represent conservation voltage reduction or voltage optimization measures.';
- PropertyHelp[27] := 'Percent reduction in reactive power (vars) per 1% reduction in voltage from 100% rated. Default=2. ' + CRLF +
- ' Typical values range from 2 to 3. Applies to Model=4 only.' + CRLF +
- ' Intended to represent conservation voltage reduction or voltage optimization measures.';
- PropertyHelp[28] := 'kWh billed for this period. Default is 0. See help on kVA and Cfactor and kWhDays.';
- PropertyHelp[29] := 'Length of kWh billing period in days (24 hr days). Default is 30. Average demand is computed using this value.'; // kwh billing period (24-hr days)
- PropertyHelp[30] := 'Factor relating average kW to peak kW. Default is 4.0. See kWh and kWhdays. See kVA.'; // multiplier from kWh avg to peak kW
- PropertyHelp[31] := 'Default is NONE. Curve describing both watt and var factors as a function of time. ' +
- 'Refers to a LoadShape object with both Mult and Qmult defined. ' +
- 'Define a Loadshape to agree with yearly or daily curve according to the type of analysis being done. ' +
- 'If NONE, the CVRwatts and CVRvars factors are used and assumed constant.';
- PropertyHelp[32] := 'Number of customers, this load. Default is 1.';
- PropertyHelp[33] := 'Array of 7 coefficients:' + CRLF + CRLF +
- ' First 3 are ZIP weighting factors for real power (should sum to 1)' + CRLF +
- ' Next 3 are ZIP weighting factors for reactive power (should sum to 1)' + CRLF +
- ' Last 1 is cut-off voltage in p.u. of base kV; load is 0 below this cut-off' + CRLF +
- ' No defaults; all coefficients must be specified if using model=8.';
- PropertyHelp[34] := 'Percent of load that is series R-L for Harmonic studies. Default is 50. Remainder is assumed to be parallel R and L. ' +
- 'This can have a significant impact on the amount of damping observed in Harmonics solutions.';
- PropertyHelp[35] := 'Relative weighting factor for reliability calcs. Default = 1. Used to designate high priority loads such as hospitals, etc. ' + CRLF + CRLF +
- 'Is multiplied by number of customers and load kW during reliability calcs.';
- PropertyHelp[36] := 'Default = 0.50. Per unit voltage at which the model switches to same as constant Z model (model=2). ' +
- 'This allows more consistent convergence at very low voltaes due to opening switches or solving for fault situations.';
- PropertyHelp[37] := 'Special reactance, pu (based on kVA, kV properties), for the series impedance branch in the load model for HARMONICS analysis. ' +
- 'Generally used to represent motor load blocked rotor reactance. ' +
- 'If not specified (that is, set =0, the default value), the series branch is computed from the percentage of the ' +
- 'nominal load at fundamental frequency specified by the %SERIESRL property. ' + CRLF + CRLF +
- 'Applies to load model in HARMONICS mode only.' + CRLF + CRLF +
- 'A typical value would be approximately 0.20 pu based on kVA * %SeriesRL / 100.0.';
- PropertyHelp[38] := 'X/R ratio of the special harmonics mode reactance specified by the puXHARM property at fundamental frequency. Default is 6. ';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enum properties
+ PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.conn)] := ptruint(@obj.Connection);
+ PropertyOffset2[ord(TProp.conn)] := PtrInt(DSS.ConnectionEnum);
+
+ PropertyType[ord(TProp.model)] := TPropertyType.MappedIntEnumProperty;
+ PropertyOffset[ord(TProp.model)] := ptruint(@obj.FLoadModel);
+ PropertyOffset2[ord(TProp.model)] := PtrInt(LoadModelEnum);
+
+ PropertyType[ord(TProp.status)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.status)] := ptruint(@obj.status);
+ PropertyOffset2[ord(TProp.status)] := PtrInt(LoadStatusEnum);
+
+ // array property
+ PropertyType[ord(TProp.ZIPV)] := TPropertyType.DoubleFArrayProperty;
+ PropertyOffset[ord(TProp.ZIPV)] := ptruint(@obj.ZIPV[1]);
+ PropertyOffset2[ord(TProp.ZIPV)] := 7;
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+
+ // pct properties
+ PropertyScale[ord(TProp.pctmean)] := 0.01;
+ PropertyScale[ord(TProp.pctstddev)] := 0.01;
+ PropertyScale[ord(TProp.pctSeriesRL)] := 0.01;
+ PropertyOffset[ord(TProp.pctmean)] := ptruint(@obj.FpuMean);
+ PropertyOffset[ord(TProp.pctstddev)] := ptruint(@obj.FpuStdDev);
+ PropertyOffset[ord(TProp.pctSeriesRL)] := ptruint(@obj.puSeriesRL);
+
+ // integer properties
+ PropertyOffset[ord(TProp.cls)] := ptruint(@obj.LoadClass);
+ PropertyOffset[ord(TProp.NumCust)] := ptruint(@obj.NumCustomers);
+ PropertyType[ord(TProp.cls)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.NumCust)] := TPropertyType.IntegerProperty;
+
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // object properties
+ AddProperties_Object(
+ [ord(P.yearly), ord(P.daily), ord(P.duty), ord(P.CVRcurve), ord(P.growth)],
+ [@obj.YearlyShapeObj, @obj.DailyShapeObj, @obj.DutyShapeObj, @obj.CVRShapeObj, @obj.GrowthShapeObj],
+ [DSS.LoadShapeClass, DSS.LoadShapeClass, DSS.LoadShapeClass, DSS.LoadShapeClass, DSS.GrowthShapeClass]
+ );
+
+ // double properties (default type)
+ AddProperties_Double(
+ [ord(P.Rneut), ord(P.Xneut), ord(P.kV), ord(P.kW), ord(P.pf), ord(P.kvar), ord(P.kVA),
+ ord(P.RelWeight), ord(P.Vlowpu), ord(P.puXharm), ord(P.XRharm), ord(P.Vminpu), ord(P.VMaxPu), ord(P.Vminnorm),
+ ord(P.Vminemerg), ord(P.CVRwatts), ord(P.CVRvars), ord(P.kwh), ord(P.xfkVA), ord(P.kwhdays),
+ ord(P.Cfactor), ord(P.allocationfactor)],
+ [@obj.Rneut, @obj.Xneut, @obj.kVLoadBase, @obj.kwBase, @obj.PFNominal, @obj.kvarBase, @obj.kVABase,
+ @obj.RelWeighting, @obj.VLowpu, @obj.FpuXHarm, @obj.FXRHarmRatio, @obj.VMinPu, @obj.VMaxPu, @obj.VminNormal,
+ @obj.VminEmerg, @obj.FCVRwattFactor, @obj.FCVRvarFactor, @obj.kWh, @obj.FConnectedkVA, @obj.kWhdays,
+ @obj.FCFactor, @obj.FkVAAllocationFactor]
+ );
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- PropertyHelp[NumPropsThisClass + 1] := 'Name of harmonic current spectrum for this load. Default is "defaultload", which is defined when the DSS starts.';
-
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLoad.NewObject(const ObjName: String): Integer;
+
+function TLoad.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new load object and add it to Load class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TLoadObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLoad.SetNcondsForConnection;
-
+procedure SetNcondsForConnection(Obj: TObj);
begin
- with DSS.ActiveLoadObj do
- begin
+ with Obj do
case Connection of
TLoadConnection.Wye:
NConds := Fnphases + 1;
@@ -543,366 +435,225 @@ procedure TLoad.SetNcondsForConnection;
else
NConds := Fnphases;
end;
- else {nada}
end;
- end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLoad.InterpretConnection(const S: String);
-
-// Accepts (checks only min number of chars required}
-// delta or LL (Case insensitive)
-// Y, wye, or LN
-var
- TestS: String;
-
+procedure TLoadObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- with DSS.ActiveLoadObj do
- begin
- TestS := lowercase(S);
- case TestS[1] of
- 'y', 'w':
- Connection := TLoadConnection.Wye;
- 'd':
- Connection := TLoadConnection.Delta;
- 'l':
- case Tests[2] of
- 'n':
- Connection := TLoadConnection.Wye;
- 'l':
- Connection := TLoadConnection.Delta;
- end;
- end;
-
- SetNCondsForConnection;
+ case Idx of
+ ord(TProp.conn):
+ begin
+ SetNCondsForConnection(self);
- case Connection of
- TLoadConnection.Delta:
- VBase := kVLoadBase * 1000.0;
- else
- case Fnphases of
- 2, 3:
- VBase := kVLoadBase * InvSQRT3x1000;
+ case Connection of
+ TLoadConnection.Delta:
+ VBase := kVLoadBase * 1000.0;
else
- VBase := kVLoadBase * 1000.0;
+ case Fnphases of
+ 2, 3:
+ VBase := kVLoadBase * InvSQRT3x1000;
+ else
+ VBase := kVLoadBase * 1000.0;
+ end;
end;
- end;
- VBase95 := Vminpu * VBase;
- VBase105 := Vmaxpu * VBase;
- VBaseLow := VLowpu * VBase;
-
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
- end;
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLoad.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
+ VBase95 := Vminpu * VBase;
+ VBase105 := Vmaxpu * VBase;
+ VBaseLow := VLowpu * VBase;
-begin
- // continue parsing WITH contents of Parser
- DSS.ActiveLoadObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveLoadObj;
-
- Result := 0;
+ Yorder := Fnconds * Fnterms;
+ Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
+ YPrimInvalid := TRUE;
+ end;
- with DSS.ActiveLoadObj do
- begin
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while (Length(Param) > 0) do
+ ord(TProp.kV), ord(TProp.phases):
begin
- if (Length(ParamName) = 0) then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 580);
- 1:
- Nphases := Parser.Intvalue; // num phases
- 2:
- SetBus(1, param);
- 3:
- kVLoadBase := Parser.DblValue;
- 4:
- kWBase := Parser.DblValue;
- 5:
- PFNominal := Parser.DblValue;
- 6:
- begin
- if (Parser.IntValue >= Ord(Low(TLoadModel))) and (Parser.IntValue <= Ord(High(TLoadModel))) then
- FLoadModel := TLoadModel(Parser.IntValue)
- else
- DoSimpleMsg(Format('Invalid load model (%d).', [Parser.IntValue]), 5004);
- end;
- 7:
- YearlyShape := Param;
- 8:
- DailyShape := Param;
- 9:
- DutyShape := Param;
- 10:
- GrowthShape := Param;
- 11:
- InterpretConnection(Param);
- 12:
- kvarBase := Parser.DblValue;
- 13:
- Rneut := Parser.DblValue;
- 14:
- Xneut := Parser.DblValue;
- 15:
- case lowercase(Param)[1] of
- 'f':
- begin
- Fixed := TRUE;
- ExemptFromLDCurve := FALSE;
- end;
- 'e':
- begin
- Fixed := FALSE;
- ExemptFromLDCurve := TRUE;
- end;
- else
- Fixed := FALSE;
- ExemptFromLDCurve := FALSE;
- end;
- 16:
- LoadClass := Parser.IntValue;
- 17:
- VMinPu := Parser.DblValue;
- 18:
- VMaxPu := Parser.DblValue;
- 19:
- VminNormal := Parser.DblValue;
- 20:
- VminEmerg := Parser.DblValue;
- 21:
- ConnectedkVA := Parser.DblValue;
- 22:
- kVAAllocationFactor := Parser.DblValue;
- 23:
- kVABase := Parser.DblValue;
- 24:
- FpuMean := Parser.DblValue / 100.0;
- 25:
- FpuStdDev := Parser.DblValue / 100.0;
- 26:
- FCVRwattFactor := Parser.DblValue;
- 27:
- FCVRvarFactor := Parser.DblValue;
- 28:
- kWh := Parser.DblValue;
- 29:
- kWhdays := Parser.DblValue;
- 30:
- Cfactor := Parser.DblValue;
- 31:
- CVRShape := Param;
- 32:
- NumCustomers := Parser.IntValue;
- 33:
- begin
- Parser.ParseAsVector(7, PDoubleArray(@ZIPV[1]));
- ZIPVset := True;
- end;
- 34:
- puSeriesRL := Parser.DblValue / 100.0;
- 35:
- RelWeighting := Parser.DblValue;
- 36:
- VLowpu := Parser.DblValue;
- 37:
- FpuXHarm := Parser.DblValue; // 0 means not set
- 38:
- FXRharmRatio := Parser.DblValue;
-
- else
- // Inherited edits
- ClassEdit(DSS.ActiveLoadObj, paramPointer - NumPropsThisClass)
+ if Idx = ord(TProp.phases) then
+ begin
+ Reallocmem(FPhaseCurr, SizeOf(FPhaseCurr^[1]) * FNphases);
+ SetNCondsForConnection(self); // Force Reallocation of terminal info
end;
- // << SIDE EFFECTS >>
- // keep kvar nominal up to date WITH kW and PF
- case ParamPointer of
- 1:
- begin
- SetNcondsForConnection; // Force Reallocation of terminal info
- UpdateVoltageBases;
+ case Connection of
+ TLoadConnection.Delta:
+ VBase := kVLoadBase * 1000.0;
+ else // wye
+ case Fnphases of
+ 2, 3:
+ VBase := kVLoadBase * InvSQRT3x1000;
+ else
+ VBase := kVLoadBase * 1000.0; // 1-phase or unknown
end;
- 3:
- UpdateVoltageBases;
+ end;
+ end;
+
+ ord(TProp.kW):
+ begin
+ LoadSpecType := TLoadSpec.kW_PF;
+ kWRef := kWBase;
+ end;
+ ord(TProp.pf):
+ begin
+ PFChanged := TRUE;
+ PFSpecified := TRUE;
+ end;
- 4:
- begin
- LoadSpecType := TLoadSpec.kW_PF;
- kWRef := kWBase;
- end;
- 5:
- begin
- PFChanged := TRUE;
- PFSpecified := TRUE;
- end;
- {Set shape objects; returns nil if not valid}
- {Sets the kW and kvar properties to match the peak kW demand from the Loadshape}
- 7:
- begin
- YearlyShapeObj := DSS.LoadShapeClass.Find(YearlyShape);
- if Assigned(YearlyShapeObj) then
- with YearlyShapeObj do
- if UseActual then
- begin
- kWref := kWBase;
- kVARref := kVARbase;
- SetkWkvar(MaxP, MaxQ);
- end;
- end;
- 8:
- begin
- DailyShapeObj := DSS.LoadShapeClass.Find(DailyShape);
- if Assigned(DailyShapeObj) then
- with DailyShapeObj do
- if UseActual then
- SetkWkvar(MaxP, MaxQ);
- {If Yearly load shape is not yet defined, make it the same as Daily}
- if YearlyShapeObj = NIL then
- YearlyShapeObj := DailyShapeObj;
- end;
- 9:
- begin
- DutyShapeObj := DSS.LoadShapeClass.Find(DutyShape);
- if Assigned(DutyShapeObj) then
- with DutyShapeObj do
- if UseActual then
- SetkWkvar(MaxP, MaxQ);
- end;
- 10:
- GrowthShapeObj := DSS.GrowthShapeClass.Find(GrowthShape);
+ ord(TProp.allocationfactor):
+ begin
+ FAllocationFactor := FkVAAllocationFactor;
+ LoadSpecType := TLoadSpec.ConnectedkVA_PF;
+ ComputeAllocatedLoad;
+ HasBeenAllocated := TRUE;
+ end;
- 12:
- begin
- LoadSpecType := TLoadSpec.kW_kvar;
- PFSpecified := FALSE;
- kVARref := kVARbase;
- end;// kW, kvar
- {*** see set_xfkva, etc 21, 22: LoadSpectype := 3; // XFKVA*AllocationFactor, PF }
- 23:
- LoadSpecType := TLoadSpec.kVA_PF; // kVA, PF
- {*** see set_kwh, etc 28..30: LoadSpecType := 4; // kWh, days, cfactor, PF }
- 31:
- CVRShapeObj := DSS.LoadShapeClass.Find(CVRshape);
- end;
+ ord(TProp.Cfactor):
+ begin
+ FAllocationFactor := FCFactor;
+ LoadSpecType := TLoadSpec.kWh_PF;
+ ComputeAllocatedLoad;
+ HasBeenAllocated := TRUE;
+ end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ // Set shape objects; returns nil if not valid
+ // Sets the kW and kvar properties to match the peak kW demand from the Loadshape
+ ord(TProp.Yearly):
+ begin
+ if Assigned(YearlyShapeObj) then
+ with YearlyShapeObj do
+ if UseActual then
+ begin
+ kWref := kWBase;
+ kVARref := kVARbase;
+ SetkWkvar(MaxP, MaxQ);
+ end;
+ end;
+ ord(TProp.daily):
+ begin
+ if Assigned(DailyShapeObj) then
+ with DailyShapeObj do
+ if UseActual then
+ SetkWkvar(MaxP, MaxQ);
+ // If Yearly load shape is not yet defined, make it the same as Daily
+ if YearlyShapeObj = NIL then
+ YearlyShapeObj := DailyShapeObj;
+ end;
+ ord(TProp.duty):
+ begin
+ if Assigned(DutyShapeObj) then
+ with DutyShapeObj do
+ if UseActual then
+ SetkWkvar(MaxP, MaxQ);
+ end;
+ ord(TProp.kwh):
+ begin
+ LoadSpecType := TLoadSpec.kWh_PF;
+ FAllocationFactor := FCFactor;
+ ComputeAllocatedLoad;
end;
+ ord(TProp.kvar):
+ begin
+ LoadSpecType := TLoadSpec.kW_kvar;
+ PFSpecified := FALSE;
+ kVARref := kVARbase;
+ end;// kW, kvar
+ {*** see set_xfkva, etc 21, 22: LoadSpectype := 3; // XFKVA*AllocationFactor, PF }
+ ord(TProp.xfkVA):
+ begin
+ LoadSpecType := TLoadSpec.ConnectedkVA_PF;
+ FAllocationFactor := FkVAAllocationFactor;
+ ComputeAllocatedLoad;
+ end;
+ ord(TProp.kwhdays):
+ begin
+ LoadSpecType := TLoadSpec.kWh_PF;
+ ComputeAllocatedLoad;
+ end;
+ ord(TProp.kVA):
+ LoadSpecType := TLoadSpec.kVA_PF; // kVA, PF
+ {*** see set_kwh, etc 28..30: LoadSpecType := 4; // kWh, days, cfactor, PF }
+ ord(TProp.ZIPV):
+ ZIPVset := True;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
+function TLoad.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
+ begin
RecalcElementData;
YPrimInvalid := TRUE;
+ Exclude(Flags, Flg.EditionActive);
end;
-
+ Result := True;
end;
-//----------------------------------------------------------------------------
-function TLoad.MakeLike(const OtherLoadName: String): Integer;
+procedure TLoadObj.MakeLike(OtherPtr: Pointer);
var
- OtherLoad: TLoadObj;
+ Other: TObj;
i: Integer;
begin
- Result := 0;
- {See IF we can find this line name in the present collection}
- OtherLoad := Find(OtherLoadName);
- if OtherLoad <> NIL then
- with DSS.ActiveLoadObj do
- begin
+ inherited MakeLike(OtherPtr); // Take care of inherited class properties
- Connection := OtherLoad.Connection;
+ Other := TObj(OtherPtr);
+ Connection := Other.Connection;
- if Fnphases <> OtherLoad.Fnphases then
- begin
- Nphases := OtherLoad.Fnphases;
- SetNcondsForConnection; // Forces reallocation of terminal stuff
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
- end;
+ if Fnphases <> Other.Fnphases then
+ begin
+ FNphases := Other.Fnphases;
+ SetNCondsForConnection(self); // Forces reallocation of terminal stuff
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
+ end;
- kVLoadBase := OtherLoad.kVLoadBase;
- Vbase := OtherLoad.Vbase;
- VLowpu := OtherLoad.VLowpu;
- Vminpu := OtherLoad.Vminpu;
- Vmaxpu := OtherLoad.Vmaxpu;
- VBaseLow := OtherLoad.VBaseLow;
- Vbase95 := OtherLoad.Vbase95;
- Vbase105 := OtherLoad.Vbase105;
- kWBase := OtherLoad.kWBase;
- kVAbase := OtherLoad.kVABase;
- kvarBase := OtherLoad.kvarBase;
- LoadSpecType := OtherLoad.LoadSpecType;
- WNominal := OtherLoad.WNominal;
- PFNominal := OtherLoad.PFNominal;
- varNominal := OtherLoad.varNominal;
- Rneut := OtherLoad.Rneut;
- Xneut := OtherLoad.Xneut;
- CVRshape := OtherLoad.CVRshape;
- CVRshapeObj := OtherLoad.CVRshapeObj;
- DailyShape := OtherLoad.DailyShape;
- DailyShapeObj := OtherLoad.DailyShapeObj;
- DutyShape := OtherLoad.DutyShape;
- DutyShapeObj := OtherLoad.DutyShapeObj;
- YearlyShape := OtherLoad.YearlyShape;
- YearlyShapeObj := OtherLoad.YearlyShapeObj;
- GrowthShape := OtherLoad.GrowthShape;
- GrowthShapeObj := OtherLoad.GrowthShapeObj;
-// Spectrum := OtherLoad.Spectrum; in base class now
-// SpectrumObj := OtherLoad.SpectrumObj;
- LoadClass := OtherLoad.LoadClass;
- NumCustomers := OtherLoad.NumCustomers;
- FLoadModel := OtherLoad.FLoadModel;
- Fixed := OtherLoad.Fixed;
- ExemptFromLDCurve := OtherLoad.ExemptFromLDCurve;
- FkVAAllocationFactor := OtherLoad.FkVAAllocationFactor;
- FConnectedkVA := OtherLoad.FConnectedkVA;
- FCVRwattFactor := OtherLoad.FCVRwattFactor;
- FCVRvarFactor := OtherLoad.FCVRvarFactor;
- ShapeIsActual := OtherLoad.ShapeIsActual;
- puSeriesRL := OtherLoad.puSeriesRL;
- RelWeighting := OtherLoad.RelWeighting;
-
- ZIPVset := OtherLoad.ZIPVset;
- if ZIPVset then
- for i := 1 to nZIPV do
- ZIPV[i] := OtherLoad.ZIPV[i];
-
- ClassMakeLike(OtherLoad); // Take care of inherited class properties
-
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherLoad.PropertyValue[i];
-
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Load MakeLike: "' + OtherLoadName + '" Not Found.', 581);
+ kVLoadBase := Other.kVLoadBase;
+ Vbase := Other.Vbase;
+ VLowpu := Other.VLowpu;
+ Vminpu := Other.Vminpu;
+ Vmaxpu := Other.Vmaxpu;
+ VBaseLow := Other.VBaseLow;
+ Vbase95 := Other.Vbase95;
+ Vbase105 := Other.Vbase105;
+ kWBase := Other.kWBase;
+ kVAbase := Other.kVABase;
+ kvarBase := Other.kvarBase;
+ LoadSpecType := Other.LoadSpecType;
+ WNominal := Other.WNominal;
+ PFNominal := Other.PFNominal;
+ varNominal := Other.varNominal;
+ Rneut := Other.Rneut;
+ Xneut := Other.Xneut;
+ CVRshapeObj := Other.CVRshapeObj;
+ DailyShapeObj := Other.DailyShapeObj;
+ DutyShapeObj := Other.DutyShapeObj;
+ YearlyShapeObj := Other.YearlyShapeObj;
+ GrowthShapeObj := Other.GrowthShapeObj;
+ LoadClass := Other.LoadClass;
+ NumCustomers := Other.NumCustomers;
+ FLoadModel := Other.FLoadModel;
+ status := Other.status;
+ FkVAAllocationFactor := Other.FkVAAllocationFactor;
+ FConnectedkVA := Other.FConnectedkVA;
+ FCVRwattFactor := Other.FCVRwattFactor;
+ FCVRvarFactor := Other.FCVRvarFactor;
+ ShapeIsActual := Other.ShapeIsActual;
+ puSeriesRL := Other.puSeriesRL;
+ RelWeighting := Other.RelWeighting;
+
+ Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
+ Reallocmem(FPhaseCurr, SizeOf(FPhaseCurr^[1]) * FNphases);
+ ZIPVset := Other.ZIPVset;
+ if ZIPVset then
+ for i := 1 to nZIPV do
+ ZIPV[i] := Other.ZIPV[i];
end;
-//----------------------------------------------------------------------------
constructor TLoadObj.Create(ParClass: TDSSClass; const SourceName: String);
begin
+ if ParClass = nil then Exit;
+
inherited create(ParClass);
- Name := LowerCase(SourceName);
+ Name := AnsiLowerCase(SourceName);
DSSObjType := ParClass.DSSClassType;
Fnphases := 3;
@@ -917,15 +668,10 @@ constructor TLoadObj.Create(ParClass: TDSSClass; const SourceName: String);
Rneut := -1.0; // signify neutral is open
Xneut := 0.0;
- YearlyShape := '';
YearlyShapeObj := NIL; // IF YearlyShapeobj = nil THEN the load alway stays nominal * global multipliers
- DailyShape := '';
DailyShapeObj := NIL; // IF DaillyShapeobj = nil THEN the load alway stays nominal * global multipliers
- DutyShape := '';
DutyShapeObj := NIL; // IF DutyShapeobj = nil THEN the load alway stays nominal * global multipliers
- Growthshape := '';
GrowthShapeObj := NIL; // IF grwothshapeobj = nil THEN the load alway stays nominal * global multipliers
- CVRShape := '';
CVRShapeObj := NIL;
Connection := TLoadConnection.Wye; // Wye (star)
FLoadModel := TLoadModel.ConstPQ; // changed from 2 RCD {easiest to solve}
@@ -963,10 +709,9 @@ constructor TLoadObj.Create(ParClass: TDSSClass; const SourceName: String);
VBase95 := VminPu * Vbase;
VBase105 := VMaxPU * Vbase;
Yorder := Fnterms * Fnconds;
- RandomMult := 1.0;
- Fixed := FALSE;
- ExemptFromLDCurve := FALSE;
+ RandomMult := 1.0;
+ status := TLoadStatus.Variable;
FpuXHarm := 0.0; // zero signifies not specified.
FXRHarmRatio := 6.0;
@@ -975,25 +720,24 @@ constructor TLoadObj.Create(ParClass: TDSSClass; const SourceName: String);
FpuStdDev := 0.1;
UE_Factor := 0.0;
EEN_Factor := 0.0;
- Spectrum := 'defaultload'; // override base class definition
+ SpectrumObj := DSS.SpectrumClass.DefaultLoad; // override base class definition
HarmMag := NIL;
HarmAng := NIL;
puSeriesRL := 0.50;
FPhaseCurr := NIL; // storage for intermediate current computation
- // allocated in Recalcelementdata
+ // allocated in Recalcelementdata
ZIPVset := False;
- InitPropertyValues(0);
-
+ Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
+ Reallocmem(FPhaseCurr, SizeOf(FPhaseCurr^[1]) * FNphases);
RecalcElementData;
-
end;
-
-//----------------------------------------------------------------------------
destructor TLoadObj.Destroy;
begin
+ if ParentClass = nil then Exit;
+
YPrimOpenCond.Free;
ReallocMem(HarmMag, 0);
ReallocMem(HarmAng, 0);
@@ -1002,7 +746,6 @@ destructor TLoadObj.Destroy;
inherited Destroy;
end;
-//----------------------------------------------------------------------------
procedure TLoadObj.Randomize(Opt: Integer);
begin
case Opt of
@@ -1020,14 +763,10 @@ procedure TLoadObj.Randomize(Opt: Integer);
RandomMult := QuasiLognormal(YearlyShapeObj.Mean)
else
RandomMult := QuasiLognormal(FpuMean);
- else
- {nada}
end;
end;
-//----------------------------------------------------------------------------
procedure TLoadObj.CalcDailyMult(Hr: Double);
-
begin
if DailyShapeObj <> NIL then
begin
@@ -1038,10 +777,7 @@ procedure TLoadObj.CalcDailyMult(Hr: Double);
ShapeFactor := Cmplx(1.0, 1.0); // Default to no daily variation
end;
-
-//----------------------------------------------------------------------------
procedure TLoadObj.CalcDutyMult(Hr: Double);
-
begin
if DutyShapeObj <> NIL then
begin
@@ -1052,41 +788,35 @@ procedure TLoadObj.CalcDutyMult(Hr: Double);
CalcDailyMult(Hr); // Default to Daily Mult IF no duty curve specified
end;
-//----------------------------------------------------------------------------
procedure TLoadObj.CalcYearlyMult(Hr: Double);
-
begin
-{Yearly curve is assumed to be hourly only}
+ // Yearly curve is assumed to be hourly only
if YearlyShapeObj <> NIL then
begin
ShapeFactor := YearlyShapeObj.GetMultAtHour(Hr);
ShapeIsActual := YearlyShapeObj.UseActual;
end
else
+ // Defaults to no variation
ShapeFactor := Cmplx(1.0, 1.0);
- // Defaults to no variation
end;
-//----------------------------------------------------------------------------
procedure TLoadObj.CalcCVRMult(Hr: Double);
-
var
CVRFactor: Complex;
begin
- {CVR curve is assumed to be used in a yearly simulation}
+ // CVR curve is assumed to be used in a yearly simulation
if CVRShapeObj <> NIL then
begin
CVRFactor := CVRShapeObj.GetMultAtHour(Hr); {Complex}
FCVRWattFactor := CVRFactor.re;
FCVRvarFactor := CVRFactor.im;
end;
- {Else FCVRWattFactor, etc. remain unchanged}
+ // Else FCVRWattFactor, etc. remain unchanged
end;
-//----------------------------------------------------------------------------
function TLoadObj.GrowthFactor(Year: Integer): Double;
-
begin
if Year = 0 then
LastGrowthFactor := 1.0 // default all to 1 in year 0 ; use base values
@@ -1102,8 +832,6 @@ function TLoadObj.GrowthFactor(Year: Integer): Double;
Result := LastGrowthFactor; // for Now
end;
-
-//----------------------------------------------------------------------------
procedure TLoadObj.SetkWkvar(const PkW, Qkvar: Double);
begin
kWBase := PkW;
@@ -1117,12 +845,11 @@ procedure TLoadObj.SetkWkvar(const PkW, Qkvar: Double);
procedure TLoadObj.SetNominalLoad;
var
Factor: Double;
-
begin
ShapeFactor := CDOUBLEONE;
ShapeIsActual := FALSE;
with ActiveCircuit.Solution do
- if Fixed then
+ if status = TLoadStatus.Fixed then
begin
Factor := GrowthFactor(Year); // For fixed loads, consider only growth factor
end
@@ -1130,14 +857,14 @@ procedure TLoadObj.SetNominalLoad;
case Mode of
TSolveMode.SNAPSHOT,
TSolveMode.HARMONICMODE:
- if ExemptFromLDCurve then
+ if status = TLoadStatus.Exempt then
Factor := GrowthFactor(Year)
else
Factor := ActiveCircuit.LoadMultiplier * GrowthFactor(Year);
TSolveMode.DAILYMODE:
begin
Factor := GrowthFactor(Year);
- if not ExemptFromLDCurve then
+ if status <> TLoadStatus.Exempt then
Factor := Factor * ActiveCircuit.LoadMultiplier;
CalcDailyMult(DynaVars.dblHour);
end;
@@ -1151,7 +878,7 @@ procedure TLoadObj.SetNominalLoad;
TSolveMode.DUTYCYCLE:
begin
Factor := GrowthFactor(Year);
- if not ExemptFromLDCurve then
+ if status <> TLoadStatus.Exempt then
Factor := Factor * ActiveCircuit.LoadMultiplier;
CalcDutyMult(DynaVars.dblHour);
end;
@@ -1159,9 +886,9 @@ procedure TLoadObj.SetNominalLoad;
TSolveMode.DYNAMICMODE:
begin
Factor := GrowthFactor(Year);
- if not ExemptFromLDCurve then
+ if status <> TLoadStatus.Exempt then
Factor := Factor * ActiveCircuit.LoadMultiplier;
- // This mode allows use of one class of load shape
+ // This mode allows use of one class of load shape
case ActiveCircuit.ActiveLoadShapeClass of
USEDAILY:
CalcDailyMult(DynaVars.dblHour);
@@ -1177,7 +904,7 @@ procedure TLoadObj.SetNominalLoad;
begin
Randomize(RandomType);
Factor := RandomMult * GrowthFactor(Year);
- if not ExemptFromLDCurve then
+ if status <> TLoadStatus.Exempt then
Factor := Factor * ActiveCircuit.LoadMultiplier;
end;
@@ -1188,7 +915,7 @@ procedure TLoadObj.SetNominalLoad;
begin
Factor := GrowthFactor(Year);
CalcDailyMult(DynaVars.dblHour);
- if not ExemptFromLDCurve then
+ if status <> TLoadStatus.Exempt then
Factor := Factor * ActiveCircuit.LoadMultiplier;
end;
TSolveMode.PEAKDAY:
@@ -1222,66 +949,60 @@ procedure TLoadObj.SetNominalLoad;
varNominal := 1000.0 * kvarBase * Factor * ShapeFactor.im / Fnphases;
end;
- Yeq := CDivReal(Cmplx(WNominal, -VarNominal), Sqr(Vbase));
+ Yeq := Cmplx(WNominal, -VarNominal) / Sqr(Vbase);
if (Vminpu <> 0.0) then
- Yeq95 := CDivReal(Yeq, sqr(Vminpu)) // at 95% voltage
+ Yeq95 := Yeq / sqr(Vminpu) // at 95% voltage
else
Yeq95 := CZERO;
if (Vmaxpu <> 0.0) then
- Yeq105 := CDivReal(Yeq, sqr(Vmaxpu)) // at 105% voltage
+ Yeq105 := Yeq / sqr(Vmaxpu) // at 105% voltage
else
Yeq105 := Yeq;
if (Vmaxpu <> 0.0) then
- Yeq105I := CDivReal(Yeq, Vmaxpu) // at 105% voltage for Constant I ***Added by Celso & Paulo
+ Yeq105I := Yeq / Vmaxpu // at 105% voltage for Constant I ***Added by Celso & Paulo
else
Yeq105I := Yeq; // **Added by Celso & Paulo
- {New code to help with convergence at low voltages}
- ILow := (CmulReal(Yeq, VbaseLow));
- I95 := (CmulReal(Yeq95, Vbase95));
- M95 := CDivReal(Csub(I95, ILow), (VBase95 - VBaseLow)); // (I95 - ILow)/(Vbase95 - VbaseLow); ***Added by Celso & Paulo
- IBase := (CmulReal(Yeq, VBase)); // ***Added by Celso & Paulo
- M95I := CDivReal(Csub(IBase, ILow), (VBase95 - VBaseLow)); // (IBase - ILow)/(Vbase95 - VbaseLow); ***Added by Celso & Paulo
-
+ // New code to help with convergence at low voltages
+ ILow := (Yeq * VbaseLow);
+ I95 := (Yeq95 * Vbase95);
+ M95 := (I95 - ILow) / (VBase95 - VBaseLow); // (I95 - ILow)/(Vbase95 - VbaseLow); ***Added by Celso & Paulo
+ IBase := (Yeq * VBase); // ***Added by Celso & Paulo
+ M95I := (IBase - ILow) / (VBase95 - VBaseLow); // (IBase - ILow)/(Vbase95 - VbaseLow); ***Added by Celso & Paulo
end;
-//----------------------------------------------------------------------------
procedure TLoadObj.RecalcElementData;
-
-
begin
-
VBaseLow := VLowpu * VBase;
VBase95 := VMinPu * VBase;
VBase105 := VMaxPu * VBase;
-
- {Set kW and kvar from root values of kVA and PF}
+ // Set kW and kvar from root values of kVA and PF
case LoadSpecType of
TLoadSpec.kW_PF:
- begin {kW, PF}
+ begin
kvarBase := kWBase * SQRT(1.0 / SQR(PFNominal) - 1.0);
if PFNominal < 0.0 then
kvarBase := -kvarBase;
kVABase := SQRT(SQR(kWbase) + SQR(kvarBase));
end;
TLoadSpec.kW_kvar:
- begin {kW, kvar -- need to set PFNominal}
+ begin // need to set PFNominal
kVABase := SQRT(SQR(kWbase) + SQR(kvarBase));
if kVABase > 0.0 then
begin
PFNominal := kWBase / kVABase;
- {If kW and kvar are different signs, PF is negative}
+ // If kW and kvar are different signs, PF is negative
if kvarbase <> 0.0 then
PFNominal := PFNominal * Sign(kWbase * kvarbase);
end;
- {Else leave it as it is}
+ // Else leave it as it is
end;
TLoadSpec.kVA_PF:
- begin {kVA, PF}
+ begin
kWbase := kVABase * Abs(PFNominal);
kWref := kWBase;
kvarBase := kWBase * SQRT(1.0 / SQR(PFNominal) - 1.0);
@@ -1297,42 +1018,12 @@ procedure TLoadObj.RecalcElementData;
kvarBase := -kvarBase;
kVABase := SQRT(SQR(kWref) + SQR(kVARref));
end;
-
-
-{ done automagically in Property set... 3, 4: ComputeAllocatedLoad; }
+ // done automagically in Property set... 3, 4: ComputeAllocatedLoad;
else
end;
SetNominalLoad;
- {Now check for errors. IF any of these came out nil and the string was not nil, give warning}
- if CompareText(YearlyShape, 'none') = 0 then
- YearlyShape := '';
- if CompareText(DailyShape, 'none') = 0 then
- DailyShape := '';
- if CompareText(DutyShape, 'none') = 0 then
- DutyShape := '';
-
- if YearlyShapeObj = NIL then
- if Length(YearlyShape) > 0 then
- DoSimpleMsg('WARNING! Yearly load shape: "' + YearlyShape + '" Not Found.', 583);
- if DailyShapeObj = NIL then
- if Length(DailyShape) > 0 then
- DoSimpleMsg('WARNING! Daily load shape: "' + DailyShape + '" Not Found.', 584);
- if DutyShapeObj = NIL then
- if Length(DutyShape) > 0 then
- DoSimpleMsg('WARNING! Duty load shape: "' + DutyShape + '" Not Found.', 585);
- if GrowthShapeObj = NIL then
- if Length(GrowthShape) > 0 then
- DoSimpleMsg('WARNING! Yearly Growth shape: "' + GrowthShape + '" Not Found.', 586);
- if CVRShapeObj = NIL then
- if Length(CVRShape) > 0 then
- DoSimpleMsg('WARNING! CVR Shape shape: "' + CVRShape + '" Not Found.', 586);
-
- SpectrumObj := DSS.SpectrumClass.Find(Spectrum);
- if SpectrumObj = NIL then
- DoSimpleMsg('ERROR! Spectrum "' + Spectrum + '" Not Found.', 587);
-
if Rneut < 0.0 then // flag FOR open neutral
YNeut := Cmplx(0.0, 0.0)
else
@@ -1344,25 +1035,17 @@ procedure TLoadObj.RecalcElementData;
varBase := 1000.0 * kvarBase / Fnphases;
YQFixed := -varBase / SQR(VBase);
- Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
- Reallocmem(FPhaseCurr, SizeOf(FPhaseCurr^[1]) * FNphases);
-
PFChanged := FALSE;
-
end;
-//----------------------------------------------------------------------------
procedure TLoadObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
-
var
Y, Yij,
ZSeries: Complex;
i, j: Integer;
FreqMultiplier: Double;
XseriesOhms: Double;
-
begin
-
FYprimFreq := ActiveCircuit.Solution.Frequency;
FreqMultiplier := FYprimFreq / BaseFrequency;
@@ -1371,31 +1054,30 @@ procedure TLoadObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
begin // Harmonic Mode and other than fundamental frequency
if ActiveCircuit.NeglectLoadY then
begin
- {Just a small value so things don't die and we get the actual injection current out the terminal}
+ // Just a small value so things don't die and we get the actual injection current out the terminal
Y := cmplx(Epsilon, 0.0)
end
- else
- // compute equivalent Y assuming some of the load is series R-L and the rest is parallel R-L
+ else // compute equivalent Y assuming some of the load is series R-L and the rest is parallel R-L
begin
- // Parallel R-L part of the Load model for harmonics mode
- // Based in equivalent Y at 100% voltage
- Y := CmulReal(Yeq, (1.0 - puSeriesRL));
+ // Parallel R-L part of the Load model for harmonics mode
+ // Based in equivalent Y at 100% voltage
+ Y := Yeq * (1.0 - puSeriesRL);
Y.im := Y.im / FreqMultiplier; {Correct reactive part for frequency}
- // Series-connected R-L part
+ // Series-connected R-L part
if puSeriesRL <> 0.0 then
begin
if FpuXharm > 0.0 then
begin // compute Zseries from special harmonic reactance for representing motors.
- // the series branch is assumed to represent the motor
+ // the series branch is assumed to represent the motor
XseriesOhms := SQR(kVLoadBase) * 1000.0 / (kVABase * puSeriesRL) * FpuXharm;
Zseries := cmplx(XseriesOhms / FXRharmRatio, XSeriesOhms);
end
else // Compute Zseries from nominal load value
- Zseries := Cinv(CmulReal(Yeq, puSeriesRL));
+ Zseries := Cinv(Yeq * puSeriesRL);
- Zseries.im := Zseries.im * FreqMultiplier; {Correct reactive part for frequency}
- Y := Cadd(Cinv(ZSeries), Y); // convert to admittance and add into Y
+ Zseries.im := Zseries.im * FreqMultiplier; // Correct reactive part for frequency
+ Y := Cinv(ZSeries) + Y; // convert to admittance and add into Y
end;
end;
@@ -1403,14 +1085,13 @@ procedure TLoadObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
else
begin // not Harmonic mode
Y := Yeq;
- Y.im := Y.im / FreqMultiplier; {Correct reactive part for frequency}
+ Y.im := Y.im / FreqMultiplier; // Correct reactive part for frequency
end;
- Yij := Cnegate(Y);
+ Yij := -Y;
case Connection of
-
TLoadConnection.Wye:
begin // WYE
for i := 1 to Fnphases do
@@ -1421,10 +1102,10 @@ procedure TLoadObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
end;
Ymatrix.AddElement(Fnconds, Fnconds, YNeut); // Neutral
- { If neutral is floating, make sure there is some small
- connection to ground by increasing the last diagonal slightly }
+ // If neutral is floating, make sure there is some small
+ // connection to ground by increasing the last diagonal slightly
if Rneut < 0.0 then
- Ymatrix.SetElement(Fnconds, Fnconds, Cmulreal(Ymatrix.GetElement(Fnconds, Fnconds), 1.000001));
+ Ymatrix.SetElement(Fnconds, Fnconds, Ymatrix.GetElement(Fnconds, Fnconds) * 1.000001)
end;
TLoadConnection.Delta:
begin // Delta or L-L
@@ -1439,24 +1120,16 @@ procedure TLoadObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
end;
end;
end;
-
end;
-
-//----------------------------------------------------------------------------
procedure TLoadObj.CalcYPrim;
-
-
// If doing an analysis that requires the load to be modeled as an impedance
// then put all in.
-
var
i: Integer;
-
begin
-
-// Build only YPrim Shunt for a Load then Copy to YPrim
-// Build a dummy Yprim Series so that CalcV does not fail
+ // Build only YPrim Shunt for a Load then Copy to YPrim
+ // Build a dummy Yprim Series so that CalcV does not fail
if YPrimInvalid then
begin
if YPrim_Shunt <> NIL then
@@ -1479,7 +1152,6 @@ procedure TLoadObj.CalcYPrim;
if ActiveCircuit.Solution.LoadModel = POWERFLOW then
begin
-
SetNominalLoad; // same as admittance model
CalcYPrimMatrix(YPrim_Shunt);
@@ -1494,70 +1166,45 @@ procedure TLoadObj.CalcYPrim;
// Set YPrim_Series based on diagonals of YPrim_shunt so that CalcVoltages doesn't fail
for i := 1 to Yorder do
- Yprim_Series.SetElement(i, i, CmulReal(Yprim_Shunt.Getelement(i, i), 1.0e-10));
+ Yprim_Series.SetElement(i, i, Yprim_Shunt.Getelement(i, i) * 1.0e-10);
YPrim.CopyFrom(YPrim_Shunt);
// Account for Open Conductors
inherited CalcYPrim;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TLoadObj.StickCurrInTerminalArray(TermArray: pComplexArray; const Curr: Complex; i: Integer);
- {Put the current into the proper location according to connection}
-
+procedure TLoadObj.StickCurrInTerminalArray(TermArray: pComplexArray; const Curr: Complex; i: Integer); inline;
+// Put the current into the proper location according to connection
var
j: Integer;
-
begin
case Connection of
TLoadConnection.Wye:
begin //Wye
- Caccum(TermArray^[i], Cnegate(Curr));
- Caccum(TermArray^[Fnconds], Curr); // Neutral
+ TermArray^[i] -= Curr;
+ TermArray^[Fnconds] += Curr; // Neutral
end;
TLoadConnection.Delta:
begin //DELTA
- Caccum(TermArray^[i], Cnegate(Curr));
+ TermArray^[i] -= Curr;
j := i + 1;
if j > Fnconds then
j := 1; // rotate the phases
- Caccum(TermArray^[j], Curr);
+ TermArray^[j] += Curr;
end;
end;
end;
-procedure TLoadObj.UpdateVoltageBases;
-begin
- with DSS.ActiveLoadObj do
- case Connection of
- TLoadConnection.Delta:
- VBase := kVLoadBase * 1000.0;
- else {wye}
- case Fnphases of
- 2, 3:
- VBase := kVLoadBase * InvSQRT3x1000;
- else
- VBase := kVLoadBase * 1000.0; {1-phase or unknown}
- end;
- end;
-
-end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TLoadObj.DoConstantPQLoad;
-
var
i: Integer;
Curr: Complex;
V: Complex;
Vmag: Double;
-
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the load
ZeroITerminal;
@@ -1568,35 +1215,31 @@ procedure TLoadObj.DoConstantPQLoad;
VMag := Cabs(V);
if VMag <= VBaseLow then
- Curr := Cmul(Yeq, V) // Below VbaseZ resort to linear equal to Yprim contribution
+ Curr := Yeq * V // Below VbaseZ resort to linear equal to Yprim contribution
else
if VMag <= VBase95 then
- Curr := Cmul(InterpolateY95_YLow(Vmag), V) // Voltage between Vminpu and Vlow
+ Curr := InterpolateY95_YLow(Vmag) * V // Voltage between Vminpu and Vlow
else
if VMag > VBase105 then
- Curr := Cmul(Yeq105, V) // above 105% use an impedance model
+ Curr := Yeq105 * V // above 105% use an impedance model
else
- Curr := Conjg(Cdiv(Cmplx(WNominal, varNominal), V)); // Above 95%, constant PQ
+ Curr := cong(Cmplx(WNominal, varNominal) / V); // Above 95%, constant PQ
- // Save this value in case the Load value is different than the terminal value (see InitHarmonics)
+ // Save this value in case the Load value is different than the terminal value (see InitHarmonics)
FPhaseCurr^[i] := Curr;
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TLoadObj.DoConstantZLoad;
var
i: Integer;
Curr: Complex;
-
begin
-
-// Assume Yeq is kept up to date
+ // Assume Yeq is kept up to date
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the load
@@ -1604,19 +1247,17 @@ procedure TLoadObj.DoConstantZLoad;
for i := 1 to Fnphases do
begin
- Curr := Cmul(Yeq, Vterminal^[i]);
+ Curr := Yeq * Vterminal^[i];
// Save this value in case the Load value is different than the terminal value (see InitHarmonics)
FPhaseCurr^[i] := Curr;
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TLoadObj.DoMotorTypeLoad;
// Constant P, quadratic Q
var
@@ -1624,9 +1265,7 @@ procedure TLoadObj.DoMotorTypeLoad;
Curr: Complex;
V: Complex;
VMag: Double;
-
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the load
ZeroITerminal;
@@ -1636,48 +1275,40 @@ procedure TLoadObj.DoMotorTypeLoad;
V := Vterminal^[i];
VMag := Cabs(V);
if VMag <= VBaseLow then
- Curr := Cmul(Yeq, V) // Below VbaseZ resort to linear equal to Yprim contribution
+ Curr := Yeq * V // Below VbaseZ resort to linear equal to Yprim contribution
else
if VMag <= VBase95 then
- Curr := Cmul(InterpolateY95_YLow(Vmag), V) // Voltage between Vminpu and Vlow
+ Curr := InterpolateY95_YLow(Vmag) * V // Voltage between Vminpu and Vlow
else
if VMag > VBase105 then
- Curr := Cmul(Yeq105, V) // above 105% use an impedance model
+ Curr := Yeq105 * V // above 105% use an impedance model
else
begin
- Curr := Conjg(Cdiv(Cmplx(WNominal, 0.0), V)); // Above 95%, constant P
- Caccum(Curr, Cmul(Cmplx(0.0, Yeq.im), V)); // add in Q component of current
+ Curr := cong(Cmplx(WNominal, 0.0) / V); // Above 95%, constant P
+ Curr += Cmplx(0.0, Yeq.im) * V; // add in Q component of current
end;
- // Save this value in case the Load value is different than the terminal value (see InitHarmonics)
+ // Save this value in case the Load value is different than the terminal value (see InitHarmonics)
FPhaseCurr^[i] := Curr;
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
end;
procedure TLoadObj.DoConstantILoad;
-
// Constant Current Load
-
var
i: Integer;
V: Complex;
Vmag: Double;
Curr: Complex;
-
begin
+ // Computes the current assuming the voltage mag is Vbase
+ // Just uses the phase angle off the voltage
-// Computes the current assuming the voltage mag is Vbase
-// Just uses the phase angle off the voltage
-
-{
- Injection = [s/v]* = [ (P+jQ)/(Vbase * V/|V|)]*
-}
-
-
+ // Injection = [s/v]* = [ (P+jQ)/(Vbase * V/|V|)]*
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the load
ZeroITerminal;
@@ -1688,28 +1319,32 @@ procedure TLoadObj.DoConstantILoad;
Vmag := Cabs(V);
if VMag <= VBaseLow then
- Curr := Cmul(Yeq, V) // Below VbaseZ resort to linear equal to Yprim contribution
- else
- if VMag <= VBase95 then
- Curr := Cmul(InterpolateY95I_YLow(Vmag), V) // Voltage between Vminpu and Vlow ***Added by Celso & Paulo
- else
- if VMag > VBase105 then
- Curr := Cmul(Yeq105I, V) // above 105% use an impedance model
+ Curr := Yeq * V // Below VbaseZ resort to linear equal to Yprim contribution
+ else if VMag <= VBase95 then
+ Curr := InterpolateY95I_YLow(Vmag) * V // Voltage between Vminpu and Vlow ***Added by Celso & Paulo
+ else if VMag > VBase105 then
+ Curr := Yeq105I * V // above 105% use an impedance model
else
- begin
- Curr := Conjg(Cdiv(Cmplx(WNominal, varNominal), CMulReal(CDivReal(V, Vmag), Vbase)));
- end;
+ Curr := cong(Cmplx(WNominal, varNominal) / ((V / Vmag) * Vbase));
// Save this value in case the Load value is different than the terminal value (see InitHarmonics)
FPhaseCurr^[i] := Curr;
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
-
end;
+
+{$IFDEF UNIX}
+//TODO: test in all platforms
+function exp(x: double): double; cdecl; external;
+function pow(x, y: double): double; cdecl; external;
+{$ELSE}
+{$DEFINE pow:=Math.Power}
+{$ENDIF}
+
procedure TLoadObj.DoZIPVModel;
var
i: Integer;
@@ -1717,19 +1352,53 @@ procedure TLoadObj.DoZIPVModel;
CurrZ: Complex;
CurrI: Complex;
CurrP: Complex;
- V: Complex;
+ V, Vaux: Complex;
Vmag: Double;
vx, evx, yv: Double;
begin
if not ZIPVset then
begin
- DoSimpleMsg('ZIPV is not set. Aborting...', 1366);
+ DoSimpleMsg(_('ZIPV is not set. Aborting...'), 1366);
DSS.SolutionAbort := True;
Exit;
end;
+{$IFDEF NO_ZIPV_MANUAL_OPT}
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the load
+{$ELSE}
+ //->CalcYPrimContribution
+ ComputeVTerminal;
+
+ // Apply these voltages to Yprim
+ YPrim.MVMult(InjCurrent, Vterminal);
+ //<-CalcYPrimContribution
+
+ //-> CalcVTerminalPhase
+ // Establish phase voltages and stick in Vtemp
+ case Connection of
+ TLoadConnection.Wye:
+ begin
+ Vaux := Vterminal[Fnconds];
+ for i := 1 to Fnphases do
+ Vterminal[i] -= Vaux;
+ end;
+ TLoadConnection.Delta:
+ begin
+ Vaux := Vterminal[1];
+ for i := 1 to Fnphases do
+ begin
+ if i >= Fnconds then
+ Vterminal[i] -= Vaux
+ else
+ Vterminal[i] -= Vterminal[i + 1]; // VDiff(NodeRef^[i], NodeRef^[j]);
+ end;
+ end;
+ end;
+ LoadSolutionCount := ActiveCircuit.Solution.SolutionCount;
+ //<- CalcVTerminalPhase
+{$ENDIF}
+
ZeroITerminal;
for i := 1 to Fnphases do
@@ -1737,58 +1406,84 @@ procedure TLoadObj.DoZIPVModel;
V := Vterminal^[i];
VMag := Cabs(V);
- { May 31, 2016 changed to linear model below VbaseLow -- converges better for low voltage}
+ // May 31, 2016 changed to linear model below VbaseLow -- converges better for low voltage
if VMag <= VBaseLow then
- Curr := Cmul(Yeq, V) // Below VbaseZ resort to linear equal to Yprim contribution
+ Curr := Yeq * V // Below VbaseZ resort to linear equal to Yprim contribution
else
begin
+ CurrZ := CZERO;
+ CurrI := CZERO;
+ CurrP := CZERO;
+
if VMag <= VBase95 then
begin
- CurrZ := Cmul(Cmplx(Yeq.re * ZIPV[1], Yeq.im * ZIPV[4]), V); // ***Changed by Celso & Paulow
- CurrP := Cmul(Cmplx(InterpolateY95_YLow(Vmag).re * ZIPV[3], InterpolateY95_YLow(Vmag).im * ZIPV[6]), V); // ***Changed by Celso & Paulo
- CurrI := Cmul(Cmplx(InterpolateY95I_YLow(Vmag).re * ZIPV[2], InterpolateY95I_YLow(Vmag).im * ZIPV[5]), V); // ***Changed by Celso & Paulo
- Curr := CAdd(CurrZ, CAdd(CurrI, CurrP)); // ***Changed by Celso & Paulo
+ if (ZIPV[1] <> 0) or (ZIPV[4] <> 0) then
+ CurrZ := Cmplx(Yeq.re * ZIPV[1], Yeq.im * ZIPV[4]);
+
+ if (ZIPV[3] <> 0) or (ZIPV[6] <> 0) then
+ CurrP := Cmplx(InterpolateY95_YLow(Vmag).re * ZIPV[3], InterpolateY95_YLow(Vmag).im * ZIPV[6]);
+
+ if (ZIPV[2] <> 0) or (ZIPV[5] <> 0) then
+ CurrI := Cmplx(InterpolateY95I_YLow(Vmag).re * ZIPV[2], InterpolateY95I_YLow(Vmag).im * ZIPV[5]);
+
+ Curr := (CurrZ + CurrI + CurrP) * V;
end
else
if VMag > VBase105 then
begin
- CurrZ := Cmul(Cmplx(Yeq.re * ZIPV[1], Yeq.im * ZIPV[4]), V); // ***Changed by Celso & Paulo
- CurrP := Cmul(Cmplx(Yeq105.re * ZIPV[3], Yeq105.im * ZIPV[6]), V); // ***Changed by Celso & Paulo
- CurrI := Cmul(Cmplx(Yeq105I.re * ZIPV[2], Yeq105I.im * ZIPV[5]), V); // ***Changed by Celso & Paulo
- Curr := CAdd(CurrZ, CAdd(CurrI, CurrP));
+ if (ZIPV[1] <> 0) or (ZIPV[4] <> 0) then
+ CurrZ := Cmplx(Yeq.re * ZIPV[1], Yeq.im * ZIPV[4]);
+
+ if (ZIPV[3] <> 0) or (ZIPV[6] <> 0) then
+ CurrP := Cmplx(Yeq105.re * ZIPV[3], Yeq105.im * ZIPV[6]);
+
+ if (ZIPV[2] <> 0) or (ZIPV[5] <> 0) then
+ CurrI := Cmplx(Yeq105I.re * ZIPV[2], Yeq105I.im * ZIPV[5]);
+
+ Curr := (CurrZ + CurrI + CurrP) * V;
end
else
begin
- CurrZ := Cmul(Cmplx(Yeq.re * ZIPV[1], Yeq.im * ZIPV[4]), V);
- CurrI := Conjg(Cdiv(Cmplx(WNominal * ZIPV[2], varNominal * ZIPV[5]), CMulReal(CDivReal(V, Cabs(V)), Vbase)));
- CurrP := Conjg(Cdiv(Cmplx(WNominal * ZIPV[3], varNominal * ZIPV[6]), V));
- Curr := CAdd(CurrZ, CAdd(CurrI, CurrP));
+ if (ZIPV[1] <> 0) or (ZIPV[4] <> 0) then
+ CurrZ := Cmplx(Yeq.re * ZIPV[1], Yeq.im * ZIPV[4]) * V;
+
+ if (ZIPV[2] <> 0) or (ZIPV[5] <> 0) then
+ CurrI := cong(Cmplx(WNominal * ZIPV[2], varNominal * ZIPV[5]) / ((V / Cabs(V)) * Vbase));
+
+ if (ZIPV[3] <> 0) or (ZIPV[6] <> 0) then
+ CurrP := cong(Cmplx(WNominal * ZIPV[3], varNominal * ZIPV[6]) / V);
+
+ Curr := CurrZ + CurrI + CurrP;
end;
- // low-voltage drop-out
+ // low-voltage drop-out
if ZIPV[7] > 0.0 then
begin
vx := 500.0 * (Vmag / Vbase - ZIPV[7]);
- evx := exp(2 * vx);
- yv := 0.5 * (1 + (evx - 1) / (evx + 1));
- Curr := CMulReal(Curr, yv);
+{$IFNDEF NO_ZIPV_MANUAL_OPT}
+ if vx < 20 then // if >= 20, yv is 1 for a float64
+ begin
+{$ENDIF}
+ evx := exp(2 * vx);
+ yv := 0.5 * (1 + (evx - 1) / (evx + 1));
+ Curr := Curr * yv;
+{$IFNDEF NO_ZIPV_MANUAL_OPT}
+ end;
+{$ENDIF}
end;
-
end;
- // Save this value in case the Load value is different than the terminal value (see InitHarmonics)
+ // Save this value in case the Load value is different than the terminal value (see InitHarmonics)
FPhaseCurr^[i] := Curr;
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TLoadObj.DoCVRModel;
// Linear P, quadratic Q
-
var
i: Integer;
V: Complex;
@@ -1798,88 +1493,80 @@ procedure TLoadObj.DoCVRModel;
VarFactor: Double;
Vmag: Double;
VRatio: Double;
-
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the load
ZeroITerminal;
-
try
-
for i := 1 to Fnphases do
begin
V := Vterminal^[i];
Vmag := Cabs(V);
if VMag <= VBaseLow then
- Curr := Cmul(Yeq, V) // Below VbaseZ resort to linear equal to Yprim contribution
+ Curr := Yeq * V // Below VbaseZ resort to linear equal to Yprim contribution
else
if VMag <= VBase95 then
- Curr := Cmul(InterpolateY95_YLow(Vmag), V) // Voltage between Vminpu and Vlow
+ Curr := InterpolateY95_YLow(Vmag) * V // Voltage between Vminpu and Vlow
else
if VMag > VBase105 then
- Curr := Cmul(Yeq105, V) // above 105% use an impedance model
+ Curr := Yeq105 * V // above 105% use an impedance model
else
begin
VRatio := Vmag / VBase; // vbase is l-n FOR wye and l-l FOR delta
- // Linear factor adjustment does not converge for some reason while power adjust does easily
+ // Linear factor adjustment does not converge for some reason while power adjust does easily
// WattFactor := (1.0 + FCVRwattFactor*(Vmag/VBase - 1.0));
if FCVRWattFactor <> 1.0 then
- WattFactor := math.power(VRatio, FCVRWattFactor)
+ WattFactor := pow(VRatio, FCVRWattFactor)
else
WattFactor := Vratio; // old value (in error): 1.0;
if WattFactor > 0.0 then
- Curr := Conjg(Cdiv(Cmplx(WNominal * WattFactor, 0.0), V))
+ Curr := cong(Cmplx(WNominal * WattFactor, 0.0) / V)
else
Curr := CZERO; // P component of current
if Vmag = 0.0 then
Cvar := CZERO // Trap divide by zero error
- {Compute Q component of current}
+ //Compute Q component of current
else
if FCVRvarFactor = 2.0 then
- begin {Check for easy, quick ones first}
- Cvar := Cmul(Cmplx(0.0, Yeq.im), V); // 2 is same as Constant impedance
+ begin // Check for easy, quick ones first
+ Cvar := Cmplx(0.0, Yeq.im) * V; // 2 is same as Constant impedance
end
else
if FCVRvarFactor = 3.0 then
begin
- VarFactor := math.intpower(VRatio, 3);
- Cvar := Conjg(Cdiv(Cmplx(0.0, VarNominal * VarFactor), V));
+ VarFactor := VRatio*VRatio*VRatio;
+ Cvar := cong(Cmplx(0.0, VarNominal * VarFactor) / V);
end
else
begin
- {Other Var factor code here if not squared or cubed}
- VarFactor := math.power(VRatio, FCVRvarFactor);
- Cvar := Conjg(Cdiv(Cmplx(0.0, VarNominal * VarFactor), V));
+ // Other Var factor code here if not squared or cubed
+ VarFactor := pow(VRatio, FCVRvarFactor);
+ Cvar := cong(Cmplx(0.0, VarNominal * VarFactor) / V);
end;
- Caccum(Curr, Cvar); // add in Q component of current
+ Curr += Cvar; // add in Q component of current
end;
- // Save this value in case the Load value is different than the terminal value (see InitHarmonics)
+ // Save this value in case the Load value is different than the terminal value (see InitHarmonics)
FPhaseCurr^[i] := Curr;
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
except
-
On E: Exception do
begin
- DoSimpleMsg(Format('Error in Load.%s: %s ', [Name, E.Message]), 5871);
+ DoSimpleMsg('Error in %s: %s ', [FullName, E.Message], 5871);
raise;
end;
end;
-
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TLoadObj.DoFixedQ;
// Constant P, Fixed Q Q is always kvarBase
var
@@ -1887,10 +1574,7 @@ procedure TLoadObj.DoFixedQ;
Curr,
V: Complex;
Vmag: Double;
-
begin
-
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the load
ZeroITerminal;
@@ -1900,28 +1584,27 @@ procedure TLoadObj.DoFixedQ;
V := Vterminal^[i];
VMag := Cabs(V);
if VMag <= VBaseLow then
- Curr := Cmul(Yeq, V) // Below VbaseZ resort to linear equal to Yprim contribution
+ Curr := Yeq * V // Below VbaseZ resort to linear equal to Yprim contribution
else
if VMag <= VBase95 then
- Curr := Cmul(Cmplx(Yeq95.re, YQfixed), V) // Below 95% use an impedance model
+ Curr := Cmplx(Yeq95.re, YQfixed) * V // Below 95% use an impedance model
else
if VMag > VBase105 then
- Curr := Cmul(Cmplx(Yeq105.re, YQfixed), V) // above 105% use an impedance model
+ Curr := Cmplx(Yeq105.re, YQfixed) * V // above 105% use an impedance model
else
begin
- Curr := Conjg(Cdiv(Cmplx(WNominal, varBase), V));
+ Curr := cong(Cmplx(WNominal, varBase) / V);
end;
// Save this value in case the Load value is different than the terminal value (see InitHarmonics)
FPhaseCurr^[i] := Curr;
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TLoadObj.DoFixedQZ;
// Constant P, Fixed Q Q is always a fixed Z derived from kvarBase
var
@@ -1929,9 +1612,7 @@ procedure TLoadObj.DoFixedQZ;
Curr,
V: Complex;
Vmag: Double;
-
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the load
ZeroITerminal;
@@ -1941,41 +1622,38 @@ procedure TLoadObj.DoFixedQZ;
V := Vterminal^[i];
Vmag := Cabs(V);
if VMag <= VBaseLow then
- Curr := Cmul(Yeq, V) // Below VbaseZ resort to linear equal to Yprim contribution
+ Curr := Yeq * V // Below VbaseZ resort to linear equal to Yprim contribution
else
if Vmag <= VBase95 then
- Curr := Cmul(Cmplx(Yeq95.re, YQfixed), V) // Below 95% use an impedance model
+ Curr := Cmplx(Yeq95.re, YQfixed) * V // Below 95% use an impedance model
else
if VMag > VBase105 then
- Curr := Cmul(Cmplx(Yeq105.re, YQfixed), V)
+ Curr := Cmplx(Yeq105.re, YQfixed) * V
else
begin
- Curr := Conjg(Cdiv(Cmplx(WNominal, 0.0), V)); // P component of current
- Caccum(Curr, Cmul(Cmplx(0.0, YQFixed), V)); // add in Q component of current
+ Curr := cong(Cmplx(WNominal, 0.0) / V); // P component of current
+ Curr += Cmplx(0.0, YQFixed) * V; // add in Q component of current
end;
// Save this value in case the Load value is different than the terminal value (see InitHarmonics)
FPhaseCurr^[i] := Curr;
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TLoadObj.DoHarmonicMode;
-{Compute Injection Current Only when in harmonics mode}
-{Assumes spectrum is an ideal current source based on the fundamental current and spectrum}
-
+// Compute Injection Current Only when in harmonics mode
+// Assumes spectrum is an ideal current source based on the fundamental current and spectrum
var
i: Integer;
Curr, Mult: Complex;
LoadHarmonic: Double;
begin
-
- {Don't calc Vterminal here because it could be undefined!}
+ // Don't calc Vterminal here because it could be undefined!
ZeroInjCurrent;
ZeroIterminal;
with ActiveCircuit.Solution do
@@ -1984,24 +1662,21 @@ procedure TLoadObj.DoHarmonicMode;
Mult := SpectrumObj.GetMult(LoadHarmonic);
for i := 1 to FNphases do
begin
- Curr := CmulReal(Mult, HarmMag^[i]); // Get base harmonic magnitude
+ Curr := Mult * HarmMag^[i]; // Get base harmonic magnitude
RotatePhasorDeg(Curr, LoadHarmonic, HarmAng^[i]); // Time shift by fundamental
- // don't need to save Curr here like we do in Power Flow modes
+ // don't need to save Curr here like we do in Power Flow modes
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into InjCurrent array taking into account connection
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
- // NOTE: This is the value of ITerminal a Monitor will capture in Harmonics mode .. it captures the harmonic injection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
+ // NOTE: This is the value of ITerminal a Monitor will capture in Harmonics mode .. it captures the harmonic injection
IterminalUpdated := TRUE;
end;
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
function TLoadObj.AllTerminalsClosed: Boolean;
-
var
i, j: Integer;
-
begin
Result := TRUE;
for i := 1 to Nterms do
@@ -2013,15 +1688,11 @@ function TLoadObj.AllTerminalsClosed: Boolean;
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TLoadObj.CalcVTerminalPhase;
-
var
i, j: Integer;
-
begin
-
-{ Establish phase voltages and stick in Vtemp}
+ // Establish phase voltages and stick in Vtemp
case Connection of
TLoadConnection.Wye:
@@ -2046,15 +1717,12 @@ procedure TLoadObj.CalcVTerminalPhase;
end;
LoadSolutionCount := ActiveCircuit.Solution.SolutionCount;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TLoadObj.CalcLoadModelContribution;
// Calculates total load current and adds it properly into the InjCurrent array
// Need to implement DynamicMode sometime ...
-
begin
IterminalUpdated := FALSE;
with ActiveCircuit, ActiveCircuit.Solution do
@@ -2085,44 +1753,29 @@ procedure TLoadObj.CalcLoadModelContribution;
else
DoConstantZLoad; // FOR now, until we implement the other models.
end;
-
end;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TLoadObj.CalcInjCurrentArray;
-
// Fill InjCurrent array with the current values to use for injections.
-
var
i, j, k: Integer;
-
begin
-
-// IF a terminal is open, THEN standard load models don't apply, so check it out first
-
- if {$IFDEF DSS_CAPI}(not DSS_CAPI_LOADS_TERMINAL_CHECK) OR {$ENDIF} AllTerminalsClosed then
+ // IF a terminal is open, THEN standard load models don't apply, so check it out first
+ if (not DSS_CAPI_LOADS_TERMINAL_CHECK) OR AllTerminalsClosed then
begin
-
-// Now Get Injection Currents
-
+ // Now Get Injection Currents
CalcLoadModelContribution
-
end
-
else
begin
-
- /// THIS MAY NOT WORK !!! WATCH FOR BAD RESULTS
-
- // some terminals not closed use admittance model FOR injection
+ /// THIS MAY NOT WORK !!! WATCH FOR BAD RESULTS
+ // some terminals not closed use admittance model FOR injection
if OpenLoadSolutionCount <> ActiveCircuit.Solution.SolutionCount then
begin
+ // Rebuild the Yprimopencond IF a new solution because values may have changed.
- // Rebuild the Yprimopencond IF a new solution because values may have changed.
-
- // only reallocate when necessary
+ // only reallocate when necessary
if YPrimOpenCond = NIL then
YPrimOpenCond := TcMatrix.CreateMatrix(Yorder)
else
@@ -2134,8 +1787,8 @@ procedure TLoadObj.CalcInjCurrentArray;
end;
CalcYPrimMatrix(YPrimOpenCond);
- {Now Account FOR the Open Conductors}
- {For any conductor that is open, zero out row and column}
+ // Now Account FOR the Open Conductors
+ // For any conductor that is open, zero out row and column
with YPrimOpenCond do
begin
k := 0;
@@ -2160,18 +1813,13 @@ procedure TLoadObj.CalcInjCurrentArray;
ComputeVTerminal;
YPrimOpenCond.MVmult(ComplexBuffer, Vterminal);
for i := 1 to Yorder do
- ComplexBuffer^[i] := Cnegate(ComplexBuffer^[i]);
+ ComplexBuffer^[i] := -ComplexBuffer^[i];
end;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TLoadObj.GetTerminalCurrents(Curr: pComplexArray);
-
// Always return total terminal currents in the Curr array
-
begin
-
with ActiveCircuit.Solution do
begin
if IterminalSolutionCount <> ActiveCircuit.Solution.SolutionCount then
@@ -2180,16 +1828,11 @@ procedure TLoadObj.GetTerminalCurrents(Curr: pComplexArray);
end;
inherited GetTerminalCurrents(Curr);
end;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
function TLoadObj.InjCurrents: Integer;
-
// Get the injection currents and add them directly into the Currents array
-
begin
-
Result := 0;
if Enabled then
with ActiveCircuit.Solution do
@@ -2199,38 +1842,20 @@ function TLoadObj.InjCurrents: Integer;
CalcInjCurrentArray;
Result := inherited Injcurrents; // Add into Global Currents Array
end;
-
end;
function TLoadObj.InterpolateY95_YLow(const Vmag: Double): Complex;
-{
- For Vmag between V95 and Vlow, interpolate for equivalent Y
-}
+// For Vmag between V95 and Vlow, interpolate for equivalent Y
begin
-
- Result := CDivReal(Cadd(ILow, CmulReal(M95, Vmag - VbaseLow)), Vmag); //(Ilow + M95 * (Vmag - VBaseLow))/Vmag)
-
-{****
- WriteDLLDebugFile(Format('Iter=%d, Name="%s", Vmag=%.6g, Yeq=%.6g +j %.6g',
- [ActiveCircuit.Solution.iteration, Name, Vmag, Result.re, Result.im]));
- }
+ Result := (ILow + M95 * (Vmag - VbaseLow)) / Vmag; //(Ilow + M95 * (Vmag - VBaseLow))/Vmag)
end;
function TLoadObj.InterpolateY95I_YLow(const Vmag: Double): Complex; // ***Added by Celso & Paulo
-{
- For Vmag between V95 and Vlow, interpolate for equivalent Y
-}
+// For Vmag between V95 and Vlow, interpolate for equivalent Y
begin
-
- Result := CDivReal(Cadd(ILow, CmulReal(M95I, Vmag - VbaseLow)), Vmag); //(Ilow + M95I * (Vmag - VBaseLow))/Vmag) // ***Changed by Celso & Paulo
-
-{****
- WriteDLLDebugFile(Format('Iter=%d, Name="%s", Vmag=%.6g, Yeq=%.6g +j %.6g',
- [ActiveCircuit.Solution.iteration, Name, Vmag, Result.re, Result.im]));
- }
+ Result := (ILow + M95I * (Vmag - VbaseLow)) / Vmag; //(Ilow + M95I * (Vmag - VBaseLow))/Vmag) // ***Changed by Celso & Paulo
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
function TLoadObj.Get_Unserved: Boolean;
var
i: Integer;
@@ -2238,11 +1863,9 @@ function TLoadObj.Get_Unserved: Boolean;
Vmag: Double;
NormMinCriteria,
EmergMinCriteria: Double;
- { Line overload takes precedence.
- Assumes that low voltage is due to overloaded line.
- IF voltage is below Emergency minumum, it is counted as unserved.
- }
-
+ // Line overload takes precedence.
+ // Assumes that low voltage is due to overloaded line.
+ // IF voltage is below Emergency minumum, it is counted as unserved.
begin
Result := FALSE;
if UE_Factor > 0.0 then
@@ -2286,26 +1909,21 @@ function TLoadObj.Get_Unserved: Boolean;
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
function TLoadObj.Get_ExceedsNormal: Boolean;
var
i: Integer;
Vpu,
Vmag: Double;
- { Line overload takes precedence.
- Assumes that low voltage is due to overloaded line.
- IF voltage is below Normal minumum, it is counted as unserved in proportion
- to the difference between the normal and emergency voltage limits.
- }
+ // Line overload takes precedence.
+ // Assumes that low voltage is due to overloaded line.
+ // IF voltage is below Normal minumum, it is counted as unserved in proportion
+ // to the difference between the normal and emergency voltage limits.
NormMinCriteria,
EmergMinCriteria: Double;
begin
-
-{ 1-4-00 Added Vpu}
-
Result := FALSE;
if EEN_Factor > 0.0 then
begin
@@ -2347,47 +1965,6 @@ function TLoadObj.Get_ExceedsNormal: Boolean;
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TLoadObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i, j: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- case i of
- 4:
- FSWriteln(F, Format('~ %s=%8.1f', [PropertyName^[i], kWBase]));
- 5:
- FSWriteln(F, Format('~ %s=%5.3f', [PropertyName^[i], PFNominal]));
- 12:
- FSWriteln(F, Format('~ %s=%8.1f', [PropertyName^[i], kvarBase]));
- 22:
- FSWriteln(F, Format('~ %s=%5.3f', [PropertyName^[i], FkVAAllocationFactor]));
- 23:
- FSWriteln(F, Format('~ %s=%8.1f', [PropertyName^[i], kVABase]));
- 33:
- if ZIPVset then
- begin
- FSWrite(F, '~ ' + PropertyName^[i] + '=');
- for j := 1 to nZIPV do
- FSWrite(F, Format('%.2f ', [ZIPV[j]]));
- FSWriteln(F, '"');
- end;
- 34:
- FSWriteln(F, Format('~ %s=%8.1f', [(puSeriesRL * 100.0)]));
- else
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
- end;
-
-end;
-
-
procedure TLoadObj.Set_kVAAllocationFactor(const Value: Double);
begin
FkVAAllocationFactor := Value;
@@ -2420,29 +1997,6 @@ procedure TLoadObj.Set_CFactor(const Value: Double);
HasBeenAllocated := TRUE;
end;
-procedure TLoadObj.Set_ConnectedkVA(const Value: Double);
-begin
- FConnectedkVA := Value;
- LoadSpecType := TLoadSpec.ConnectedkVA_PF;
- FAllocationFactor := FkVAAllocationFactor;
- ComputeAllocatedLoad;
-end;
-
-procedure TLoadObj.Set_kWh(const Value: Double);
-begin
- FkWh := Value;
- LoadSpecType := TLoadSpec.kWh_PF;
- FAllocationFactor := FCFactor;
- ComputeAllocatedLoad;
-end;
-
-procedure TLoadObj.Set_kWhDays(const Value: Double);
-begin
- FkWhDays := Value;
- LoadSpecType := TLoadSpec.kWh_PF;
- ComputeAllocatedLoad;
-end;
-
procedure TLoadObj.ComputeAllocatedLoad;
begin
{Fixed loads defined by kW, kvar or kW, pf are ignored}
@@ -2467,19 +2021,15 @@ procedure TLoadObj.ComputeAllocatedLoad;
kvarBase := -kvarBase;
end;
end;
-
end;
procedure TLoadObj.InitHarmonics;
-{
- Get the present terminal currents and store for harmonics base reference;
-}
+// Get the present terminal currents and store for harmonics base reference;
var
- {Currents:pComplexArray;}
i: Integer;
begin
- {Make Sure there's enuff memory}
+ // Make Sure there's enuff memory
ReallocMem(HarmMag, Sizeof(HarmMag^[1]) * FNphases);
ReallocMem(HarmAng, Sizeof(HarmAng^[1]) * FNphases);
@@ -2487,11 +2037,9 @@ procedure TLoadObj.InitHarmonics;
LoadFundamental := ActiveCircuit.Solution.Frequency;
- // GetCurrents(Currents); // Use FPhaseCurr from most recent pflow solution
- {Store the currents at fundamental frequency.
- The spectrum is applied to these.
- }
-
+ // GetCurrents(Currents); // Use FPhaseCurr from most recent pflow solution
+ // Store the currents at fundamental frequency. The spectrum is applied to these.
+
for i := 1 to Fnphases do
begin
HarmMag^[i] := Cabs(FPhaseCurr^[i]);
@@ -2501,148 +2049,41 @@ procedure TLoadObj.InitHarmonics;
// ReallocMem(Currents, 0); // get rid of temp space
end;
-
-procedure TLoadObj.InitPropertyValues(ArrayOffset: Integer);
-
-begin
-
- PropertyValue[1] := '3'; //'phases';
- PropertyValue[2] := Getbus(1); //'bus1';
- PropertyValue[3] := '12.47';
- PropertyValue[4] := '10';
- PropertyValue[5] := '.88';
- PropertyValue[6] := '1';
- PropertyValue[7] := '';
- PropertyValue[8] := '';
- PropertyValue[9] := '';
- PropertyValue[10] := '';
- PropertyValue[11] := 'wye';
- PropertyValue[12] := '5';
- PropertyValue[13] := '-1'; // 'rneut'; // IF entered -, assume open or user defined
- PropertyValue[14] := '0'; //'xneut';
- PropertyValue[15] := 'variable'; //'status'; // fixed or variable
- PropertyValue[16] := '1'; //class
- PropertyValue[17] := '0.95';
- PropertyValue[18] := '1.05';
- PropertyValue[19] := '0.0';
- PropertyValue[20] := '0.0';
- PropertyValue[21] := '0.0';
- PropertyValue[22] := '0.5'; // Allocation Factor
- PropertyValue[23] := '11.3636';
- PropertyValue[24] := '50';
- PropertyValue[25] := '10';
- PropertyValue[26] := '1'; // CVR watt factor
- PropertyValue[27] := '2'; // CVR var factor
- PropertyValue[28] := '0'; // kwh bulling
- PropertyValue[29] := '30'; // kwhdays
- PropertyValue[30] := '4'; // Cfactor
- PropertyValue[31] := ''; // CVRCurve
- PropertyValue[32] := '1'; // NumCust
- PropertyValue[33] := ''; // ZIPV coefficient array
- PropertyValue[34] := '50'; // %SeriesRL
- PropertyValue[35] := '1'; // RelWeighting
- PropertyValue[36] := '0.5'; // VZpu
- PropertyValue[37] := '0.0'; // puXharm
- PropertyValue[38] := '6.0'; // XRHarm
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-procedure TLoadObj.MakePosSequence;
+procedure TLoadObj.MakePosSequence();
var
- S: String;
- V: Double;
-
+ V, newkW, newkvar, newkva: Double;
+ changes: Integer;
begin
-
- S := 'Phases=1 conn=wye';
-
- // Make sure voltage is line-neutral
+ // Make sure voltage is line-neutral
if (Fnphases > 1) or (connection <> TLoadConnection.Wye) then
V := kVLoadBase / SQRT3
else
V := kVLoadBase;
- S := S + Format(' kV=%-.5g', [V]);
-
-(* OLD Method
- // Divide the load by no. phases
- If Fnphases>1 Then
- Begin
- S := S + Format(' kW=%-.5g kvar=%-.5g',[kWbase/Fnphases, kvarbase/Fnphases]);
- If FConnectedKVA>0.0 Then
- S := S + Format(' xfkVA=%-.5g ',[FConnectedkVA/Fnphases]);
- End;
-*)
-
-// New Method: Assume load is distributed equally among the 3 phases -- works better
-//1-5-2016 RCD
-
- S := S + Format(' kW=%-.5g kvar=%-.5g', [kWbase / 3.0, kvarbase / 3.0]);
- if FConnectedKVA > 0.0 then
- S := S + Format(' xfkVA=%-.5g ', [FConnectedkVA / 3.0]);
-
-
- Parser.CmdString := S;
- Edit;
+ // New Method: Assume load is distributed equally among the 3 phases -- works better
+ //1-5-2016 RCD
+
+ newkw := kWbase / 3.0;
+ newkvar := kvarbase / 3.0;
+ newkva := FConnectedKVA / 3.0;
+
+ BeginEdit(True);
+ SetInteger(ord(TProp.Phases), 1);
+ SetInteger(ord(TProp.conn), 0);
+ SetDouble(ord(TProp.kV), V);
+ SetDouble(ord(TProp.kW), newkW);
+ SetDouble(ord(TProp.kvar), newkvar);
+ changes := 5;
+ if newkva > 0.0 then
+ begin
+ SetDouble(ord(TProp.xfkVA), newkva);
+ changes := changes + 1;
+ end;
+ EndEdit(changes);
inherited;
end;
-function TLoadObj.GetPropertyValue(Index: Integer): String;
-var
- i: Integer;
-begin
- case Index of
- 2:
- Result := GetBus(1);
- 3:
- Result := Format('%-g', [kVLoadBase]);
- 4:
- Result := Format('%-g', [kwBase]);
- 5:
- Result := Format('%-.4g', [PFNominal]);
- 7:
- Result := Yearlyshape;
- 8:
- Result := Dailyshape;
- 9:
- Result := Dutyshape;
- 12:
- Result := Format('%-g', [kvarbase]);
- 22:
- Result := Format('%-g', [FkVAAllocationFactor]);
- 23:
- Result := Format('%-g', [kVABase]);
- 30:
- Result := Format('%-.4g', [FCFactor]);
- 33:
- begin
- Result := '';
- if ZIPVset then
- for i := 1 to nZIPV do
- Result := Result + Format(' %-g', [ZIPV[i]]);
- end;
- 34:
- Result := Format('%-g', [puSeriesRL * 100.0]);
- 35:
- Result := Format('%-g', [RelWeighting]);
- 36:
- Result := Format('%-g', [VLowpu]);
- 37:
- Result := Format('%-g', [FpuXHarm]);
- 38:
- Result := Format('%-g', [FXRHarmRatio]);
- else
- Result := inherited GetPropertyValue(index);
- end;
-end;
-
-
-initialization
-
- CDOUBLEONE := CMplx(1.0, 1.0);
-
+finalization LoadStatusEnum.Free;
+ LoadModelEnum.Free;
end.
diff --git a/src/PCElements/PCClass.pas b/src/PCElements/PCClass.pas
index b071250d6..6164f1afc 100644
--- a/src/PCElements/PCClass.pas
+++ b/src/PCElements/PCClass.pas
@@ -1,6 +1,5 @@
unit PCClass;
-{$M+}
{
----------------------------------------------------------
Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
@@ -15,22 +14,21 @@ interface
CktElementClass;
type
- TPCClass = class(TCktElementClass)
- PRIVATE
+{$SCOPEDENUMS ON}
+ TPCElementProp = (
+ INVALID = 0,
+ spectrum = 1
+ );
+{$SCOPEDENUMS OFF}
+ TPCClass = class(TCktElementClass)
PROTECTED
- procedure ClassEdit(const ActivePCObj: Pointer; const ParamPointer: Integer);
- procedure ClassMakeLike(const OtherObj: Pointer);
-
- procedure CountProperties; // Add no. of intrinsic properties
- procedure DefineProperties; // Add Properties of this class to propName
+ procedure CountPropertiesAndAllocate; override;
+ procedure DefineProperties; override;
PUBLIC
- NumPCClassProps: Integer;
- constructor Create(dssContext: TDSSContext);
+ constructor Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
destructor Destroy; OVERRIDE;
- PUBLISHED
-
end;
@@ -38,7 +36,6 @@ implementation
uses
PCElement,
- ParserDel,
DSSClassDefs,
DSSGlobals,
Utilities,
@@ -46,12 +43,25 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TPCElement;
+ TProp = TPCElementProp;
+const
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-constructor TPCClass.Create(dssContext: TDSSContext);
+constructor TPCClass.Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
begin
- inherited Create(dssContext);
- NumPCClassProps := 1;
- DSSClassType := PC_ELEMENT;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
+
+ inherited Create(dssContext, DSSClsType, DSSClsName);
+
+ if (DSSClassType and NON_PCPD_ELEM) <> NON_PCPD_ELEM then
+ DSSClassType := DSSClassType or PC_ELEMENT;
+
+ ClassParents.Add('PCClass');
end;
destructor TPCClass.Destroy;
@@ -60,62 +70,22 @@ destructor TPCClass.Destroy;
inherited Destroy;
end;
-procedure TPCClass.CountProperties;
+procedure TPCClass.CountPropertiesAndAllocate;
begin
- NumProperties := NumProperties + NumPCClassProps;
- inherited CountProperties;
+ NumProperties := NumProperties + NumPropsThisClass;
+ inherited CountPropertiesAndAllocate;
end;
procedure TPCClass.DefineProperties;
-
-// Define the properties for the base power delivery element class
-
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
- PropertyName^[ActiveProperty + 1] := 'spectrum';
-
- PropertyHelp^[ActiveProperty + 1] := 'Name of harmonic spectrum for this device.';
-
- ActiveProperty := ActiveProperty + NumPCClassProps;
-
+ PopulatePropertyNames(ActiveProperty, NumPropsThisClass, PropInfo, False, 'PCClass');
+ PropertyType[ActiveProperty + ord(TProp.Spectrum)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ActiveProperty + ord(TProp.Spectrum)] := ptruint(@obj.SpectrumObj);
+ PropertyOffset2[ActiveProperty + ord(TProp.Spectrum)] := ptruint(DSS.SpectrumClass);
+ ActiveProperty := ActiveProperty + NumPropsThisClass;
inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TPCClass.ClassEdit(const ActivePCObj: Pointer; const ParamPointer: Integer);
-begin
- // continue parsing with contents of Parser
- if ParamPointer > 0 then
- with TPCElement(ActivePCObj) do
- begin
-
- case ParamPointer of
- 1:
- Spectrum := Parser.StrValue;
- else
- inherited ClassEdit(ActivePCObj, ParamPointer - NumPCClassProps)
- end;
- end;
-
-end;
-
-procedure TPCClass.ClassMakeLike(const OtherObj: Pointer);
-
-var
- OtherPCObj: TPCElement;
-begin
-
- OtherPCObj := TPCElement(OtherObj);
-
- with TPCElement(ActiveDSSObject) do
- begin
- Spectrum := OtherPCObj.Spectrum;
- SpectrumObj := OtherPCObj.SpectrumObj;
- end;
-
- inherited ClassMakeLike(OtherObj);
-
-end;
-
-
-end.
+end.
\ No newline at end of file
diff --git a/src/PCElements/PCElement.pas b/src/PCElements/PCElement.pas
index 5efe25b03..9dd48498c 100644
--- a/src/PCElements/PCElement.pas
+++ b/src/PCElements/PCElement.pas
@@ -1,6 +1,5 @@
unit PCElement;
-{$M+}
{
----------------------------------------------------------
Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
@@ -13,7 +12,7 @@ interface
uses
Classes,
CktElement,
- ucomplex,
+ UComplex, DSSUcomplex,
DSSClass,
Spectrum,
Arraydef,
@@ -27,14 +26,10 @@ TPCElement = class(TDSSCktElement)
procedure GetTerminalCurrents(Curr: pComplexArray); VIRTUAL;
function Get_Variable(i: Integer): Double; VIRTUAL;
procedure Set_Variable(i: Integer; Value: Double); VIRTUAL;
-
PUBLIC
-
-
- Spectrum: String;
SpectrumObj: TSpectrumObj;
- MeterObj, {Upline Energymeter}
+ MeterObj, // Upline Energymeter
SensorObj: TMeterElement; // Upline Sensor for this element
InjCurrent: pComplexArray;
@@ -42,15 +37,14 @@ TPCElement = class(TDSSCktElement)
constructor Create(ParClass: TDSSClass);
destructor Destroy; OVERRIDE;
-
+ procedure MakeLike(OtherObj: Pointer); override;
procedure ZeroInjCurrent;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present values of terminal
procedure ComputeIterminal; OVERRIDE;
function InjCurrents: Integer; OVERRIDE;
procedure CalcYPrimContribution(Curr: pComplexArray); INLINE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
procedure set_ITerminalUpdated(const Value: Boolean);
// Sweep solution removed PROCEDURE BackwardSweep;Override;
@@ -70,10 +64,8 @@ TPCElement = class(TDSSCktElement)
property Variable[i: Integer]: Double READ Get_Variable WRITE Set_Variable;
property ITerminalUpdated: Boolean READ FITerminalUpdated WRITE set_ITerminalUpdated;
-
end;
-
implementation
uses
@@ -90,8 +82,7 @@ implementation
constructor TPCElement.Create(ParClass: TDSSClass);
begin
inherited Create(ParClass);
- Spectrum := 'default';
- SpectrumObj := NIL; // have to allocate later because not guaranteed there will be one now.
+ SpectrumObj := DSS.SpectrumClass.DefaultGeneral;
SensorObj := NIL;
MeterObj := NIL;
InjCurrent := NIL;
@@ -108,28 +99,22 @@ destructor TPCElement.Destroy;
end;
function TPCElement.InjCurrents: Integer;
-
// Add injection currents into System currents array
-
var
i: Integer;
begin
Result := 0;
with ActiveCircuit.Solution do
for i := 1 to Yorder do
- Caccum(Currents^[NodeRef^[i]], InjCurrent^[i]);
+ Currents^[NodeRef^[i]] += InjCurrent^[i];
end;
-//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
procedure TPCElement.GetTerminalCurrents(Curr: pComplexArray);
-
// This is called only if we need to compute the terminal currents from the inj currents
// Such as for Harmonic model
-
var
i: Integer;
begin
-
if ITerminalUpdated then
begin // Just copy iTerminal unless iTerminal=Curr
if Curr <> ITerminal then
@@ -140,44 +125,32 @@ procedure TPCElement.GetTerminalCurrents(Curr: pComplexArray);
begin
YPrim.MVmult(Curr, VTerminal);
for i := 1 to Yorder do
- CAccum(Curr^[i], CNegate(Injcurrent^[i]));
+ Curr^[i] -= Injcurrent^[i];
IterminalUpdated := TRUE;
end;
IterminalSolutionCount := ActiveCircuit.Solution.SolutionCount;
end;
-//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
-
procedure TPCElement.GetCurrents(Curr: pComplexArray);
-
-{Gets total Currents going INTO a devices terminals}
-
+// Gets total Currents going INTO a devices terminals
var
i: Integer;
-
begin
try
-
with ActiveCircuit.Solution do
begin
if (Enabled) then
begin
-
if (LastSolutionWasDirect) and (not (IsDynamicModel or IsHarmonicModel)) then
begin
-
// Take a short cut and get Currents from YPrim only
// For case where model is entirely in Y matrix
-
CalcYPrimContribution(Curr);
-
end
else
begin
-
GetTerminalCurrents(Curr);
- end; {IF}
-
+ end;
end
else
begin // not enabled
@@ -189,14 +162,12 @@ procedure TPCElement.GetCurrents(Curr: pComplexArray);
except
On E: Exception do
- DoErrorMsg(('GetCurrents for Element: ' + Name + '.'), E.Message,
- 'Inadequate storage allotted for circuit element.', 641);
+ DoErrorMsg(Format(_('GetCurrents for Element: %s.'), [FullName]), E.Message,
+ _('Inadequate storage allotted for circuit element.'), 641);
end;
-
end;
procedure TPCElement.CalcYPrimContribution(Curr: pComplexArray);
-
begin
ComputeVTerminal;
// Apply these voltages to Yprim
@@ -206,34 +177,22 @@ procedure TPCElement.CalcYPrimContribution(Curr: pComplexArray);
procedure TPCElement.InitHarmonics;
begin
// By default do nothing in the base class
-
-end;
-
-procedure TPCElement.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[ArrayOffset + 1] := Spectrum;
-
- inherited InitPropertyValues(ArrayOffset + 1);
-
end;
procedure TPCElement.InitStateVars;
begin
// By default do nothing
-
end;
procedure TPCElement.IntegrateStates;
begin
// inherited;
// By default do nothing
-
end;
procedure TPCElement.GetAllVariables(States: pDoubleArray);
begin
- {Do Nothing}
+ // Do Nothing
end;
function TPCElement.NumVariables: Integer;
@@ -243,24 +202,21 @@ function TPCElement.NumVariables: Integer;
function TPCElement.VariableName(i: Integer): String;
begin
- {Do Nothing}
+ // Do Nothing
Result := '';
end;
function TPCElement.LookupVariable(const S: String): Integer;
-
-{Search through variable name list and return index if found}
-{Compare up to length of S}
-
+// Search through variable name list and return index if found
+// Compare up to length of S
var
i, TestLength: Integer;
-
begin
Result := -1; // Returns -1 for error not found
TestLength := Length(S);
for i := 1 to NumVariables do
begin
- if CompareText(Copy(VariableName(i), 1, TestLength), S) = 0 then
+ if AnsiCompareText(Copy(VariableName(i), 1, TestLength), S) = 0 then
begin
Result := i;
Break;
@@ -268,8 +224,7 @@ function TPCElement.LookupVariable(const S: String): Integer;
end;
end;
-
-procedure TPCElement.DumpProperties(F: TFileStream; Complete: Boolean);
+procedure TPCElement.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i: Integer;
begin
@@ -284,20 +239,33 @@ procedure TPCElement.DumpProperties(F: TFileStream; Complete: Boolean);
end;
end;
+ if Leaf then
+ begin
+ with ParentClass do
+ for i := 1 to NumProperties do
+ begin
+ FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
+ end;
+
+ if Complete then
+ begin
+ FSWriteln(F);
+ FSWriteln(F);
+ end;
+ end;
end;
function TPCElement.Get_Variable(i: Integer): Double;
begin
- {do Nothing here -- up to override function}
+ // do Nothing here -- up to override function
Result := -9999.99;
end;
procedure TPCElement.Set_Variable(i: Integer; Value: Double);
begin
- {Do Nothing}
+ // Do Nothing
end;
-
procedure TPCElement.ComputeIterminal;
begin
if IterminalSolutionCount <> ActiveCircuit.Solution.SolutionCount then
@@ -305,7 +273,6 @@ procedure TPCElement.ComputeIterminal;
GetCurrents(Iterminal);
IterminalSolutionCount := ActiveCircuit.Solution.SolutionCount;
end;
-
end;
procedure TPCElement.ZeroInjCurrent;
@@ -323,4 +290,15 @@ procedure TPCElement.set_ITerminalUpdated(const Value: Boolean);
ITerminalSolutionCount := ActiveCircuit.Solution.SolutionCount;
end;
+procedure TPCElement.MakeLike(OtherObj: Pointer);
+var
+ Other: TPCElement;
+begin
+ inherited MakeLike(OtherObj);
+
+ Other := TPCElement(OtherObj);
+
+ SpectrumObj := Other.SpectrumObj;
+end;
+
end.
diff --git a/src/PCElements/PVSystemUserModel.pas b/src/PCElements/PVSystemUserModel.pas
index ea153f0d1..2f9424425 100644
--- a/src/PCElements/PVSystemUserModel.pas
+++ b/src/PCElements/PVSystemUserModel.pas
@@ -1,6 +1,5 @@
unit PVSystemUserModel;
-{$M+}
{
----------------------------------------------------------
Copyright (c) 2009-2015, Electric Power Research Institute, Inc.
@@ -10,10 +9,9 @@
interface
-USES Dynamics, DSSCallBackRoutines, ucomplex, Arraydef, DSSClass;
+USES Dynamics, DSSCallBackRoutines, UComplex, DSSUcomplex, Arraydef, DSSClass;
TYPE
-
TPVsystemUserModel = class(TObject)
private
FHandle: NativeUInt; // Handle to DLL containing user model
@@ -22,7 +20,7 @@ TPVsystemUserModel = class(TObject)
FuncError: Boolean;
- {These functions should only be called by the object itself}
+ // These functions should only be called by the object itself
FNew: Function(Var DynaData:TDynamicsRec; Var CallBacks:TDSSCallBacks): Integer; Stdcall;// Make a new instance
FDelete: Procedure(var x:Integer); Stdcall; // deletes specified instance
FSelect: Function (var x:Integer):Integer; Stdcall; // Select active instance
@@ -44,11 +42,11 @@ TPVsystemUserModel = class(TObject)
FUpdateModel: Procedure; Stdcall; // Called when props of generator updated
- {Save and restore data}
+ // Save and restore data
FSave: Procedure; Stdcall;
FRestore: Procedure; Stdcall;
- {Monitoring functions}
+ // Monitoring functions
FNumVars: Function:Integer;Stdcall;
FGetAllVars: Procedure(Vars:pDoubleArray);StdCall; // Get all vars
FGetVariable: Function(var I:Integer):Double;StdCall;// Get a particular var
@@ -66,22 +64,18 @@ TPVsystemUserModel = class(TObject)
constructor Create(dssContext: TDSSContext);
destructor Destroy; override;
- published
-
end;
implementation
-Uses PVSystem, DSSGlobals, {$IFDEF FPC}dynlibs{$ELSE}Windows{$ENDIF}, Sysutils,
+Uses PVSystem, DSSGlobals, dynlibs, Sysutils,
DSSHelper;
-{ TPVsystemUserModel }
-
function TPVsystemUserModel.CheckFuncError(Addr: Pointer; FuncName: String): Pointer;
begin
If Addr=nil then
Begin
- DoSimpleMsg(DSS, 'PVSystem User Model Does Not Have Required Function: ' + FuncName, 1569);
+ DoSimpleMsg(DSS, 'PVSystem User Model Does Not Have Required Function: %s', [FuncName], 1569);
FuncError := True;
End;
Result := Addr;
@@ -93,12 +87,10 @@ constructor TPVsystemUserModel.Create(dssContext: TDSSContext);
FID := 0;
Fhandle := 0;
FName := '';
-
end;
destructor TPVsystemUserModel.Destroy;
begin
-
If FID <> 0 Then
Begin
FDelete(FID); // Clean up all memory associated with this instance
@@ -106,7 +98,6 @@ destructor TPVsystemUserModel.Destroy;
End;
inherited;
-
end;
function TPVsystemUserModel.Get_Exists: Boolean;
@@ -139,7 +130,6 @@ procedure TPVsystemUserModel.Set_Edit(const Value: String);
procedure TPVsystemUserModel.Set_Name(const Value:String);
begin
-
{If Model already points to something, then free it}
IF FHandle <> 0 Then Begin
@@ -163,10 +153,9 @@ procedure TPVsystemUserModel.Set_Name(const Value:String);
End;
If FHandle = 0 Then
- DoSimpleMsg(DSS, 'PVSystem User Model ' + Value + ' Not Loaded. DSS Directory = '+DSSDirectory, 1570)
+ DoSimpleMsg(DSS, 'PVSystem User Model "%s" Not Loaded. DSS Directory = %s', [Value, DSSDirectory], 1570)
Else
- Begin
-
+ begin
FName := Value;
// Now set up all the procedure variables
@@ -199,6 +188,4 @@ procedure TPVsystemUserModel.Set_Name(const Value:String);
End;
end;
-
-
end.
diff --git a/src/PCElements/PVsystem.pas b/src/PCElements/PVsystem.pas
index 95040ae70..4842616ad 100644
--- a/src/PCElements/PVsystem.pas
+++ b/src/PCElements/PVsystem.pas
@@ -7,23 +7,16 @@
----------------------------------------------------------
}
-{ Change Log
+// To Do:
+// Make connection to User model
+// Yprim for various modes
+// Define state vars and dynamics mode behavior
+// Complete Harmonics mode algorithm (generator mode is implemented)
- 1/28/2011 Created from Storage Model
-
-
- To Do:
- Make connection to User model
- Yprim for various modes
- Define state vars and dynamics mode behavior
- Complete Harmonics mode algorithm (generator mode is implemented)
-}
-{
- The PVsystem element is essentially a generator that consists of a PV panel and an inverter.
-
- The PVsystem element can also produce or absorb vars within the kVA rating of the inverter.
- // WGS: Updated 9/24/2015 to allow for simultaneous modes and additional functionality in the InvControl.
-}
+// The PVsystem element is essentially a generator that consists of a PV panel and an inverter.
+//
+// The PVsystem element can also produce or absorb vars within the kVA rating of the inverter.
+// // WGS: Updated 9/24/2015 to allow for simultaneous modes and additional functionality in the InvControl.
// The PVsystem element is assumed balanced over the no. of phases defined
@@ -32,12 +25,11 @@ interface
uses
Classes,
- PVsystemUserModel,
DSSClass,
PCClass,
PCElement,
ucmatrix,
- ucomplex,
+ UComplex, DSSUcomplex,
LoadShape,
TempShape,
XYCurve,
@@ -52,20 +44,60 @@ interface
VARMODEKVAR = 1;
type
-
-
-{Struct to pass basic data to user-written DLLs}
+{$SCOPEDENUMS ON}
+ TPVSystemProp = (
+ INVALID = 0,
+ phases = 1,
+ bus1 = 2,
+ kv = 3, // propKV
+ irradiance = 4, // propIrradiance
+ Pmpp = 5, // propPmpp
+ pctPmpp = 6, // proppctPmpp
+ Temperature = 7, // propTemp
+ pf = 8, // propPF
+ conn = 9, // propCONNECTION
+ kvar = 10, // propKVAR
+ kVA = 11, // propKVA
+ pctCutin = 12, // propCutin
+ pctCutout = 13, // propCutout
+ EffCurve = 14, // propInvEffCurve
+ P__TCurve = 15, // propP_T_Curve
+ pctR = 16, // propPCTR
+ pctX = 17, // propPCTX
+ model = 18, // propMODEL
+ Vminpu = 19, // propVMINPU
+ Vmaxpu = 20, // propVMAXPU
+ Balanced = 21, // propBalanced
+ LimitCurrent = 22, // propLimited
+ yearly = 23, // propYEARLY
+ daily = 24, // propDAILY
+ duty = 25, // propDUTY
+ Tyearly = 26, // propTYEARLY
+ Tdaily = 27, // propTDAILY
+ Tduty = 28, // propTDUTY
+ cls = 29, // propCLASS
+ UserModel = 30, // propUSERMODEL
+ UserData = 31, // propUSERDATA
+ debugtrace = 32, // propDEBUGTRACE
+ VarFollowInverter = 33, // propVarFollowInverter
+ kvarLimit = 34, // propkvarLimit
+ DutyStart = 35, // propDutyStart
+ WattPriority = 36 // propPpriority
+ );
+{$SCOPEDENUMS OFF}
+
+ // Struct to pass basic data to user-written DLLs
TPVSystemVars = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}packed{$ENDIF} record
FkVArating: Double;
kVPVSystemBase: Double;
RThev: Double;
XThev: Double;
- Vthevharm: Double; {Thevinen equivalent voltage mag for Harmonic model}
- VthevmagDyn: Double; {Thevinen equivalent voltage mag reference for Dynamics model}
- Thetaharm: Double; {Thevinen equivalent angle reference for Harmonic model}
- ThetaDyn: Double; {Thevinen equivalent angle reference for Dynamics model}
- InitialVAngle: Double; {initial terminal voltage angle when entering dynamics mode}
+ Vthevharm: Double; // Thevinen equivalent voltage mag for Harmonic model
+ VthevmagDyn: Double; // Thevinen equivalent voltage mag reference for Dynamics model
+ Thetaharm: Double; // Thevinen equivalent angle reference for Harmonic model
+ ThetaDyn: Double; // Thevinen equivalent angle reference for Dynamics model
+ InitialVAngle: Double; // initial terminal voltage angle when entering dynamics mode
EffFactor: Double;
TempFactor: Double;
PanelkW: Double; //computed
@@ -77,31 +109,27 @@ interface
Fkvarlimit: Double; //maximum kvar output of the PVSystem (unsigned)
Vreg: Double; // will be set from InvControl or ExpControl
- {32-bit integers}
- NumPhases: Integer; {Number of phases}
- NumConductors: Integer;{Total Number of conductors (wye-connected will have 4)}
+ // 32-bit integers
+ NumPhases: Integer; // Number of phases
+ NumConductors: Integer;// Total Number of conductors (wye-connected will have 4)
Conn: Integer; // 0 = wye; 1 = Delta
- P_Priority: Boolean; // default False // added 10/30/2018
+ P_Priority: LongBool; // default False // added 10/30/2018
end;
-// ===========================================================================================
TPVSystem = class(TPCClass)
- PRIVATE
-
- procedure InterpretConnection(const S: String);
- procedure SetNcondsForConnection;
PROTECTED
- procedure DefineProperties;
- function MakeLike(const OtherPVsystemObjName: String): Integer; OVERRIDE;
+ cBuffer: TCBuffer24; // Temp buffer for calcs 24-phase PVSystem element?
+
+ procedure DefineProperties; override;
PUBLIC
RegisterNames: array[1..NumPVSystemRegisters] of String;
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
procedure ResetRegistersAll;
procedure SampleAll;
@@ -109,7 +137,6 @@ TPVSystem = class(TPCClass)
end;
-// ===========================================================================================
TPVsystemObj = class(TPCElement)
PRIVATE
YEQ: Complex; // at nominal
@@ -120,17 +147,17 @@ TPVsystemObj = class(TPCElement)
LastThevAngle: Double;
- DebugTrace: Boolean;
+ DebugTrace: LongBool;
PVSystemSolutionCount: Integer;
- PVSystemFundamental: Double; {Thevinen equivalent voltage mag and angle reference for Harmonic model}
+ PVSystemFundamental: Double; // Thevinen equivalent voltage mag and angle reference for Harmonic model
PVsystemObjSwitchOpen: Boolean;
FirstSampleAfterReset: Boolean;
PFSpecified: Boolean;
kvarSpecified: Boolean;
- ForceBalanced: Boolean;
- CurrentLimited: Boolean;
+ ForceBalanced: LongBool;
+ CurrentLimited: LongBool;
kvar_out: Double;
kW_out: Double;
@@ -139,7 +166,6 @@ TPVsystemObj = class(TPCElement)
FpctCutIn: Double;
FpctCutOut: Double;
- FVarFollowInverter: Boolean;
CutInkW: Double;
CutOutkW: Double;
FInverterON: Boolean;
@@ -163,7 +189,7 @@ TPVsystemObj = class(TPCElement)
TShapeValue: Double;
TraceFile: TFileStream;
- UserModel: TPVsystemUserModel; {User-Written Models}
+ UserModelNameStr, UserModelEditStr: String;
varBase: Double; // Base vars per phase
VBase: Double; // Base volts suitable for computing currents
@@ -191,7 +217,6 @@ TPVsystemObj = class(TPCElement)
procedure ComputekWkvar;
procedure CalcPVSystemModelContribution; // This is where the power gets computed
procedure CalcInjCurrentArray;
- (*PROCEDURE CalcVterminal;*)
procedure CalcVTerminalPhase;
procedure CalcYPrimMatrix(Ymatrix: TcMatrix);
@@ -200,7 +225,6 @@ TPVsystemObj = class(TPCElement)
procedure DoConstantZPVsystemObj;
procedure DoDynamicMode;
procedure DoHarmonicMode;
- procedure DoUserModel;
procedure Integrate(Reg: Integer; const Deriv: Double; const Interval: Double);
procedure SetDragHandRegister(Reg: Integer; const Value: Double);
@@ -208,7 +232,6 @@ TPVsystemObj = class(TPCElement)
procedure WriteTraceRecord(const s: String);
- // PROCEDURE SetKWandKvarOut;
procedure UpdatePVSystem; // Update PVSystem elements based on present kW and IntervalHrs variable
function Get_PresentkW: Double;
@@ -244,25 +267,17 @@ TPVsystemObj = class(TPCElement)
PVSystemVars: TPVSystemVars;
- Connection: Integer; {0 = line-neutral; 1=Delta}
- DailyShape: String; // Daily (24 HR) PVSystem element irradiance shape
+ Connection: Integer; // 0 = line-neutral; 1=Delta
DailyShapeObj: TLoadShapeObj; // Daily PVSystem element irradianceShape for this load
- DutyShape: String; // Duty cycle irradiance shape for changes typically less than one hour
DutyShapeObj: TLoadShapeObj; // irradiance Shape for this PVSystem element
DutyStart: Double; // starting time offset into the DutyShape [hrs] for this PVsystem
- YearlyShape: String; //
YearlyShapeObj: TLoadShapeObj; // Yearly irradiance Shape for this PVSystem element
- DailyTShape: String;
DailyTShapeObj: TTShapeObj;
- DutyTShape: String;
DutyTShapeObj: TTShapeObj;
- YearlyTShape: String;
YearlyTShapeObj: TTShapeObj;
- InverterCurve: String;
InverterCurveObj: TXYCurveObj;
- Power_TempCurve: String;
Power_TempCurveObj: TXYCurveObj;
FClass: Integer;
@@ -270,9 +285,13 @@ TPVsystemObj = class(TPCElement)
PFnominal: Double;
Registers, Derivatives: array[1..NumPVSystemRegisters] of Double;
+ VarFollowInverter: LongBool;
+
constructor Create(ParClass: TDSSClass; const SourceName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure Set_ConductorClosed(Index: Integer; Value: Boolean); OVERRIDE;
procedure RecalcElementData; OVERRIDE;
@@ -287,8 +306,6 @@ TPVsystemObj = class(TPCElement)
function Get_InverterON: Boolean;
procedure Set_InverterON(const Value: Boolean);
- function Get_VarFollowInverter: Boolean;
- procedure Set_VarFollowInverter(const Value: Boolean);
procedure Set_Maxkvar(const Value: Double);
procedure SetNominalPVSystemOuput;
procedure Randomize(Opt: Integer); // 0 = reset to 1.0; 1 = Gaussian around mean and std Dev ; // 2 = uniform
@@ -304,11 +321,7 @@ TPVsystemObj = class(TPCElement)
// Support for Harmonics Mode
procedure InitHarmonics; OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
property PresentIrradiance: Double READ Get_PresentIrradiance WRITE Set_PresentIrradiance;
property PresentkW: Double READ Get_PresentkW WRITE Set_PresentkW;
@@ -322,18 +335,15 @@ TPVsystemObj = class(TPCElement)
property VWmode: Boolean READ Get_VWmode WRITE Set_VWmode;
property VWYAxis: Integer READ Get_VWYAxis WRITE Set_VWYAxis;
property InverterON: Boolean READ Get_InverterON WRITE Set_InverterON;
- property VarFollowInverter: Boolean READ Get_VarFollowInverter WRITE Set_VarFollowInverter;
property kvarLimit: Double READ PVSystemVars.Fkvarlimit WRITE Set_Maxkvar;
property MinModelVoltagePU: Double READ VminPu;
property IrradianceNow :Double READ ShapeFactor.re;
end;
-// ===========================================================================================
implementation
-
uses
- ParserDel,
+ BufStream,
Circuit,
Sysutils,
Command,
@@ -346,68 +356,20 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TPVsystemObj;
+ TProp = TPVSystemProp;
const
-
-// ===========================================================================================
-{
- To add a property,
- 1) add a property constant to this list
- 2) add a handler to the CASE statement in the Edit FUNCTION
- 3) add a statement(s) to InitPropertyValues FUNCTION to initialize the string value
- 4) add any special handlers to DumpProperties and GetPropertyValue, If needed
-}
-// ===========================================================================================
-
- propKV = 3;
- propIrradiance = 4;
- propPF = 5;
- propMODEL = 6;
- propYEARLY = 7;
- propDAILY = 8;
- propDUTY = 9;
- propTYEARLY = 10;
- propTDAILY = 11;
- propTDUTY = 12;
- propCONNECTION = 13;
- propKVAR = 14;
- propPCTR = 15;
- propPCTX = 16;
- propCLASS = 17;
- propInvEffCurve = 18;
- propTemp = 19;
- propPmpp = 20;
- propP_T_Curve = 21;
- propCutin = 22;
- propCutout = 23;
- propVMINPU = 24;
- propVMAXPU = 25;
- propKVA = 26;
- propUSERMODEL = 27;
- propUSERDATA = 28;
- propDEBUGTRACE = 29;
- proppctPmpp = 30;
- propBalanced = 31;
- propLimited = 32;
- propVarFollowInverter = 33;
- propkvarLimit = 34;
- propDutyStart = 35;
- propPpriority = 36;
-
- NumPropsThisClass = 36; // Make this agree with the last property constant
-
+ NumPropsThisClass = Ord(High(TProp));
var
+ PropInfo: Pointer = NIL;
- cBuffer: array[1..24] of Complex; // Temp buffer for calcs 24-phase PVSystem element?
- CDOUBLEONE: Complex;
-
-// ===========================================================================================
-constructor TPVsystem.Create(dssContext: TDSSContext); // Creates superstructure for all PVSystem elements
+constructor TPVsystem.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'PVSystem';
- DSSClassType := DSSClassType + PVSYSTEM_ELEMENT; // In both PCelement and PVSystem element list
-
- ActiveElement := 0;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
+
+ inherited Create(dssContext, PVSYSTEM_ELEMENT, 'PVSystem');
// Set Register names
RegisterNames[1] := 'kWh';
@@ -416,199 +378,137 @@ constructor TPVsystem.Create(dssContext: TDSSContext); // Creates superstructur
RegisterNames[4] := 'Max kVA';
RegisterNames[5] := 'Hours';
RegisterNames[6] := 'Price($)';
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
end;
-// ===========================================================================================
destructor TPVsystem.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
+end;
+function Getkvar(Obj: TObj): Double;
+begin
+ Result := Obj.kvar_out;
end;
-// ===========================================================================================
procedure TPVsystem.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays; {see DSSClass}
-
- // Define Property names
- {
- Using the AddProperty FUNCTION, you can list the properties here in the order you want
- them to appear when properties are accessed sequentially without tags. Syntax:
-
- AddProperty( , , );
-
- }
- AddProperty('phases', 1,
- 'Number of Phases, this PVSystem element. Power is evenly divided among phases.');
- AddProperty('bus1', 2,
- 'Bus to which the PVSystem element is connected. May include specific node specification.');
- AddProperty('kv', propKV,
- 'Nominal rated (1.0 per unit) voltage, kV, for PVSystem element. For 2- and 3-phase PVSystem elements, specify phase-phase kV. ' +
- 'Otherwise, specify actual kV across each branch of the PVSystem element. ' +
- 'If 1-phase wye (star or LN), specify phase-neutral kV. ' +
- 'If 1-phase delta or phase-phase connected, specify phase-phase kV.'); // line-neutral voltage// base voltage
- AddProperty('irradiance', propIrradiance,
- 'Get/set the present irradiance value in kW/sq-m. Used as base value for shape multipliers. ' +
- 'Generally entered as peak value for the time period of interest and the yearly, daily, and duty load shape ' +
- 'objects are defined as per unit multipliers (just like Loads/Generators).');
- AddProperty('Pmpp', propPmpp,
- 'Get/set the rated max power of the PV array for 1.0 kW/sq-m irradiance and a user-selected array temperature. ' +
- 'The P-TCurve should be defined relative to the selected array temperature.');
- AddProperty('pctPmpp', proppctPmpp,
- 'Upper limit on active power as a percentage of Pmpp.');
- AddProperty('Temperature', propTemp,
- 'Get/set the present Temperature. Used as fixed value corresponding to PTCurve property. ' +
- 'A multiplier is obtained from the Pmpp-Temp curve and applied to the nominal Pmpp from the irradiance ' +
- 'to determine the net array output.');
- AddProperty('pf', propPF,
- 'Nominally, the power factor for the output power. Default is 1.0. ' +
- 'Setting this property will cause the inverter to operate in constant power factor mode.' +
- 'Enter negative when kW and kvar have opposite signs.' + CRLF +
- 'A positive power factor signifies that the PVSystem element produces vars ' + CRLF +
- 'as is typical for a generator. ');
- AddProperty('conn', propCONNECTION,
- '={wye|LN|delta|LL}. Default is wye.');
- AddProperty('kvar', propKVAR,
- 'Get/set the present kvar value. Setting this property forces the inverter to operate in constant kvar mode.');
- AddProperty('kVA', propKVA,
- 'kVA rating of inverter. Used as the base for Dynamics mode and Harmonics mode values.');
- AddProperty('%Cutin', propCutin,
- '% cut-in power -- % of kVA rating of inverter. ' +
- 'When the inverter is OFF, the power from the array must be greater than this for the inverter to turn on.');
- AddProperty('%Cutout', propCutout,
- '% cut-out power -- % of kVA rating of inverter. ' +
- 'When the inverter is ON, the inverter turns OFF when the power from the array drops below this valye.');
-
- AddProperty('EffCurve', propInvEffCurve,
- 'An XYCurve object, previously defined, that describes the PER UNIT efficiency vs PER UNIT of rated kVA for the inverter. ' +
- 'Inverter output power is discounted by the multiplier obtained from this curve.');
-
- AddProperty('P-TCurve', propP_T_Curve,
- 'An XYCurve object, previously defined, that describes the PV array PER UNIT Pmpp vs Temperature curve. ' +
- 'Temperature units must agree with the Temperature property and the Temperature shapes used for simulations. ' +
- 'The Pmpp values are specified in per unit of the Pmpp value for 1 kW/sq-m irradiance. ' +
- 'The value for the temperature at which Pmpp is defined should be 1.0. ' +
- 'The net array power is determined by the irradiance * Pmpp * f(Temperature)');
- AddProperty('%R', propPCTR,
- 'Equivalent percent internal resistance, ohms. Default is 50%. Placed in series with internal voltage source' +
- ' for harmonics and dynamics modes. (Limits fault current to about 2 pu if not current limited -- see LimitCurrent) ');
- AddProperty('%X', propPCTX,
- 'Equivalent percent internal reactance, ohms. Default is 0%. Placed in series with internal voltage source' +
- ' for harmonics and dynamics modes. ');
- AddProperty('model', propMODEL,
- 'Integer code (default=1) for the model to use for power output variation with voltage. ' +
- 'Valid values are:' + CRLF + CRLF +
- '1:PVSystem element injects a CONSTANT kW at specified power factor.' + CRLF +
- '2:PVSystem element is modeled as a CONSTANT ADMITTANCE.' + CRLF +
- '3:Compute load injection from User-written Model.');
-
- AddProperty('Vminpu', propVMINPU,
- 'Default = 0.90. Minimum per unit voltage for which the Model is assumed to apply. ' +
- 'Below this value, the load model reverts to a constant impedance model except for Dynamics model. ' +
- 'In Dynamics mode, the current magnitude is limited to the value the power flow would compute for this voltage.');
- AddProperty('Vmaxpu', propVMAXPU,
- 'Default = 1.10. Maximum per unit voltage for which the Model is assumed to apply. ' +
- 'Above this value, the load model reverts to a constant impedance model.');
- AddProperty('Balanced', propBalanced,
- '{Yes | No*} Default is No. Force balanced current only for 3-phase PVSystems. Forces zero- and negative-sequence to zero. ');
- AddProperty('LimitCurrent', propLimited,
- 'Limits current magnitude to Vminpu value for both 1-phase and 3-phase PVSystems similar to Generator Model 7. For 3-phase, ' +
- 'limits the positive-sequence current but not the negative-sequence.');
- AddProperty('yearly', propYEARLY,
- 'Dispatch shape to use for yearly simulations. Must be previously defined ' +
- 'as a Loadshape object. If this is not specified, the Daily dispatch shape, if any, is repeated ' +
- 'during Yearly solution modes. In the default dispatch mode, ' +
- 'the PVSystem element uses this loadshape to trigger State changes.');
- AddProperty('daily', propDAILY,
- 'Dispatch shape to use for daily simulations. Must be previously defined ' +
- 'as a Loadshape object of 24 hrs, typically. In the default dispatch mode, ' +
- 'the PVSystem element uses this loadshape to trigger State changes.'); // daily dispatch (hourly)
- AddProperty('duty', propDUTY,
- 'Load shape to use for duty cycle dispatch simulations such as for solar ramp rate studies. ' +
- 'Must be previously defined as a Loadshape object. ' +
- 'Typically would have time intervals of 1-5 seconds. ' +
- 'Designate the number of points to solve using the Set Number=xxxx command. ' +
- 'If there are fewer points in the actual shape, the shape is assumed to repeat.'); // as for wind generation
-
- AddProperty('Tyearly', propTYEARLY,
- 'Temperature shape to use for yearly simulations. Must be previously defined ' +
- 'as a TShape object. If this is not specified, the Daily dispatch shape, if any, is repeated ' +
- 'during Yearly solution modes. ' +
- 'The PVSystem element uses this TShape to determine the Pmpp from the Pmpp vs T curve. ' +
- 'Units must agree with the Pmpp vs T curve.');
- AddProperty('Tdaily', propTDAILY,
- 'Temperature shape to use for daily simulations. Must be previously defined ' +
- 'as a TShape object of 24 hrs, typically. ' +
- 'The PVSystem element uses this TShape to determine the Pmpp from the Pmpp vs T curve. ' +
- 'Units must agree with the Pmpp vs T curve.'); // daily dispatch (hourly)
- AddProperty('Tduty', propTDUTY,
- 'Temperature shape to use for duty cycle dispatch simulations such as for solar ramp rate studies. ' +
- 'Must be previously defined as a TShape object. ' +
- 'Typically would have time intervals of 1-5 seconds. ' +
- 'Designate the number of points to solve using the Set Number=xxxx command. ' +
- 'If there are fewer points in the actual shape, the shape is assumed to repeat. ' +
- 'The PVSystem model uses this TShape to determine the Pmpp from the Pmpp vs T curve. ' +
- 'Units must agree with the Pmpp vs T curve.'); // Cloud transient simulation
- AddProperty('class', propCLASS,
- 'An arbitrary integer number representing the class of PVSystem element so that PVSystem values may ' +
- 'be segregated by class.'); // integer
-
- AddProperty('UserModel', propUSERMODEL,
- 'Name of DLL containing user-written model, which computes the terminal currents for Dynamics studies, ' +
- 'overriding the default model. Set to "none" to negate previous setting.');
- AddProperty('UserData', propUSERDATA,
- 'String (in quotes or parentheses) that gets passed to user-written model for defining the data required for that model.');
- AddProperty('debugtrace', propDEBUGTRACE,
- '{Yes | No } Default is no. Turn this on to capture the progress of the PVSystem model ' +
- 'for each iteration. Creates a separate file for each PVSystem element named "PVSystem_name.CSV".');
- AddProperty('VarFollowInverter', propVarFollowInverter,
- 'Boolean variable (Yes|No) or (True|False). Defaults to False which indicates that the reactive power generation/absorption does not respect the inverter status.' +
- 'When set to True, the PVSystem reactive power generation/absorption will cease when the inverter status is off, due to panel kW dropping below %Cutout. The reactive power ' +
- 'generation/absorption will begin again when the panel kW is above %Cutin. When set to False, the PVSystem will generate/absorb reactive power regardless of the status of the inverter.');
- AddProperty('kvarLimit', propkvarLimit,
- 'Un-signed numerical variable Defaults to kVA rating of the inverter. Indicates the maximum reactive power generation/absorption (in kvar) for the PVSystem (as an un-signed value).');
- AddProperty('DutyStart', propDutyStart,
- 'Starting time offset [hours] into the duty cycle shape for this PVSystem, defaults to 0');
- AddProperty('WattPriority', propPPriority,
- '{Yes/No*/True/False} Set inverter to watt priority instead of the default var priority');
-
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enum properties
+ PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.conn)] := ptruint(@obj.Connection);
+ PropertyOffset2[ord(TProp.conn)] := PtrInt(DSS.ConnectionEnum);
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+
+ // strings
+ PropertyType[ord(TProp.UserModel)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.UserModel)] := ptruint(@obj.UserModelNameStr);
+ PropertyType[ord(TProp.UserData)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.UserData)] := ptruint(@obj.UserModelEditStr);
+
+ // integer properties
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ PropertyType[ord(TProp.cls)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.cls)] := ptruint(@obj.FClass);
+ PropertyType[ord(TProp.model)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.model)] := ptruint(@obj.VoltageModel); // TODO: enum?
+
+ // object properties
+ PropertyType[ord(TProp.yearly)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.daily)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.duty)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.Tyearly)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.Tdaily)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.Tduty)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.EffCurve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.P__TCurve)] := TPropertyType.DSSObjectReferenceProperty;
+
+ PropertyOffset[ord(TProp.yearly)] := ptruint(@obj.YearlyShapeObj);
+ PropertyOffset[ord(TProp.daily)] := ptruint(@obj.DailyShapeObj);
+ PropertyOffset[ord(TProp.duty)] := ptruint(@obj.DutyShapeObj);
+ PropertyOffset[ord(TProp.Tyearly)] := ptruint(@obj.YearlyTShapeObj);
+ PropertyOffset[ord(TProp.Tdaily)] := ptruint(@obj.DailyTShapeObj);
+ PropertyOffset[ord(TProp.Tduty)] := ptruint(@obj.DutyTShapeObj);
+ PropertyOffset[ord(TProp.EffCurve)] := ptruint(@obj.InverterCurveObj);
+ PropertyOffset[ord(TProp.P__TCurve)] := ptruint(@obj.Power_TempCurveObj);
+
+ PropertyOffset2[ord(TProp.yearly)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.daily)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.duty)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.Tyearly)] := ptruint(DSS.TShapeClass);
+ PropertyOffset2[ord(TProp.Tdaily)] := ptruint(DSS.TShapeClass);
+ PropertyOffset2[ord(TProp.Tduty)] := ptruint(DSS.TShapeClass);
+ PropertyOffset2[ord(TProp.EffCurve)] := ptruint(DSS.XYCurveClass);
+ PropertyOffset2[ord(TProp.P__TCurve)] := ptruint(DSS.XYCurveClass);
+
+ // booleans
+ PropertyType[ord(TProp.LimitCurrent)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.LimitCurrent)] := ptruint(@obj.CurrentLimited);
+ PropertyType[ord(TProp.Balanced)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.Balanced)] := ptruint(@obj.ForceBalanced);
+ PropertyType[ord(TProp.VarFollowInverter)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.VarFollowInverter)] := ptruint(@obj.VarFollowInverter);
+ PropertyType[ord(TProp.debugtrace)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.debugtrace)] := ptruint(@obj.DebugTrace);
+ PropertyType[ord(TProp.WattPriority)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.WattPriority)] := ptruint(@obj.PVSystemVars.P_priority);
+
+ // double properties
+ PropertyOffset[ord(TProp.irradiance)] := ptruint(@obj.PVSystemVars.FIrradiance);
+ PropertyOffset[ord(TProp.pf)] := ptruint(@obj.PFnominal);
+ PropertyOffset[ord(TProp.pctR)] := ptruint(@obj.pctR);
+ PropertyOffset[ord(TProp.pctX)] := ptruint(@obj.pctX);
+ PropertyOffset[ord(TProp.Temperature)] := ptruint(@obj.PVSystemVars.FTemperature);
+ PropertyOffset[ord(TProp.Pmpp)] := ptruint(@obj.PVSystemVars.FPmpp);
+ PropertyOffset[ord(TProp.pctCutin)] := ptruint(@obj.FpctCutIn);
+ PropertyOffset[ord(TProp.pctCutout)] := ptruint(@obj.FpctCutOut);
+ PropertyOffset[ord(TProp.Vminpu)] := ptruint(@obj.VMinPu);
+ PropertyOffset[ord(TProp.Vmaxpu)] := ptruint(@obj.VMaxPu);
+ PropertyOffset[ord(TProp.kVA)] := ptruint(@obj.PVSystemVars.FkVArating);
+ PropertyOffset[ord(TProp.DutyStart)] := ptruint(@obj.DutyStart);
+ PropertyOffset[ord(TProp.kv)] := ptruint(@obj.PVSystemVars.kVPVSystemBase);
+
+ PropertyOffset[ord(TProp.kvar)] := ptruint(@obj.kvarRequested);
+ PropertyReadFunction[ord(TProp.kvar)] := @Getkvar;
+ PropertyFlags[ord(TProp.kvar)] := [TPropertyFlag.ReadByFunction];
+
+ // double percent
+ PropertyScale[ord(TProp.pctPmpp)] := 0.01;
+ PropertyOffset[ord(TProp.pctPmpp)] := ptruint(@obj.PVSystemVars.FpuPmpp);
+
+ // advanced doubles
+ PropertyOffset[ord(TProp.kvarLimit)] := ptruint(@obj.PVSystemVars.Fkvarlimit);
+ PropertyFlags[ord(TProp.kvarLimit)] := [TPropertyFlag.Transform_Abs];
- // Override default help string
- PropertyHelp[NumPropsThisClass + 1] := 'Name of harmonic voltage or current spectrum for this PVSystem element. ' +
- 'A harmonic voltage source is assumed for the inverter. ' +
- 'Default value is "default", which is defined when the DSS starts.';
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
end;
-// ===========================================================================================
-function TPVsystem.NewObject(const ObjName: String): Integer;
+function TPVsystem.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new PVSystem element and add it to PVSystem class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TPVsystemObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-// ===========================================================================================
-procedure TPVsystem.SetNcondsForConnection;
-
+procedure SetNcondsForConnection(Obj: TObj);
begin
- with DSS.ActivePVsystemObj do
- begin
+ with Obj do
case Connection of
0:
NConds := Fnphases + 1;
@@ -620,10 +520,8 @@ procedure TPVsystem.SetNcondsForConnection;
NConds := Fnphases;
end;
end;
- end;
end;
-// ===========================================================================================
procedure TPVsystem.UpdateAll;
var
i: Integer;
@@ -634,343 +532,164 @@ procedure TPVsystem.UpdateAll;
UpdatePVSystem;
end;
-// ===========================================================================================
-procedure TPVsystem.InterpretConnection(const S: String);
-
-// Accepts
-// delta or LL (Case insensitive)
-// Y, wye, or LN
+procedure TPVsystemObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- TestS: String;
-
+ i: Integer;
begin
- with DSS.ActivePVsystemObj do
- begin
- TestS := lowercase(S);
- case TestS[1] of
- 'y', 'w':
- Connection := 0; {Wye}
- 'd':
- Connection := 1; {Delta or line-Line}
- 'l':
- case Tests[2] of
- 'n':
- Connection := 0;
- 'l':
- Connection := 1;
- end;
- end;
-
- SetNCondsForConnection;
-
- {VBase is always L-N voltage unless 1-phase device or more than 3 phases}
-
- with PVSystemVars do
- case Fnphases of
- 2, 3:
- VBase := kVPVSystemBase * InvSQRT3x1000; // L-N Volts
- else
- VBase := kVPVSystemBase * 1000.0; // Just use what is supplied
- end;
-
- VBaseMin := Vminpu * VBase;
- VBaseMax := Vmaxpu * VBase;
-
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
- end;
-end;
-
-
-//- - - - - - - - - - - - - - -MAIN EDIT FUNCTION - - - - - - - - - - - - - - -
-
-function TPVsystem.Edit: Integer;
-
-var
- i, iCase,
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
+ case Idx of
+ ord(TProp.UserModel):
+ DoSimpleMsg('%s model designated to use user-written model, but user-written model are not available for legacy models anymore (removed in DSS C-API v0.12).', [FullName], 1287);
+ ord(TProp.conn):
+ begin
+ SetNCondsForConnection(self);
-begin
+ // VBase is always L-N voltage unless 1-phase device or more than 3 phases
+ with PVSystemVars do
+ case Fnphases of
+ 2, 3:
+ VBase := kVPVSystemBase * InvSQRT3x1000; // L-N Volts
+ else
+ VBase := kVPVSystemBase * 1000.0; // Just use what is supplied
+ end;
- // continue parsing with contents of Parser
- DSS.ActivePVsystemObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActivePVsystemObj;
+ VBaseMin := Vminpu * VBase;
+ VBaseMax := Vmaxpu * VBase;
- Result := 0;
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
+ end;
- with DSS.ActivePVsystemObj do
- begin
+ ord(TProp.kv):
+ begin
+ with PVSystemVars do
+ case FNphases of
+ 2, 3:
+ VBase := kVPVSystemBase * InvSQRT3x1000;
+ else
+ VBase := kVPVSystemBase * 1000.0;
+ end;
+ end;
- ParamPointer := 0;
- ParamName := Parser.NextParam; // Parse next property off the command line
- Param := Parser.StrValue; // Put the string value of the property value in local memory for faster access
- while Length(Param) > 0 do
+ ord(TProp.kvar):
begin
+ kvarSpecified := TRUE;
+ PFSpecified := FALSE;
+ end;
- if (Length(ParamName) = 0) then
- Inc(ParamPointer) // If it is not a named property, assume the next property
- else
- ParamPointer := CommandList.GetCommand(ParamName); // Look up the name in the list for this class
+ ord(TProp.pf):
+ begin
+ PFSpecified := TRUE;
+ kvarSpecified := FALSE;
+ end;
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[PropertyIdxMap[ParamPointer]] := Param // Update the string value of the property
+ ord(TProp.kVA):
+ with PVSystemVars do
+ Fkvarlimit := FkVArating; // Reset kvar limit to kVA rating
+
+ ord(TProp.phases):
+ SetNCondsForConnection(self); // Force Reallocation of terminal info
+
+ ord(TProp.debugtrace):
+ if DebugTrace then
+ begin // Init trace file
+ FreeAndNil(TraceFile);
+ TraceFile := TBufferedFileStream.Create(DSS.OutputDirectory + 'STOR_' + Name + '.csv', fmCreate);
+ FSWrite(TraceFile, 't, Iteration, LoadMultiplier, Mode, LoadModel, PVSystemModel, Qnominalperphase, Pnominalperphase, CurrentType');
+ for i := 1 to nphases do
+ FSWrite(Tracefile, ', |Iinj' + IntToStr(i) + '|');
+ for i := 1 to nphases do
+ FsWrite(Tracefile, ', |Iterm' + IntToStr(i) + '|');
+ for i := 1 to nphases do
+ FSWrite(Tracefile, ', |Vterm' + IntToStr(i) + '|');
+ FSWrite(TraceFile, ',Vthev, Theta');
+ FSWriteln(TraceFile);
+ FSFlush(Tracefile);
+ end
else
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for PVSystem "' + Name + '"', 560);
-
- if (ParamPointer > 0) then
begin
- iCase := PropertyIdxMap[ParamPointer];
- case iCASE of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 561);
- 1:
- NPhases := Parser.Intvalue; // num phases
- 2:
- SetBus(1, param);
- propKV:
- PresentkV := Parser.DblValue;
- propIrradiance:
- PVSystemVars.FIrradiance := Parser.DblValue;
- propPF:
- begin
- PFSpecified := TRUE;
- kvarSpecified := FALSE;
- PFnominal := Parser.DblValue;
- end;
- propMODEL:
- VoltageModel := Parser.IntValue;
- propYEARLY:
- YearlyShape := Param;
- propDAILY:
- DailyShape := Param;
- propDUTY:
- DutyShape := Param;
- propTYEARLY:
- YearlyTShape := Param;
- propTDAILY:
- DailyTShape := Param;
- propTDUTY:
- DutyTShape := Param;
- propCONNECTION:
- InterpretConnection(Param);
- propKVAR:
- begin
- kvarSpecified := TRUE;
- PFSpecified := FALSE;
- Presentkvar := Parser.DblValue;
- end;
- propPCTR:
- pctR := Parser.DblValue;
- propPCTX:
- pctX := Parser.DblValue;
- propCLASS:
- FClass := Parser.IntValue;
- propInvEffCurve:
- InverterCurve := Param;
- propTemp:
- PVSystemVars.FTemperature := Parser.DblValue;
- propPmpp:
- PVSystemVars.FPmpp := Parser.DblValue;
- propP_T_Curve:
- Power_TempCurve := Param;
- propCutin:
- FpctCutIn := Parser.DblValue;
- propCutout:
- FpctCutOut := Parser.DblValue;
- propVMINPU:
- VMinPu := Parser.DblValue;
- propVMAXPU:
- VMaxPu := Parser.DblValue;
- propKVA:
- with PVSystemVars do
- begin
- FkVArating := Parser.DblValue;
- Fkvarlimit := FkVArating; // Reset kvar limit to kVA rating
- end;
- propUSERMODEL:
- UserModel.Name := Parser.StrValue; // Connect to user written models
- propUSERDATA:
- if UserModel.Exists then
- UserModel.Edit := Parser.StrValue; // Send edit string to user model
- propDEBUGTRACE:
- DebugTrace := InterpretYesNo(Param);
- proppctPmpp:
- PVSystemVars.FpuPmpp := Parser.DblValue / 100.0; // convert to pu
- propBalanced:
- ForceBalanced := InterpretYesNo(Param);
- propLimited:
- CurrentLimited := InterpretYesNo(Param);
- propVarFollowInverter:
- FVarFollowInverter := InterpretYesNo(Param);
- propkvarLimit:
- PVSystemVars.Fkvarlimit := Abs(Parser.DblValue);
- propDutyStart:
- DutyStart := Parser.DblValue;
- propPPriority:
- PVSystemVars.P_priority := InterpretYesNo(Param); // set watt priority flag
-
-
- else
- // Inherited parameters
- ClassEdit(DSS.ActivePVsystemObj, ParamPointer - NumPropsThisClass)
- end;
-
- case iCase of
- 1:
- SetNcondsForConnection; // Force Reallocation of terminal info
-
- {Set loadshape objects; returns nil If not valid}
- propYEARLY:
- YearlyShapeObj := DSS.LoadShapeClass.Find(YearlyShape);
- propDAILY:
- DailyShapeObj := DSS.LoadShapeClass.Find(DailyShape);
- propDUTY:
- DutyShapeObj := DSS.LoadShapeClass.Find(DutyShape);
-
- propTYEARLY:
- YearlyTShapeObj := DSS.TShapeClass.Find(YearlyTShape);
- propTDAILY:
- DailyTShapeObj := DSS.TShapeClass.Find(DailyTShape);
- propTDUTY:
- DutyTShapeObj := DSS.TShapeClass.Find(DutyTShape);
-
- propInvEffCurve:
- InverterCurveObj := DSS.XYCurveClass.Find(InverterCurve);
- propP_T_Curve:
- Power_TempCurveObj := DSS.XYCurveClass.Find(Power_TempCurve);
-
- propDEBUGTRACE:
- if DebugTrace then
- begin // Init trace file
- FreeAndNil(TraceFile);
- TraceFile := TFileStream.Create(DSS.OutputDirectory + 'STOR_' + Name + '.CSV', fmCreate);
- FSWrite(TraceFile, 't, Iteration, LoadMultiplier, Mode, LoadModel, PVSystemModel, Qnominalperphase, Pnominalperphase, CurrentType');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Iinj' + IntToStr(i) + '|');
- for i := 1 to nphases do
- FsWrite(Tracefile, ', |Iterm' + IntToStr(i) + '|');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Vterm' + IntToStr(i) + '|');
- FSWrite(TraceFile, ',Vthev, Theta');
- FSWriteln(TraceFile);
- FSFlush(Tracefile);
- end
- else
- begin
- FreeAndNil(TraceFile);
- end;
-
- end;
+ FreeAndNil(TraceFile);
end;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
+function TPVsystem.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
+ begin
RecalcElementData;
YPrimInvalid := TRUE;
+ Exclude(Flags, Flg.EditionActive);
end;
-
+ Result := True;
end;
-// ===========================================================================================
-function TPVsystem.MakeLike(const OtherPVsystemObjName: String): Integer;
-
-// Copy over essential properties from other object
-
+procedure TPVsystemObj.MakeLike(OtherPtr: Pointer);
var
- OtherPVsystemObj: TPVsystemObj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- {See If we can find this line name in the present collection}
- OtherPVsystemObj := Find(OtherPVsystemObjName);
- if (OtherPVsystemObj <> NIL) then
- with DSS.ActivePVsystemObj do
- begin
- if (Fnphases <> OtherPVsystemObj.Fnphases) then
- begin
- Nphases := OtherPVsystemObj.Fnphases;
- NConds := Fnphases; // Forces reallocation of terminal stuff
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
- end;
+ inherited MakeLike(OtherPtr);
- PVSystemVars.kVPVSystemBase := OtherPVsystemObj.PVSystemVars.kVPVSystemBase;
- Vbase := OtherPVsystemObj.Vbase;
- Vminpu := OtherPVsystemObj.Vminpu;
- Vmaxpu := OtherPVsystemObj.Vmaxpu;
- VBaseMin := OtherPVsystemObj.VBaseMin;
- VBaseMax := OtherPVsystemObj.VBaseMax;
- kW_out := OtherPVsystemObj.kW_out;
- kvar_out := OtherPVsystemObj.kvar_out;
- Pnominalperphase := OtherPVsystemObj.Pnominalperphase;
- PFnominal := OtherPVsystemObj.PFnominal;
- Qnominalperphase := OtherPVsystemObj.Qnominalperphase;
- Connection := OtherPVsystemObj.Connection;
- YearlyShape := OtherPVsystemObj.YearlyShape;
- YearlyShapeObj := OtherPVsystemObj.YearlyShapeObj;
- DailyShape := OtherPVsystemObj.DailyShape;
- DailyShapeObj := OtherPVsystemObj.DailyShapeObj;
- DutyShape := OtherPVsystemObj.DutyShape;
- DutyShapeObj := OtherPVsystemObj.DutyShapeObj;
- DutyStart := OtherPVsystemObj.DutyStart;
- YearlyTShape := OtherPVsystemObj.YearlyTShape;
- YearlyTShapeObj := OtherPVsystemObj.YearlyTShapeObj;
- DailyTShape := OtherPVsystemObj.DailyTShape;
- DailyTShapeObj := OtherPVsystemObj.DailyTShapeObj;
- DutyTShape := OtherPVsystemObj.DutyTShape;
- DutyTShapeObj := OtherPVsystemObj.DutyTShapeObj;
- InverterCurve := OtherPVsystemObj.InverterCurve;
- InverterCurveObj := OtherPVsystemObj.InverterCurveObj;
- Power_TempCurve := OtherPVsystemObj.Power_TempCurve;
- Power_TempCurveObj := OtherPVsystemObj.Power_TempCurveObj;
- FClass := OtherPVsystemObj.FClass;
- VoltageModel := OtherPVsystemObj.VoltageModel;
-
- PVSystemVars.FTemperature := OtherPVsystemObj.PVSystemVars.FTemperature;
- PVSystemVars.FPmpp := OtherPVsystemObj.PVSystemVars.FPmpp;
- FpctCutin := OtherPVsystemObj.FpctCutin;
- FpctCutout := OtherPVsystemObj.FpctCutout;
- FVarFollowInverter := OtherPVsystemObj.FVarFollowInverter;
- PVSystemVars.Fkvarlimit := OtherPVsystemObj.PVSystemVars.Fkvarlimit;
-
-
- PVSystemVars.FIrradiance := OtherPVsystemObj.PVSystemVars.FIrradiance;
-
- PVSystemVars.FkVArating := OtherPVsystemObj.PVSystemVars.FkVArating;
-
- pctR := OtherPVsystemObj.pctR;
- pctX := OtherPVsystemObj.pctX;
-
- RandomMult := OtherPVsystemObj.RandomMult;
- FVWMode := OtherPVsystemObj.FVWMode;
- FVWYAxis := OtherPVsystemObj.FVWYAxis;
- UserModel.Name := OtherPVsystemObj.UserModel.Name; // Connect to user written models
-
- ForceBalanced := OtherPVsystemObj.ForceBalanced;
- CurrentLimited := OtherPVsystemObj.CurrentLimited;
-
- ClassMakeLike(OtherPVsystemObj);
-
- for i := 1 to ParentClass.NumProperties do
- FPropertyValue^[i] := OtherPVsystemObj.FPropertyValue^[i];
-
- Result := 1;
- end
- else
- DoSimpleMsg('Error in PVSystem MakeLike: "' + OtherPVsystemObjName + '" Not Found.', 562);
+ Other := TObj(OtherPtr);
+ if (Fnphases <> Other.Fnphases) then
+ begin
+ FNphases := Other.Fnphases;
+ NConds := Fnphases; // Forces reallocation of terminal stuff
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
+ end;
+ PVSystemVars.kVPVSystemBase := Other.PVSystemVars.kVPVSystemBase;
+ Vbase := Other.Vbase;
+ Vminpu := Other.Vminpu;
+ Vmaxpu := Other.Vmaxpu;
+ VBaseMin := Other.VBaseMin;
+ VBaseMax := Other.VBaseMax;
+ kW_out := Other.kW_out;
+ kvar_out := Other.kvar_out;
+ Pnominalperphase := Other.Pnominalperphase;
+ PFnominal := Other.PFnominal;
+ Qnominalperphase := Other.Qnominalperphase;
+ Connection := Other.Connection;
+ YearlyShapeObj := Other.YearlyShapeObj;
+ DailyShapeObj := Other.DailyShapeObj;
+ DutyShapeObj := Other.DutyShapeObj;
+ DutyStart := Other.DutyStart;
+ YearlyTShapeObj := Other.YearlyTShapeObj;
+ DailyTShapeObj := Other.DailyTShapeObj;
+ DutyTShapeObj := Other.DutyTShapeObj;
+ InverterCurveObj := Other.InverterCurveObj;
+ Power_TempCurveObj := Other.Power_TempCurveObj;
+ FClass := Other.FClass;
+ VoltageModel := Other.VoltageModel;
+
+ PVSystemVars.FTemperature := Other.PVSystemVars.FTemperature;
+ PVSystemVars.FPmpp := Other.PVSystemVars.FPmpp;
+ FpctCutin := Other.FpctCutin;
+ FpctCutout := Other.FpctCutout;
+ VarFollowInverter := Other.VarFollowInverter;
+ PVSystemVars.Fkvarlimit := Other.PVSystemVars.Fkvarlimit;
+
+ PVSystemVars.FIrradiance := Other.PVSystemVars.FIrradiance;
+
+ PVSystemVars.FkVArating := Other.PVSystemVars.FkVArating;
+
+ pctR := Other.pctR;
+ pctX := Other.pctX;
+
+ RandomMult := Other.RandomMult;
+ FVWMode := Other.FVWMode;
+ FVWYAxis := Other.FVWYAxis;
+ UserModelNameStr := Other.UserModelNameStr;
+ //UserModelEditStr := Other.UserModelEditStr; -- TODO: not copied?
+
+ ForceBalanced := Other.ForceBalanced;
+ CurrentLimited := Other.CurrentLimited;
end;
-{--------------------------------------------------------------------------}
procedure TPVsystem.ResetRegistersAll; // Force all EnergyMeters in the circuit to reset
-
var
idx: Integer;
-
begin
idx := First;
while (idx > 0) do
@@ -980,9 +699,7 @@ procedure TPVsystem.ResetRegistersAll; // Force all EnergyMeters in the circuit
end;
end;
-{--------------------------------------------------------------------------}
procedure TPVsystem.SampleAll; // Force all active PV System energy meters to take a sample
-
var
i: Integer;
begin
@@ -992,42 +709,32 @@ procedure TPVsystem.SampleAll; // Force all active PV System energy meters to
TakeSample;
end;
-// ===========================================================================================
constructor TPVsystemObj.Create(ParClass: TDSSClass; const SourceName: String);
begin
-
inherited create(ParClass);
- Name := LowerCase(SourceName);
+ Name := AnsiLowerCase(SourceName);
DSSObjType := ParClass.DSSClassType; // + PVSystem_ELEMENT; // In both PCelement and PVSystemelement list
TraceFile := nil;
- Nphases := 3;
+ FNphases := 3;
Fnconds := 4; // defaults to wye
Yorder := 0; // To trigger an initial allocation
Nterms := 1; // forces allocations
- YearlyShape := '';
YearlyShapeObj := NIL; // If YearlyShapeobj = nil Then the Irradiance alway stays nominal
- DailyShape := '';
DailyShapeObj := NIL; // If DaillyShapeobj = nil Then the Irradiance alway stays nominal
- DutyShape := '';
DutyShapeObj := NIL; // If DutyShapeobj = nil Then the Irradiance alway stays nominal
DutyStart := 0.0;
- YearlyTShape := '';
YearlyTShapeObj := NIL; // If YearlyShapeobj = nil Then the Temperature always stays nominal
- DailyTShape := '';
DailyTShapeObj := NIL; // If DaillyShapeobj = nil Then the Temperature always stays nominal
- DutyTShape := '';
DutyTShapeObj := NIL; // If DutyShapeobj = nil Then the Temperature always stays nominal
InverterCurveObj := NIL;
Power_TempCurveObj := NIL;
- InverterCurve := '';
- Power_TempCurve := '';
Connection := 0; // Wye (star, L-N)
- VoltageModel := 1; {Typical fixed kW negative load}
+ VoltageModel := 1; // Typical fixed kW negative load
FClass := 1;
PVSystemSolutionCount := -1; // For keep track of the present solution in Injcurrent calcs
@@ -1046,7 +753,7 @@ constructor TPVsystemObj.Create(ParClass: TDSSClass; const SourceName: String);
PFSpecified := TRUE;
kvarSpecified := FALSE;
FInverterON := TRUE; // start with inverterON
- FVarFollowInverter := FALSE;
+ VarFollowInverter := FALSE;
ForceBalanced := FALSE;
CurrentLimited := FALSE;
@@ -1065,19 +772,19 @@ constructor TPVsystemObj.Create(ParClass: TDSSClass; const SourceName: String);
FpctCutIn := 20.0;
FpctCutOut := 20.0;
- {Output rating stuff}
+ // Output rating stuff
kW_out := 500.0;
kvar_out := 0.0;
PFnominal := 1.0;
pctR := 50.0;
- ;
pctX := 0.0;
PublicDataStruct := @PVSystemVars;
PublicDataSize := SizeOf(TPVSystemVars);
- UserModel := TPVsystemUserModel.Create(DSS);
+ UserModelNameStr := '';
+ UserModelEditStr := '';
Reg_kWh := 1;
Reg_kvarh := 2;
@@ -1088,170 +795,22 @@ constructor TPVsystemObj.Create(ParClass: TDSSClass; const SourceName: String);
DebugTrace := FALSE;
PVsystemObjSwitchOpen := FALSE;
- Spectrum := ''; // override base class
- SpectrumObj := NIL;
+ SpectrumObj := NIL; // override base class
FVWMode := FALSE;
FVWYAxis := 1;
- InitPropertyValues(0);
RecalcElementData;
-
-end;
-
-
-// ===========================================================================================
-procedure TPVsystemObj.InitPropertyValues(ArrayOffset: Integer);
-
-// Define default values for the properties
-
-begin
-
- with PVSystemVars do
- begin
- PropertyValue[1] := '3'; //'phases';
- PropertyValue[2] := Getbus(1); //'bus1';
-
- PropertyValue[propKV] := Format('%-g', [kVPVSystemBase]);
- PropertyValue[propIrradiance] := Format('%-g', [FIrradiance]);
- PropertyValue[propPF] := Format('%-g', [PFnominal]);
- PropertyValue[propMODEL] := '1';
- PropertyValue[propYEARLY] := '';
- PropertyValue[propDAILY] := '';
- PropertyValue[propDUTY] := '';
- PropertyValue[propTYEARLY] := '';
- PropertyValue[propTDAILY] := '';
- PropertyValue[propTDUTY] := '';
- PropertyValue[propCONNECTION] := 'wye';
- PropertyValue[propKVAR] := Format('%-g', [Presentkvar]);
-
- PropertyValue[propPCTR] := Format('%-g', [pctR]);
- PropertyValue[propPCTX] := Format('%-g', [pctX]);
-
- PropertyValue[propCLASS] := '1'; //'class'
-
- PropertyValue[propInvEffCurve] := '';
- PropertyValue[propTemp] := Format('%-g', [FTemperature]);
- PropertyValue[propPmpp] := Format('%-g', [FPmpp]);
- PropertyValue[propP_T_Curve] := '';
- PropertyValue[propCutin] := '20';
- PropertyValue[propCutout] := '20';
- PropertyValue[propVarFollowInverter] := 'NO';
-
- PropertyValue[propVMINPU] := '0.90';
- PropertyValue[propVMAXPU] := '1.10';
- PropertyValue[propKVA] := Format('%-g', [FkVArating]);
-
- PropertyValue[propUSERMODEL] := ''; // Usermodel
- PropertyValue[propUSERDATA] := ''; // Userdata
- PropertyValue[propDEBUGTRACE] := 'NO';
- PropertyValue[proppctPmpp] := '100';
- PropertyValue[propBalanced] := 'NO';
- PropertyValue[propLimited] := 'NO';
- PropertyValue[propkvarLimit] := Format('%-g', [Fkvarlimit]);
- PropertyValue[propPpriority] := 'NO'
-
- end;
-
- inherited InitPropertyValues(NumPropsThisClass);
-
end;
-
-// ===========================================================================================
-function TPVsystemObj.GetPropertyValue(Index: Integer): String;
-
-
-begin
-
- Result := '';
- with PVSystemVars do
- case Index of
- propKV:
- Result := Format('%.6g', [kVPVSystemBase]);
- propIrradiance:
- Result := Format('%.6g', [FIrradiance]);
- propPF:
- Result := Format('%.6g', [PFnominal]);
- propMODEL:
- Result := Format('%d', [VoltageModel]);
- propYEARLY:
- Result := YearlyShape;
- propDAILY:
- Result := DailyShape;
- propDUTY:
- Result := DutyShape;
-
- propTYEARLY:
- Result := YearlyTShape;
- propTDAILY:
- Result := DailyTShape;
- propTDUTY:
- Result := DutyTShape;
-
- {propCONNECTION :;}
- propKVAR:
- Result := Format('%.6g', [kvar_out]);
- propPCTR:
- Result := Format('%.6g', [pctR]);
- propPCTX:
- Result := Format('%.6g', [pctX]);
- {propCLASS = 17;}
- propInvEffCurve:
- Result := InverterCurve;
- propTemp:
- Result := Format('%.6g', [FTemperature]);
- propPmpp:
- Result := Format('%.6g', [FPmpp]);
- propP_T_Curve:
- Result := Power_TempCurve;
- propCutin:
- Result := Format('%.6g', [FpctCutin]);
- propCutOut:
- Result := Format('%.6g', [FpctCutOut]);
- propVarFollowInverter:
- Result := StrYorN(FVarFollowInverter);
- propVMINPU:
- Result := Format('%.6g', [VMinPu]);
- propVMAXPU:
- Result := Format('%.6g', [VMaxPu]);
- propKVA:
- Result := Format('%.6g', [FkVArating]);
-
- propUSERMODEL:
- Result := UserModel.Name;
- propUSERDATA:
- Result := '(' + inherited GetPropertyValue(index) + ')';
- proppctPmpp:
- Result := Format('%.6g', [FpuPmpp * 100.0]);
- propBalanced:
- Result := StrYorN(ForceBalanced);
- propLimited:
- Result := StrYorN(CurrentLimited);
- propkvarLimit:
- Result := Format('%.6g', [Fkvarlimit]);
- propDutyStart:
- Result := Format('%.6g', [DutyStart]);
-
-
- {propDEBUGTRACE = 33;}
- else // take the generic handler
- Result := inherited GetPropertyValue(index);
- end;
-end;
-
-// ===========================================================================================
destructor TPVsystemObj.Destroy;
begin
YPrimOpenCond.Free;
- UserModel.Free;
FreeAndNil(TraceFile);
inherited Destroy;
end;
-// ===========================================================================================
procedure TPVsystemObj.Randomize(Opt: Integer);
begin
-
case Opt of
0:
RandomMult := 1.0;
@@ -1262,12 +821,9 @@ procedure TPVsystemObj.Randomize(Opt: Integer);
LOGNORMAL:
RandomMult := QuasiLognormal(YearlyShapeObj.Mean);
end;
-
end;
-// ===========================================================================================
procedure TPVsystemObj.CalcDailyMult(Hr: Double);
-
begin
if (DailyShapeObj <> NIL) then
begin
@@ -1277,7 +833,6 @@ procedure TPVsystemObj.CalcDailyMult(Hr: Double);
ShapeFactor := CDOUBLEONE; // Default to no variation
end;
-// ===========================================================================================
procedure TPVsystemObj.CalcDailyTemperature(Hr: Double);
begin
if (DailyTShapeObj <> NIL) then
@@ -1289,9 +844,7 @@ procedure TPVsystemObj.CalcDailyTemperature(Hr: Double);
; // Default to no variation
end;
-// ===========================================================================================
procedure TPVsystemObj.CalcDutyMult(Hr: Double);
-
begin
if DutyShapeObj <> NIL then
begin
@@ -1301,7 +854,6 @@ procedure TPVsystemObj.CalcDutyMult(Hr: Double);
CalcDailyMult(Hr); // Default to Daily Mult If no duty curve specified
end;
-// ===========================================================================================
procedure TPVsystemObj.CalcDutyTemperature(Hr: Double);
begin
if DutyTShapeObj <> NIL then
@@ -1312,9 +864,7 @@ procedure TPVsystemObj.CalcDutyTemperature(Hr: Double);
CalcDailyTemperature(Hr); // Default to Daily Mult If no duty curve specified
end;
-// ===========================================================================================
procedure TPVsystemObj.CalcYearlyMult(Hr: Double);
-
begin
if YearlyShapeObj <> NIL then
begin
@@ -1324,8 +874,6 @@ procedure TPVsystemObj.CalcYearlyMult(Hr: Double);
CalcDailyMult(Hr); // Defaults to Daily curve
end;
-
-// ===========================================================================================
procedure TPVsystemObj.CalcYearlyTemperature(Hr: Double);
begin
if YearlyTShapeObj <> NIL then
@@ -1334,14 +882,10 @@ procedure TPVsystemObj.CalcYearlyTemperature(Hr: Double);
end
else
CalcDailyTemperature(Hr); // Defaults to Daily curve
-
end;
-// ===========================================================================================
procedure TPVsystemObj.RecalcElementData;
-
begin
-
VBaseMin := VMinPu * VBase;
VBaseMax := VMaxPu * VBase;
@@ -1360,52 +904,14 @@ procedure TPVsystemObj.RecalcElementData;
SetNominalPVSystemOuput;
- {Now check for errors. If any of these came out nil and the string was not nil, give warning}
- if YearlyShapeObj = NIL then
- if Length(YearlyShape) > 0 then
- DoSimpleMsg('WARNING! Yearly load shape: "' + YearlyShape + '" Not Found.', 563);
- if DailyShapeObj = NIL then
- if Length(DailyShape) > 0 then
- DoSimpleMsg('WARNING! Daily load shape: "' + DailyShape + '" Not Found.', 564);
- if DutyShapeObj = NIL then
- if Length(DutyShape) > 0 then
- DoSimpleMsg('WARNING! Duty load shape: "' + DutyShape + '" Not Found.', 565);
- if YearlyTShapeObj = NIL then
- if Length(YearlyTShape) > 0 then
- DoSimpleMsg('WARNING! Yearly temperature shape: "' + YearlyTShape + '" Not Found.', 5631);
- if DailyTShapeObj = NIL then
- if Length(DailyTShape) > 0 then
- DoSimpleMsg('WARNING! Daily temperature shape: "' + DailyTShape + '" Not Found.', 5641);
- if DutyTShapeObj = NIL then
- if Length(DutyTShape) > 0 then
- DoSimpleMsg('WARNING! Duty temperature shape: "' + DutyTShape + '" Not Found.', 5651);
-
- if Length(Spectrum) > 0 then
- begin
- SpectrumObj := DSS.SpectrumClass.Find(Spectrum);
- if SpectrumObj = NIL then
- DoSimpleMsg('ERROR! Spectrum "' + Spectrum + '" Not Found.', 566);
- end
- else
- SpectrumObj := NIL;
-
// Initialize to Zero - defaults to PQ PVSystem element
// Solution object will reset after circuit modifications
Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
-
- {Update any user-written models}
- if Usermodel.Exists then
- UserModel.FUpdateModel;
-
end;
-
-// ===========================================================================================
procedure TPVsystemObj.SetNominalPVSystemOuput;
-
begin
-
ShapeFactor := CDOUBLEONE; // init here; changed by curve routine
TShapeValue := PVSystemVars.FTemperature; // init here; changed by curve routine
@@ -1419,7 +925,7 @@ procedure TPVsystemObj.SetNominalPVSystemOuput;
with Solution do
case Mode of
- TSolveMode.SNAPSHOT: ; {Just solve for the present kW, kvar} // Don't check for state change
+ TSolveMode.SNAPSHOT: ; // Just solve for the present kW, kvar // Don't check for state change
TSolveMode.DAILYMODE:
begin
CalcDailyMult(DynaVars.dblHour);
@@ -1430,12 +936,10 @@ procedure TPVsystemObj.SetNominalPVSystemOuput;
CalcYearlyMult(DynaVars.dblHour);
CalcYearlyTemperature(DynaVars.dblHour);
end;
- (*
- MONTECARLO1,
- MONTEFAULT,
- FAULTSTUDY,
- DYNAMICMODE: ; // {do nothing yet}
- *)
+ // TSolveMode.MONTECARLO1,
+ // TSolveMode.MONTEFAULT,
+ // TSolveMode.FAULTSTUDY,
+ // TSolveMode.DYNAMICMODE: ; // do nothing yet
TSolveMode.GENERALTIME:
begin
// This mode allows use of one class of load shape
@@ -1480,7 +984,7 @@ procedure TPVsystemObj.SetNominalPVSystemOuput;
CalcDutyMult(DynaVars.dblHour);
CalcDutyTemperature(DynaVars.dblHour);
end;
- {AUTOADDFLAG: ; }
+ // AUTOADDFLAG: ;
end;
ComputekWkvar;
@@ -1494,15 +998,15 @@ procedure TPVsystemObj.SetNominalPVSystemOuput;
else
- YEQ := CDivReal(Cmplx(Pnominalperphase, -Qnominalperphase), Sqr(Vbase)); // Vbase must be L-N for 3-phase
+ YEQ := Cmplx(Pnominalperphase, -Qnominalperphase) / Sqr(Vbase); // Vbase must be L-N for 3-phase
if (Vminpu <> 0.0) then
- YEQ_Min := CDivReal(YEQ, SQR(Vminpu)) // at 95% voltage
+ YEQ_Min := YEQ / SQR(Vminpu) // at 95% voltage
else
YEQ_Min := YEQ; // Always a constant Z model
if (Vmaxpu <> 0.0) then
- YEQ_Max := CDivReal(YEQ, SQR(Vmaxpu)) // at 105% voltage
+ YEQ_Max := YEQ / SQR(Vmaxpu) // at 105% voltage
else
YEQ_Max := YEQ;
@@ -1510,44 +1014,37 @@ procedure TPVsystemObj.SetNominalPVSystemOuput;
}
with PVSystemvars do
begin
- PhaseCurrentLimit := Cdivreal(Cmplx(Pnominalperphase, Qnominalperphase), VBaseMin);
+ PhaseCurrentLimit := Cmplx(Pnominalperphase, Qnominalperphase) / VBaseMin;
MaxDynPhaseCurrent := Cabs(PhaseCurrentLimit);
end;
end;
- { When we leave here, all the YEQ's are in L-N values}
-
- end; {If NOT (IsDynamicModel or IsHarmonicModel)}
- end; {With ActiveCircuit}
-
+ // When we leave here, all the YEQ's are in L-N values
+ end; // If NOT (IsDynamicModel or IsHarmonicModel)
+ end; // With ActiveCircuit
end;
-
-// ===========================================================================================
procedure TPVsystemObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
-
var
Y, Yij: Complex;
i, j: Integer;
FreqMultiplier: Double;
-
begin
-
FYprimFreq := ActiveCircuit.Solution.Frequency;
FreqMultiplier := FYprimFreq / BaseFrequency;
- with ActiveCircuit.solution do
+ with ActiveCircuit.solution do
if IsDynamicModel or IsHarmonicModel then
begin
- {YEQ is computed from %R and %X -- inverse of Rthev + j Xthev}
+ // YEQ is computed from %R and %X -- inverse of Rthev + j Xthev
Y := YEQ; // L-N value computed in initialization routines
if Connection = 1 then
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
+ Y := Y / 3.0; // Convert to delta impedance
Y.im := Y.im / FreqMultiplier;
- Yij := Cnegate(Y);
+ Yij := -Y;
for i := 1 to Fnphases do
begin
case Connection of
@@ -1558,7 +1055,7 @@ procedure TPVsystemObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
Ymatrix.SetElemsym(i, Fnconds, Yij);
end;
1:
- begin {Delta connection}
+ begin // Delta connection
Ymatrix.SetElement(i, i, Y);
Ymatrix.AddElement(i, i, Y); // put it in again
for j := 1 to i - 1 do
@@ -1570,11 +1067,11 @@ procedure TPVsystemObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
else
begin // Regular power flow PVSystem element model
- {YEQ is always expected as the equivalent line-neutral admittance}
+ // YEQ is always expected as the equivalent line-neutral admittance
- Y := cnegate(YEQ); // negate for generation YEQ is L-N quantity
+ Y := -YEQ; // negate for generation YEQ is L-N quantity
- // ****** Need to modify the base admittance for real harmonics calcs
+ // ****** Need to modify the base admittance for real harmonics calcs
Y.im := Y.im / FreqMultiplier;
case Connection of
@@ -1582,7 +1079,7 @@ procedure TPVsystemObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
0:
with YMatrix do
begin // WYE
- Yij := Cnegate(Y);
+ Yij := -Y;
for i := 1 to Fnphases do
begin
SetElement(i, i, Y);
@@ -1594,8 +1091,8 @@ procedure TPVsystemObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
1:
with YMatrix do
begin // Delta or L-L
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
- Yij := Cnegate(Y);
+ Y := Y / 3.0; // Convert to delta impedance
+ Yij := -Y;
for i := 1 to Fnphases do
begin
j := i + 1;
@@ -1608,15 +1105,11 @@ procedure TPVsystemObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
end;
end;
- end; {ELSE IF Solution.mode}
-
+ end; // ELSE IF Solution.mode
end;
-
-// ===========================================================================================
procedure TPVsystemObj.ComputeInverterPower;
var
-
kVA_Gen: Double;
begin
with PVSystemVars do
@@ -1624,7 +1117,7 @@ procedure TPVsystemObj.ComputeInverterPower;
EffFactor := 1.0;
kW_Out := 0.0;
- // Determine state of the inverter
+ // Determine state of the inverter
if FInverterON then
begin
if Panelkw < CutOutkW then
@@ -1640,8 +1133,7 @@ procedure TPVsystemObj.ComputeInverterPower;
end;
end;
-
- // set inverter output. Defaults to 100% of the panelkW if no efficiency curve spec'd
+ // set inverter output. Defaults to 100% of the panelkW if no efficiency curve spec'd
if FInverterON then
begin
if Assigned(InverterCurveObj) then
@@ -1652,7 +1144,6 @@ procedure TPVsystemObj.ComputeInverterPower;
// if VOLTWATT control mode is not enabled then go with
// panelKW* EffFactor*puPmpp (puPmpp can be set locally in the
// PVSystem object, but defaults to 100% or 1.0 per-unit.
-
end
else
begin
@@ -1685,7 +1176,7 @@ procedure TPVsystemObj.ComputeInverterPower;
else
kvar_Out := kvarRequested;
end;
- if (FInverterON = FALSE) and (FVarFollowInverter = TRUE) then
+ if (FInverterON = FALSE) and (VarFollowInverter = TRUE) then
kvar_out := 0.0;
@@ -1723,27 +1214,20 @@ procedure TPVsystemObj.ComputeInverterPower;
end;
end;
- if (FInverterON = FALSE) and (FVarFollowInverter = TRUE) then
+ if (FInverterON = FALSE) and (VarFollowInverter = TRUE) then
kvar_out := 0.0;
- end; {With PVSystemVars}
-
+ end; // With PVSystemVars
end;
-// ===========================================================================================
procedure TPVsystemObj.ComputekWkvar;
begin
-
ComputePanelPower; // apply irradiance
ComputeInverterPower; // apply inverter eff after checking for cutin/cutout
-
end;
-// ===========================================================================================
procedure TPVsystemObj.ComputePanelPower;
-
begin
-
with PVSystemVars do
begin
TempFactor := 1.0;
@@ -1754,19 +1238,14 @@ procedure TPVsystemObj.ComputePanelPower;
PanelkW := FIrradiance * ShapeFactor.re * FPmpp * TempFactor;
end;
-
end;
-// ===========================================================================================
procedure TPVsystemObj.CalcYPrim;
-
var
i: Integer;
-
begin
-
- // Build only shunt Yprim
- // Build a dummy Yprim Series so that CalcV Does not fail
+ // Build only shunt Yprim
+ // Build a dummy Yprim Series so that CalcV Does not fail
if YPrimInvalid then
begin
if YPrim_Shunt <> NIL then
@@ -1791,50 +1270,43 @@ procedure TPVsystemObj.CalcYPrim;
// Set YPrim_Series based on diagonals of YPrim_shunt so that CalcVoltages Doesn't fail
for i := 1 to Yorder do
- Yprim_Series.SetElement(i, i, CmulReal(Yprim_Shunt.Getelement(i, i), 1.0e-10));
+ Yprim_Series.SetElement(i, i, Yprim_Shunt.Getelement(i, i) * 1.0e-10);
YPrim.CopyFrom(YPrim_Shunt);
// Account for Open Conductors
inherited CalcYPrim;
-
end;
-// ===========================================================================================
procedure TPVsystemObj.StickCurrInTerminalArray(TermArray: pComplexArray; const Curr: Complex; i: Integer);
- {Add the current into the proper location according to connection}
-
- {Reverse of similar routine in load (Cnegates are switched)}
+ // Add the current into the proper location according to connection
+ // Reverse of similar routine in load (Cnegates are switched)
var
j: Integer;
-
begin
case Connection of
0:
begin //Wye
- Caccum(TermArray^[i], Curr);
- Caccum(TermArray^[Fnconds], Cnegate(Curr)); // Neutral
+ TermArray^[i] += Curr;
+ TermArray^[Fnconds] -= Curr; // Neutral
end;
1:
begin //DELTA
- Caccum(TermArray^[i], Curr);
+ TermArray^[i] += Curr;
j := i + 1;
if j > Fnconds then
j := 1;
- Caccum(TermArray^[j], Cnegate(Curr));
+ TermArray^[j] -= Curr;
end;
end;
end;
-// ===========================================================================================
procedure TPVsystemObj.WriteTraceRecord(const s: String);
-
var
i: Integer;
sout: String;
begin
-
try
if (not DSS.InShowResults) then
begin
@@ -1842,8 +1314,8 @@ procedure TPVsystemObj.WriteTraceRecord(const s: String);
[ActiveCircuit.Solution.DynaVARs.t,
ActiveCircuit.Solution.Iteration,
ActiveCircuit.LoadMultiplier]),
- GetSolutionModeID(DSS), ', ',
- GetLoadModel(DSS), ', ',
+ DSS.SolveModeEnum.OrdinalToString(ord(DSS.ActiveCircuit.Solution.mode)), ', ',
+ DSS.DefaultLoadModelEnum.OrdinalToString(DSS.ActiveCircuit.Solution.LoadModel), ', ',
VoltageModel: 0, ', ',
(Qnominalperphase * 3.0 / 1.0e6): 8: 2, ', ',
(Pnominalperphase * 3.0 / 1.0e6): 8: 2, ', ',
@@ -1876,12 +1348,8 @@ procedure TPVsystemObj.WriteTraceRecord(const s: String);
end;
end;
-
-// ===========================================================================================
procedure TPVsystemObj.DoConstantPQPVsystemObj;
-
-{Compute total terminal current for Constant PQ}
-
+// Compute total terminal current for Constant PQ
var
i: Integer;
PhaseCurr,
@@ -1890,7 +1358,6 @@ procedure TPVsystemObj.DoConstantPQPVsystemObj;
VmagLN,
VmagLL: Double;
V012: array[0..2] of Complex; // Sequence voltages
-
begin
//Treat this just like the Load model
@@ -1909,53 +1376,52 @@ procedure TPVsystemObj.DoConstantPQPVsystemObj;
for i := 1 to Fnphases do
begin
-
case Connection of
0:
- begin {Wye}
+ begin // Wye
VLN := Vterminal^[i];
VMagLN := Cabs(VLN);
if CurrentLimited then
begin
- {Current-Limited Model}
- PhaseCurr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLN));
+ // Current-Limited Model
+ PhaseCurr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLN);
if Cabs(PhaseCurr) > PVSystemvars.MaxDynPhaseCurrent then
- PhaseCurr := Conjg(Cdiv(PhaseCurrentLimit, CDivReal(VLN, VMagLN)));
+ PhaseCurr := cong(PhaseCurrentLimit / (VLN / VMagLN));
end
else
begin
- {The usual model}
+ // The usual model
if (VMagLN <= VBaseMin) then
- PhaseCurr := Cmul(YEQ_Min, VLN) // Below Vminpu use an impedance model
+ PhaseCurr := YEQ_Min * VLN // Below Vminpu use an impedance model
else
if (VMagLN > VBaseMax) then
- PhaseCurr := Cmul(YEQ_Max, VLN) // above Vmaxpu use an impedance model
+ PhaseCurr := YEQ_Max * VLN // above Vmaxpu use an impedance model
else
- PhaseCurr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLN)); // Between Vminpu and Vmaxpu, constant PQ
+ PhaseCurr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLN); // Between Vminpu and Vmaxpu, constant PQ
end;
- StickCurrInTerminalArray(ITerminal, Cnegate(PhaseCurr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -PhaseCurr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, PhaseCurr, i); // Put into Terminal array taking into account connection
end;
1:
- begin {Delta}
+ begin // Delta
VLL := Vterminal^[i];
VMagLL := Cabs(VLL);
if CurrentLimited then
begin
- {Current-Limited Model}
- DeltaCurr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLL));
+ // Current-Limited Model
+ DeltaCurr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLL);
if Cabs(DeltaCurr) * SQRT3 > PVSystemvars.MaxDynPhaseCurrent then
- DeltaCurr := Conjg(Cdiv(PhaseCurrentLimit, CDivReal(VLL, VMagLL / SQRT3)));
+ DeltaCurr := cong(PhaseCurrentLimit / (VLL / (VMagLL / SQRT3)));
end
else
begin
- {The usual model}
+ // The usual model
case Fnphases of
2, 3:
VMagLN := VmagLL / SQRT3;
@@ -1963,38 +1429,31 @@ procedure TPVsystemObj.DoConstantPQPVsystemObj;
VMagLN := VmagLL;
end;
if VMagLN <= VBaseMin then
- DeltaCurr := Cmul(CdivReal(YEQ_Min, 3.0), VLL) // Below 95% use an impedance model
+ DeltaCurr := (YEQ_Min / 3.0) * VLL // Below 95% use an impedance model
else
if VMagLN > VBaseMax then
- DeltaCurr := Cmul(CdivReal(YEQ_Max, 3.0), VLL) // above 105% use an impedance model
+ DeltaCurr := (YEQ_Max / 3.0) * VLL // above 105% use an impedance model
else
- DeltaCurr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLL)); // Between 95% -105%, constant PQ
+ DeltaCurr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLL); // Between 95% -105%, constant PQ
end;
- StickCurrInTerminalArray(ITerminal, Cnegate(DeltaCurr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -DeltaCurr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, DeltaCurr, i); // Put into Terminal array taking into account connection
end;
-
end;
-
end;
-
end;
-// ===========================================================================================
procedure TPVsystemObj.DoConstantZPVsystemObj;
-
-{constant Z model}
+// constant Z model
var
i: Integer;
Curr,
YEQ2: Complex;
V012: array[0..2] of Complex; // Sequence voltages
-
begin
-
-// Assume YEQ is kept up to date
+ // Assume YEQ is kept up to date
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the load
@@ -2011,51 +1470,20 @@ procedure TPVsystemObj.DoConstantZPVsystemObj;
if (Connection = 0) then
YEQ2 := YEQ // YEQ is always line to neutral
else
- YEQ2 := CdivReal(YEQ, 3.0); // YEQ for delta connection
+ YEQ2 := YEQ / 3.0; // YEQ for delta connection
for i := 1 to Fnphases do
begin
-
- Curr := Cmul(YEQ2, Vterminal^[i]);
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ Curr := YEQ2 * Vterminal^[i];
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
-
-end;
-
-
-// =================================================================DOUSERMODEL==========================
-procedure TPVsystemObj.DoUserModel;
-{Compute total terminal Current from User-written model}
-var
- i: Integer;
-
-begin
-
- CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
-
- if UserModel.Exists // Check automatically selects the usermodel If true
- then
- begin
- UserModel.FCalc(Vterminal, Iterminal);
- IterminalUpdated := TRUE;
- with ActiveCircuit.Solution do
- begin // Negate currents from user model for power flow PVSystem element model
- for i := 1 to FnConds do
- Caccum(InjCurrent^[i], Cnegate(Iterminal^[i]));
- end;
- end
- else
- DoSimpleMsg('PVSystem.' + name + ' model designated to use user-written model, but user-written model is not defined.', 567);
-
end;
-// ===============================================================DoDynamicMode============================
procedure TPVsystemObj.DoDynamicMode;
-
-{Compute Total Current and add into InjTemp}
+// Compute Total Current and add into InjTemp
var
i: Integer;
V012,
@@ -2063,19 +1491,16 @@ procedure TPVsystemObj.DoDynamicMode;
Vthev: Complex;
Theta: Double; // phase angle of thevinen source
- {-------------- Internal Proc -----------------------}
+ // -------------- Internal Proc -----------------------
procedure CalcVthev_Dyn(const V: Complex);
- {
- If the voltage magnitude drops below 15% or so, the accuracy of determining the
- phase angle gets flaky. This algorithm approximates the action of a PLL that will
- hold the last phase angle until the voltage recovers.
- }
+ // If the voltage magnitude drops below 15% or so, the accuracy of determining the
+ // phase angle gets flaky. This algorithm approximates the action of a PLL that will
+ // hold the last phase angle until the voltage recovers.
begin
- {Try to keep in phase with terminal voltage}
+ // Try to keep in phase with terminal voltage
with PVSystemVars do
begin
-
if Cabs(V) > 0.20 * Vbase then
Theta := ThetaDyn + (Cang(V) - InitialVangle)
else
@@ -2087,45 +1512,36 @@ procedure TPVsystemObj.DoDynamicMode;
end;
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array and computes VTerminal
- {Inj = -Itotal (in) - Yprim*Vtemp}
+ // Inj = -Itotal (in) - Yprim*Vtemp
case VoltageModel of
3:
- if UserModel.Exists then // auto selects model
- begin {We have total currents in Iterminal}
- UserModel.FCalc(Vterminal, Iterminal); // returns terminal currents in Iterminal
- end
- else
- begin
- DoSimpleMsg(Format('Dynamics model missing for PVSystem.%s ', [Name]), 5671);
- DSS.SolutionAbort := TRUE;
- end;
- else {All other models -- current-limited like Generator Model 7}
-
- {
- This is a simple model that is basically a thevinen equivalent without inertia
- }
+ begin
+ DoSimpleMsg('Dynamics model missing for %s ', [FullName], 5671);
+ DSS.SolutionAbort := TRUE;
+ end;
+ else // All other models -- current-limited like Generator Model 7
- case Fnphases of {No user model, use default Thevinen equivalent for standard Generator model}
+ // This is a simple model that is basically a thevinen equivalent without inertia
+ case Fnphases of // No user model, use default Thevinen equivalent for standard Generator model
1:
with PVSystemVars do
begin
// 1-phase generators have 2 conductors
// Assume inverter stays in phase with terminal voltage
- CalcVthev_Dyn(CSub(VTerminal^[1], VTerminal^[2])); // see internal proc above
+ CalcVthev_Dyn(VTerminal^[1] - VTerminal^[2]); // see internal proc above
- ITerminal^[1] := CDiv(CSub(Csub(VTerminal^[1], Vthev), VTerminal^[2]), Zthev);
+ ITerminal^[1] := (VTerminal^[1] - Vthev - VTerminal^[2]) / Zthev;
if CurrentLimited then
if Cabs(Iterminal^[1]) > MaxDynPhaseCurrent then // Limit the current but keep phase angle
ITerminal^[1] := ptocomplex(topolar(MaxDynPhaseCurrent, cang(Iterminal^[1])));
- ITerminal^[2] := Cnegate(ITerminal^[1]);
+ ITerminal^[2] := -ITerminal^[1];
end;
3:
@@ -2139,7 +1555,7 @@ procedure TPVsystemObj.DoDynamicMode;
CalcVthev_Dyn(V012[1]);
// Positive Sequence Contribution to Iterminal
- I012[1] := CDiv(Csub(V012[1], Vthev), Zthev);
+ I012[1] := (V012[1] - Vthev) / Zthev;
if CurrentLimited and (Cabs(I012[1]) > MaxDynPhaseCurrent) then // Limit the pos seq current but keep phase angle
I012[1] := ptocomplex(topolar(MaxDynPhaseCurrent, cang(I012[1])));
@@ -2149,24 +1565,24 @@ procedure TPVsystemObj.DoDynamicMode;
I012[2] := CZERO;
end
else
- I012[2] := Cdiv(V012[2], Zthev); // for inverter
+ I012[2] := V012[2] / Zthev; // for inverter
end;
- {Adjust for generator connection}
+ // Adjust for generator connection
if (Connection = 1) or ForceBalanced then
I012[0] := CZERO
else
- I012[0] := Cdiv(V012[0], Zthev);
+ I012[0] := V012[0] / Zthev;
SymComp2Phase(ITerminal, pComplexArray(@I012)); // Convert back to phase components
// Neutral current
if Connection = 0 then
- ITerminal^[FnConds] := Cnegate(CmulReal(I012[0], 3.0));
+ ITerminal^[FnConds] := -I012[0] * 3.0;
end;
else
- DoSimpleMsg(Format('Dynamics mode is implemented only for 1- or 3-phase Generators. Generator.%s has %d phases.', [name, Fnphases]), 5671);
+ DoSimpleMsg('Dynamics mode is implemented only for 1- or 3-phase Generators. %s has %d phases.', [FullName, Fnphases], 5671);
DSS.SolutionAbort := TRUE;
end;
@@ -2174,27 +1590,23 @@ procedure TPVsystemObj.DoDynamicMode;
IterminalUpdated := TRUE;
- {Add it into inj current array}
+ // Add it into inj current array
for i := 1 to FnConds do
- Caccum(InjCurrent^[i], Cnegate(Iterminal^[i]));
-
-
+ InjCurrent^[i] -= Iterminal^[i];
end;
-// ====================================================================DoHarmonicMode=======================
procedure TPVsystemObj.DoHarmonicMode;
+// Compute Injection Current Only when in harmonics mode
-{Compute Injection Current Only when in harmonics mode}
-
-{Assumes spectrum is a voltage source behind subtransient reactance and YPrim has been built}
-{Vd is the fundamental frequency voltage behind Xd" for phase 1}
-
+// Assumes spectrum is a voltage source behind subtransient reactance and YPrim has been built
+// Vd is the fundamental frequency voltage behind Xd" for phase 1
var
i: Integer;
E: Complex;
PVSystemHarmonic: Double;
-
+ pBuffer: PCBuffer24;
begin
+ pBuffer := @TPVSystem(ParentClass).cBuffer;
ComputeVterminal;
@@ -2202,37 +1614,32 @@ procedure TPVsystemObj.DoHarmonicMode;
begin
PVSystemHarmonic := Frequency / PVSystemFundamental;
if SpectrumObj <> NIL then
- E := CmulReal(SpectrumObj.GetMult(PVSystemHarmonic), VThevHarm) // Get base harmonic magnitude
+ E := SpectrumObj.GetMult(PVSystemHarmonic) * VThevHarm // Get base harmonic magnitude
else
E := CZERO;
RotatePhasorRad(E, PVSystemHarmonic, ThetaHarm); // Time shift by fundamental frequency phase shift
for i := 1 to Fnphases do
begin
- cBuffer[i] := E;
+ pBuffer[i] := E;
if i < Fnphases then
RotatePhasorDeg(E, PVSystemHarmonic, -120.0); // Assume 3-phase PVSystem element
end;
end;
- {Handle Wye Connection}
+ // Handle Wye Connection
if Connection = 0 then
- cbuffer[Fnconds] := Vterminal^[Fnconds]; // assume no neutral injection voltage
-
- {Inj currents = Yprim (E) }
- YPrim.MVMult(InjCurrent, pComplexArray(@cBuffer));
+ pBuffer[Fnconds] := Vterminal^[Fnconds]; // assume no neutral injection voltage
+ // Inj currents = Yprim (E)
+ YPrim.MVMult(InjCurrent, pComplexArray(pBuffer));
end;
-// ===========================================================================================
procedure TPVsystemObj.CalcVTerminalPhase;
-
var
i, j: Integer;
-
begin
-
-{ Establish phase voltages and stick in Vterminal}
+ // Establish phase voltages and stick in Vterminal
case Connection of
0:
@@ -2257,15 +1664,11 @@ procedure TPVsystemObj.CalcVTerminalPhase;
end;
PVSystemSolutionCount := ActiveCircuit.Solution.SolutionCount;
-
end;
-// ============================================CalcPVSystemModelContribution===============================================
procedure TPVsystemObj.CalcPVSystemModelContribution;
-
// Calculates PVSystem element current and adds it properly into the injcurrent array
// routines may also compute ITerminal (ITerminalUpdated flag)
-
begin
IterminalUpdated := FALSE;
with ActiveCircuit, ActiveCircuit.Solution do
@@ -2283,19 +1686,14 @@ procedure TPVsystemObj.CalcPVSystemModelContribution;
DoConstantPQPVsystemObj;
2:
DoConstantZPVsystemObj;
- 3:
- DoUserModel;
else
DoConstantPQPVsystemObj; // for now, until we implement the other models.
end;
- end; {ELSE}
- end; {WITH}
-
- {When this is Done, ITerminal is up to date}
-
+ end;
+ end;
+ // When this is Done, ITerminal is up to date
end;
-// ==========================================CalcInjCurrentArray=================================================
procedure TPVsystemObj.CalcInjCurrentArray;
// Difference between currents in YPrim and total current
begin
@@ -2306,11 +1704,8 @@ procedure TPVsystemObj.CalcInjCurrentArray;
CalcPVSystemModelContribution;
end;
-// =========================================GetTerminalCurrents==================================================
procedure TPVsystemObj.GetTerminalCurrents(Curr: pComplexArray);
-
// Compute total Currents
-
begin
with ActiveCircuit.Solution do
begin
@@ -2324,12 +1719,9 @@ procedure TPVsystemObj.GetTerminalCurrents(Curr: pComplexArray);
if (DebugTrace) then
WriteTraceRecord('TotalCurrent');
-
end;
-// ===========================================INJCURRENTS================================================
function TPVsystemObj.InjCurrents: Integer;
-
begin
with ActiveCircuit.Solution do
begin
@@ -2347,9 +1739,7 @@ function TPVsystemObj.InjCurrents: Integer;
end;
end;
-// ===========================================================================================
procedure TPVsystemObj.ResetRegisters;
-
var
i: Integer;
@@ -2361,36 +1751,28 @@ procedure TPVsystemObj.ResetRegisters;
FirstSampleAfterReset := TRUE; // initialize for trapezoidal integration
end;
-// ===========================================================================================
procedure TPVsystemObj.Integrate(Reg: Integer; const Deriv: Double; const Interval: Double);
-
begin
-
if ActiveCircuit.TrapezoidalIntegration then
begin
- {Trapezoidal Rule Integration}
+ // Trapezoidal Rule Integration
if not FirstSampleAfterReset then
Registers[Reg] := Registers[Reg] + 0.5 * Interval * (Deriv + Derivatives[Reg]);
end
- else {Plain Euler integration}
+ else // Plain Euler integration
Registers[Reg] := Registers[Reg] + Interval * Deriv;
Derivatives[Reg] := Deriv;
-
end;
-// ===========================================================================================
procedure TPVsystemObj.TakeSample;
// Update Energy from metered zone
-
var
S: Complex;
Smag: Double;
HourValue: Double;
-
begin
-
-// Compute energy in PVSystem element branch
+ // Compute energy in PVSystem element branch
if Enabled then
begin
S := cmplx(Get_PresentkW, Get_Presentkvar);
@@ -2402,7 +1784,7 @@ procedure TPVsystemObj.TakeSample;
begin
if ActiveCircuit.PositiveSequence then
begin
- S := CmulReal(S, 3.0);
+ S := S * 3;
Smag := 3.0 * Smag;
end;
Integrate(Reg_kWh, S.re, IntervalHrs); // Accumulate the power
@@ -2416,97 +1798,52 @@ procedure TPVsystemObj.TakeSample;
end;
end;
-// ===========================================================================================
procedure TPVsystemObj.UpdatePVSystem;
-{Update PVSystem levels}
+// Update PVSystem levels
begin
-
- { Do Nothing}
-
+ // Do Nothing
end;
-// ===========================================================================================
function TPVsystemObj.Get_PresentkW: Double;
begin
Result := Pnominalperphase * 0.001 * Fnphases;
end;
-// ===========================================================================================
function TPVsystemObj.Get_PresentIrradiance: Double;
begin
Result := PVSystemVars.FIrradiance * ShapeFactor.re;
end;
-// ===========================================================================================
function TPVsystemObj.Get_PresentkV: Double;
begin
Result := PVSystemVars.kVPVSystemBase;
end;
-// ===========================================================================================
function TPVsystemObj.Get_Presentkvar: Double;
begin
Result := Qnominalperphase * 0.001 * Fnphases;
end;
-// ===========================================================================================
-function TPVsystemObj.Get_VarFollowInverter: Boolean;
-begin
- if FVarFollowInverter then
- Result := TRUE
- else
- Result := FALSE;
-
-end;
-// ===========================================================================================
-procedure TPVsystemObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i, idx: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- idx := PropertyIdxMap[i];
- case idx of
- propUSERDATA:
- FSWriteln(F, '~ ' + PropertyName^[i] + '=(' + PropertyValue[idx] + ')')
- else
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[idx]);
- end;
- end;
-
- FSWriteln(F);
-end;
-
-
-// ============================================================InitHarmonics===============================
procedure TPVsystemObj.InitHarmonics;
-
// This routine makes a thevenin equivalent behis the reactance spec'd in %R and %X
-
var
E, Va: complex;
-
begin
YPrimInvalid := TRUE; // Force rebuild of YPrims
PVSystemFundamental := ActiveCircuit.Solution.Frequency; // Whatever the frequency is when we enter here.
- {Compute reference Thevinen voltage from phase 1 current}
+ // Compute reference Thevinen voltage from phase 1 current
ComputeIterminal; // Get present value of current
with ActiveCircuit.solution do
case Connection of
0:
- begin {wye - neutral is explicit}
- Va := Csub(NodeV^[NodeRef^[1]], NodeV^[NodeRef^[Fnconds]]);
+ begin // wye - neutral is explicit
+ Va := NodeV^[NodeRef^[1]] - NodeV^[NodeRef^[Fnconds]];
end;
1:
- begin {delta -- assume neutral is at zero}
+ begin // delta -- assume neutral is at zero
Va := NodeV^[NodeRef^[1]];
end;
end;
@@ -2514,17 +1851,13 @@ procedure TPVsystemObj.InitHarmonics;
with PVSystemVars do
begin
YEQ := Cinv(Cmplx(RThev, XThev)); // used for current calcs Always L-N
- E := Csub(Va, Cmul(Iterminal^[1], cmplx(Rthev, Xthev)));
+ E := Va - Iterminal^[1] * cmplx(Rthev, Xthev);
Vthevharm := Cabs(E); // establish base mag and angle
ThetaHarm := Cang(E);
end;
-
end;
-
-// ===============================================================InitStateVars============================
procedure TPVsystemObj.InitStateVars;
-
// for going into dynamics mode
var
// VNeut,
@@ -2554,9 +1887,9 @@ procedure TPVsystemObj.InitStateVars;
1:
begin
- V12 := CSub(NodeV^[NodeRef^[1]], NodeV^[NodeRef^[2]]);
+ V12 := NodeV^[NodeRef^[1]] - NodeV^[NodeRef^[2]];
InitialVAngle := Cang(V12);
- Edp := Csub(V12, Cmul(ITerminal^[1], Zthev));
+ Edp := V12 - ITerminal^[1] * Zthev;
VthevmagDyn := Cabs(Edp);
ThetaDyn := Cang(Edp); // initial thev equivalent phase angle
end;
@@ -2571,55 +1904,37 @@ procedure TPVsystemObj.InitStateVars;
Vabc[i] := NodeV^[NodeRef^[i]]; // Wye Voltage
Phase2SymComp(pComplexArray(@Vabc), pComplexArray(@V012));
InitialVAngle := Cang(V012[1]);
- Edp := Csub(V012[1], Cmul(I012[1], Zthev)); // Pos sequence
+ Edp := V012[1] - I012[1] * Zthev; // Pos sequence
VthevmagDyn := Cabs(Edp);
ThetaDyn := Cang(Edp); // initial thev equivalent phase angle
end;
else
- DoSimpleMsg(Format('Dynamics mode is implemented only for 1- or 3-phase Generators. PVSystem.' + name + ' has %d phases.', [Fnphases]), 5673);
+ DoSimpleMsg('Dynamics mode is implemented only for 1- or 3-phase Generators. %s has %d phases.', [FullName, Fnphases], 5673);
DSS.SolutionAbort := TRUE;
end;
LastThevAngle := ThetaDyn;
end;
-
end;
-// ===========================================================================================
procedure TPVsystemObj.IntegrateStates;
-
// dynamics mode integration routine
begin
// Compute Derivatives and Then integrate
-
ComputeIterminal;
-
- if Usermodel.Exists then // Checks for existence and Selects
-
- Usermodel.Integrate
-
- else
-
- with ActiveCircuit.Solution {, StorageVars} do
- begin
-end;
-
end;
-// ===========================================================Get_Variable================================
function TPVsystemObj.Get_Variable(i: Integer): Double;
-{Return variables one at a time}
-
+// Return variables one at a time
var
N, k: Integer;
-
begin
Result := -9999.99; // error return value; no state fars
if i < 1 then
Exit;
-// for now, report kWhstored and mode
+ // for now, report kWhstored and mode
with PVSystemVars do
case i of
1:
@@ -2632,19 +1947,6 @@ function TPVsystemObj.Get_Variable(i: Integer): Double;
Result := EffFactor;
5:
Result := Vreg;
- else
- begin
- if UserModel.Exists then
- begin
- N := UserModel.FNumVars;
- k := (i - NumPVSystemVariables);
- if k <= N then
- begin
- Result := UserModel.FGetVariable(k);
- Exit;
- end;
- end;
- end;
end;
end;
@@ -2654,9 +1956,7 @@ function TPVsystemObj.Get_InverterON: Boolean;
Result := TRUE
else
Result := FALSE;
-
end;
-// ============================================================Get_Varmode===============================
function TPVsystemObj.Get_Varmode: Integer;
begin
@@ -2666,8 +1966,6 @@ function TPVsystemObj.Get_Varmode: Integer;
Result := 1; // 1 for kvar specified
end;
-// ============================================================Get_VWmode===============================
-
function TPVsystemObj.Get_VWmode: Boolean;
begin
@@ -2675,26 +1973,16 @@ function TPVsystemObj.Get_VWmode: Boolean;
Result := TRUE
else
Result := FALSE; // TRUE if volt-watt mode
- // engaged from InvControl (not ExpControl)
+ // engaged from InvControl (not ExpControl)
end;
-
-// ============================================================Get_VWYAxis===============================
-
-
function TPVsystemObj.Get_VWYAxis: Integer;
-
begin
Result := FVWYAxis;
- // engaged from InvControl (not ExpControl)
+ // engaged from InvControl (not ExpControl)
end;
-
-// ============================================================kWOut_Calc===============================
-
-
procedure TPVsystemObj.kWOut_Calc;
-
var
Peff, Pmpp, PTemp: Double;
@@ -2712,8 +2000,6 @@ procedure TPVsystemObj.kWOut_Calc;
kW_Out := Peff;
end;
end;
- // -------------------------------
-
begin
if VWmode then
case FVWYAxis of
@@ -2737,11 +2023,9 @@ procedure TPVsystemObj.kWOut_Calc;
Calc_kWOut;
end;
-// ============================================================Set_Variable===============================
procedure TPVsystemObj.Set_Variable(i: Integer; Value: Double);
var
N, k: Integer;
-
begin
if i < 1 then
Exit; // No variables to set
@@ -2754,24 +2038,9 @@ procedure TPVsystemObj.Set_Variable(i: Integer; Value: Double);
4: ; // Setting this has no effect Read only
5:
Vreg := Value; // the InvControl or ExpControl will do this
- else
- begin
- if UserModel.Exists then
- begin
- N := UserModel.FNumVars;
- k := (i - NumPVSystemVariables);
- if k <= N then
- begin
- UserModel.FSetVariable(k, Value);
- Exit;
- end;
- end;
- end;
end;
-
end;
-
procedure TPVsystemObj.Set_Varmode(const Value: Integer);
begin
case Value of
@@ -2782,7 +2051,6 @@ procedure TPVsystemObj.Set_Varmode(const Value: Integer);
end;
kvarSpecified := not PFSpecified;
-
end;
procedure TPVsystemObj.Set_VWmode(const Value: Boolean);
@@ -2795,29 +2063,19 @@ procedure TPVsystemObj.Set_VWYAxis(const Value: Integer);
FVWYAxis := Value;
end;
-// ===========================================================================================
procedure TPVsystemObj.GetAllVariables(States: pDoubleArray);
-
var
- i{, N}: Integer;
+ i: Integer;
begin
for i := 1 to NumPVSystemVariables do
States^[i] := Variable[i];
-
- if UserModel.Exists then
- UserModel.FGetAllVars(pDoubleArray(@States^[NumPVSystemVariables + 1]));
-
end;
-// ===========================================================================================
function TPVsystemObj.NumVariables: Integer;
begin
Result := NumPVSystemVariables;
- if UserModel.Exists then
- Result := Result + UserModel.FNumVars;
end;
-// ===========================================================================================
function TPVsystemObj.VariableName(i: Integer): String;
const
@@ -2843,105 +2101,87 @@ function TPVsystemObj.VariableName(i: Integer): String;
Result := 'Efficiency';
5:
Result := 'Vreg';
- else
- begin
- if UserModel.Exists then
- begin
- pName := PAnsiChar(@Buff);
- n := UserModel.FNumVars;
- i2 := i - NumPVSystemVariables;
- if (i2 <= n) then
- begin
- UserModel.FGetVarName(i2, pName, BuffSize);
- Result := String(pName);
- Exit;
- end;
- end;
end;
- end;
-
end;
-// ===========================================================================================
-procedure TPVsystemObj.MakePosSequence;
-
+procedure TPVsystemObj.MakePosSequence();
var
- S: String;
- V: Double;
-
+ newkVA, newPF, V: Double;
+ oldPhases, changes: Integer;
begin
-
- S := 'Phases=1 conn=wye';
-
with PVSystemVars do
begin
- // Make sure voltage is line-neutral
+ BeginEdit(True);
+ // Make sure voltage is line-neutral
if (Fnphases > 1) or (connection <> 0) then
V := kVPVSystemBase / SQRT3
else
V := kVPVSystemBase;
- S := S + Format(' kV=%-.5g', [V]);
-
- if (Fnphases > 1) then
- S := S + Format(' kva=%-.5g PF=%-.5g', [FkVArating / Fnphases, PFnominal]);
-
- Parser.CmdString := S;
- Edit;
+ oldPhases := FnPhases;
+ changes := 3;
+ if oldPhases > 1 then
+ begin
+ newkVA := kVArating / Fnphases;
+ newPF := PFNominal;
+ changes := changes + 2;
+ end;
+ SetInteger(ord(TProp.Phases), 1);
+ SetInteger(ord(TProp.conn), 0);
+ SetDouble(ord(TProp.kV), V);
+ if oldPhases > 1 then
+ begin
+ SetDouble(ord(TProp.KVA), newkVA);
+ SetDouble(ord(TProp.PF), newPF);
+ end;
+ EndEdit(changes);
end;
inherited; // write out other properties
end;
-// ===========================================================================================
-procedure TPVsystemObj.Set_ConductorClosed(Index: Integer;
- Value: Boolean);
+procedure TPVsystemObj.Set_ConductorClosed(Index: Integer; Value: Boolean);
begin
inherited;
- // Just turn PVSystem element on or off;
+ // Just turn PVSystem element on or off;
if Value then
PVsystemObjSwitchOpen := FALSE
else
PVsystemObjSwitchOpen := TRUE;
-
end;
-// ===========================================================================================
+
procedure TPVsystemObj.Set_Maxkvar(const Value: Double);
begin
PVSystemVars.Fkvarlimit := Value;
- PropertyValue[propkvarLimit] := Format('%-g', [PVSystemVars.Fkvarlimit]);
+ SetAsNextSeq(ord(TProp.kvarLimit));
end;
-// ===========================================================================================
+
procedure TPVsystemObj.Set_kVARating(const Value: Double);
begin
PVSystemVars.FkVARating := Value;
- PropertyValue[propKVA] := Format('%-g', [PVSystemVars.FkVArating]);
+ SetAsNextSeq(ord(TProp.kVA));
end;
-// ===========================================================================================
+
procedure TPVsystemObj.Set_Pmpp(const Value: Double);
begin
PVSystemVars.FPmpp := Value;
- PropertyValue[propPmpp] := Format('%-g', [PVSystemVars.FkVArating]);
+ SetAsNextSeq(ord(TProp.Pmpp));
end;
-// ===========================================================================================
procedure TPVsystemObj.Set_PowerFactor(const Value: Double);
begin
PFnominal := Value;
PFSpecified := TRUE;
end;
-// ===========================================================================================
procedure TPVsystemObj.Set_PresentIrradiance(const Value: Double);
begin
-
PVSystemVars.FIrradiance := Value;
- PropertyValue[propKVA] := Format('%-g', [PVSystemVars.FPmpp]);
+ SetAsNextSeq(ord(TProp.kVA));
end;
-// ===========================================================================================
procedure TPVsystemObj.Set_PresentkV(const Value: Double);
begin
with PVSystemVars do
@@ -2956,46 +2196,29 @@ procedure TPVsystemObj.Set_PresentkV(const Value: Double);
end;
end;
-// ===========================================================================================
-procedure TPVsystemObj.Set_VarFollowInverter(const Value: Boolean);
-begin
- FVarFollowInverter := Value;
-end;
-
-// ===========================================================================================
procedure TPVsystemObj.Set_InverterON(const Value: Boolean);
begin
FInverterON := Value;
end;
-// ===========================================================================================
procedure TPVsystemObj.Set_PresentkW(const Value: Double);
begin
kWRequested := Value;
end;
-// ===========================================================================================
procedure TPVsystemObj.Set_Presentkvar(const Value: Double);
-
begin
kvarRequested := Value;
end;
-{ =========================================================================================== }
procedure TPVsystemObj.Set_puPmpp(const Value: Double);
begin
PVSystemVars.FpuPmpp := Value;
end;
-{ =========================================================================================== }
procedure TPVsystemObj.SetDragHandRegister(Reg: Integer; const Value: Double);
begin
if (Value > Registers[reg]) then
Registers[Reg] := Value;
end;
-
-initialization
-
- CDOUBLEONE := Cmplx(1.0, 1.0);
-
-end.
+end.
\ No newline at end of file
diff --git a/src/PCElements/PVsystem2.pas b/src/PCElements/PVsystem2.pas
index cd813441d..55c4e2565 100644
--- a/src/PCElements/PVsystem2.pas
+++ b/src/PCElements/PVsystem2.pas
@@ -7,23 +7,16 @@
----------------------------------------------------------
}
-{ Change Log
+// To Do:
+// Make connection to User model
+// Yprim for various modes
+// Define state vars and dynamics mode behavior
+// Complete Harmonics mode algorithm (generator mode is implemented)
- 1/28/2011 Created from Storage Model
-
-
- To Do:
- Make connection to User model
- Yprim for various modes
- Define state vars and dynamics mode behavior
- Complete Harmonics mode algorithm (generator mode is implemented)
-}
-{
- The PVsystem element is essentially a generator that consists of a PV panel and an inverter.
-
- The PVsystem element can also produce or absorb vars within the kVA rating of the inverter.
- // WGS: Updated 9/24/2015 to allow for simultaneous modes and additional functionality in the InvControl.
-}
+// The PVsystem element is essentially a generator that consists of a PV panel and an inverter.
+//
+// The PVsystem element can also produce or absorb vars within the kVA rating of the inverter.
+// // WGS: Updated 9/24/2015 to allow for simultaneous modes and additional functionality in the InvControl.
// The PVsystem element is assumed balanced over the no. of phases defined
@@ -37,7 +30,7 @@ interface
PCClass,
PCElement,
ucmatrix,
- ucomplex,
+ UComplex, DSSUcomplex,
LoadShape,
TempShape,
XYCurve,
@@ -52,18 +45,64 @@ interface
VARMODEKVAR = 1;
type
- {Struct to pass basic data to user-written DLLs}
+{$SCOPEDENUMS ON}
+ TPVSystem2Prop = (
+ INVALID = 0,
+ phases = 1,
+ bus1 = 2,
+ kv = 3, // propKV
+ irradiance = 4, // propIrradiance
+ Pmpp = 5, // propPmpp
+ pctPmpp = 6, // proppctPmpp
+ Temperature = 7, // propTemp
+ pf = 8, // propPF
+ conn = 9, // propCONNECTION
+ kvar = 10, // propKVAR
+ kVA = 11, // propKVA
+ pctCutin = 12, // propCutin
+ pctCutout = 13, // propCutout
+ EffCurve = 14, // propInvEffCurve
+ P__TCurve = 15, // propP_T_Curve
+ pctR = 16, // propPCTR
+ pctX = 17, // propPCTX
+ model = 18, // propMODEL
+ Vminpu = 19, // propVMINPU
+ Vmaxpu = 20, // propVMAXPU
+ Balanced = 21, // propBalanced
+ LimitCurrent = 22, // propLimited
+ yearly = 23, // propYEARLY
+ daily = 24, // propDAILY
+ duty = 25, // propDUTY
+ Tyearly = 26, // propTYEARLY
+ Tdaily = 27, // propTDAILY
+ Tduty = 28, // propTDUTY
+ cls = 29, // propCLASS
+ UserModel = 30, // propUSERMODEL
+ UserData = 31, // propUSERDATA
+ debugtrace = 32, // propDEBUGTRACE
+ VarFollowInverter = 33, // propVarFollowInverter
+ DutyStart = 34, // propDutyStart
+ WattPriority = 35, // propPpriority
+ PFPriority = 36, // propPFpriority
+ pctPminNoVars = 37, // propPminNoVars
+ pctPminkvarMax = 38, // propPminkvarLimit
+ kvarMax = 39, // propkvarLimit
+ kvarMaxAbs = 40 // propkvarLimitneg
+ );
+{$SCOPEDENUMS OFF}
+
+ // Struct to pass basic data to user-written DLLs
TPVSystem2Vars = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}packed{$ENDIF} record
FkVArating: Double;
kVPVSystemBase: Double;
RThev: Double;
XThev: Double;
- Vthevharm: Double; {Thevinen equivalent voltage mag for Harmonic model}
- VthevmagDyn: Double; {Thevinen equivalent voltage mag reference for Dynamics model}
- Thetaharm: Double; {Thevinen equivalent angle reference for Harmonic model}
- ThetaDyn: Double; {Thevinen equivalent angle reference for Dynamics model}
- InitialVAngle: Double; {initial terminal voltage angle when entering dynamics mode}
+ Vthevharm: Double; // Thevinen equivalent voltage mag for Harmonic model
+ VthevmagDyn: Double; // Thevinen equivalent voltage mag reference for Dynamics model
+ Thetaharm: Double; // Thevinen equivalent angle reference for Harmonic model
+ ThetaDyn: Double; // Thevinen equivalent angle reference for Dynamics model
+ InitialVAngle: Double; // initial terminal voltage angle when entering dynamics mode
EffFactor: Double;
TempFactor: Double;
PanelkW: Double; //computed
@@ -86,31 +125,27 @@ interface
WVOperation: Double;
// kW_out_desired :Double;
- {32-bit integers}
- NumPhases: Integer; {Number of phases}
- NumConductors: Integer; {Total Number of conductors (wye-connected will have 4)}
+ // 32-bit integers
+ NumPhases: Integer; // Number of phases
+ NumConductors: Integer; // Total Number of conductors (wye-connected will have 4)
Conn: Integer; // 0 = wye; 1 = Delta
- P_Priority: Boolean; // default False // added 10/30/2018
- PF_Priority: Boolean; // default False // added 1/29/2019
+ P_Priority: LongBool; // default False // added 10/30/2018
+ PF_Priority: LongBool; // default False // added 1/29/2019
end;
TPVSystem2 = class(TPCClass)
- PRIVATE
- procedure InterpretConnection(const S: String);
- procedure SetNcondsForConnection;
-
PROTECTED
- procedure DefineProperties;
- function MakeLike(const OtherPVsystemObjName: String): Integer; OVERRIDE;
+ cBuffer: TCBuffer24; // Temp buffer for calcs 24-phase PVSystem element?
+ procedure DefineProperties; override;
PUBLIC
RegisterNames: array[1..NumPVSystem2Registers] of String;
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit(): Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
procedure ResetRegistersAll;
procedure SampleAll();
@@ -129,7 +164,7 @@ TPVsystem2Obj = class(TPCElement)
DebugTrace: Boolean;
PVSystemSolutionCount: Integer;
- PVSystemFundamental: Double; {Thevinen equivalent voltage mag and angle reference for Harmonic model}
+ PVSystemFundamental: Double; // Thevinen equivalent voltage mag and angle reference for Harmonic model
PVsystemObjSwitchOpen: Boolean;
FirstSampleAfterReset: Boolean;
@@ -146,9 +181,6 @@ TPVsystem2Obj = class(TPCElement)
kWRequested: Double;
FvarMode: Integer;
- FpctCutIn: Double;
- FpctCutOut: Double;
- FVarFollowInverter: Boolean;
CutInkW: Double;
CutOutkW: Double;
FInverterON: Boolean;
@@ -176,13 +208,13 @@ TPVsystem2Obj = class(TPCElement)
TShapeValue: Double;
TraceFile: TFileStream;
- UserModel: TPVsystemUserModel; {User-Written Models}
+ UserModel: TPVsystemUserModel; // User-Written Models
+
+ UserModelNameStr, UserModelEditStr: String;
varBase: Double; // Base vars per phase
VBaseMax: Double;
VBaseMin: Double;
- Vmaxpu: Double;
- Vminpu: Double;
YPrimOpenCond: TCmatrix;
FVWMode: Boolean; //boolean indicating if under volt-watt control mode from InvControl (not ExpControl)
@@ -205,7 +237,6 @@ TPVsystem2Obj = class(TPCElement)
procedure CalcPVSystemModelContribution(); // This is where the power gets computed
procedure CalcInjCurrentArray();
- (*PROCEDURE CalcVterminal;*)
procedure CalcVTerminalPhase();
procedure CalcYPrimMatrix(Ymatrix: TcMatrix);
@@ -222,14 +253,12 @@ TPVsystem2Obj = class(TPCElement)
procedure WriteTraceRecord(const s: String);
- // PROCEDURE SetKWandKvarOut;
procedure UpdatePVSystem; // Update PVSystem elements based on present kW and IntervalHrs variable
function Get_PresentkW: Double;
function Get_Presentkvar: Double;
function Get_PresentIrradiance: Double;
- procedure Set_PresentkV(const Value: Double);
procedure Set_PowerFactor(const Value: Double);
procedure Set_pf_wp_nominal(const Value: Double);
@@ -245,30 +274,27 @@ TPVsystem2Obj = class(TPCElement)
PVSystemVars: TPVSystem2Vars;
VBase: Double; // Base volts suitable for computing currents
+ Vmaxpu: Double;
+ Vminpu: Double;
+
CurrentkvarLimit: Double;
CurrentkvarLimitNeg: Double;
+ FpctCutIn: Double;
+ FpctCutOut: Double;
- AVRMode: Boolean;
+ AVRMode: Boolean; //boolean indicating whether under AVR mode from ExpControl (or InvControl, but that does not seem to be implemented yet)
- Connection: Integer; {0 = line-neutral; 1=Delta}
- DailyShape: String; // Daily (24 HR) PVSystem element irradiance shape
+ Connection: Integer; // 0 = line-neutral; 1=Delta
DailyShapeObj: TLoadShapeObj; // Daily PVSystem element irradianceShape for this load
- DutyShape: String; // Duty cycle irradiance shape for changes typically less than one hour
DutyShapeObj: TLoadShapeObj; // irradiance Shape for this PVSystem element
DutyStart: Double; // starting time offset into the DutyShape [hrs] for this PVsystem
- YearlyShape: String; //
YearlyShapeObj: TLoadShapeObj; // Yearly irradiance Shape for this PVSystem element
- DailyTShape: String;
DailyTShapeObj: TTShapeObj;
- DutyTShape: String;
DutyTShapeObj: TTShapeObj;
- YearlyTShape: String;
YearlyTShapeObj: TTShapeObj;
- InverterCurve: String;
InverterCurveObj: TXYCurveObj;
- Power_TempCurve: String;
Power_TempCurveObj: TXYCurveObj;
kvarLimitSet: Boolean;
@@ -281,8 +307,12 @@ TPVsystem2Obj = class(TPCElement)
Registers: array[1..NumPVSystem2Registers] of Double;
Derivatives: array[1..NumPVSystem2Registers] of Double;
+ VarFollowInverter: LongBool;
+
constructor Create(ParClass: TDSSClass; const SourceName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure Set_ConductorClosed(Index: Integer; Value: Boolean); OVERRIDE;
procedure RecalcElementData(); OVERRIDE;
@@ -309,20 +339,17 @@ TPVsystem2Obj = class(TPCElement)
procedure InitStateVars(); OVERRIDE;
procedure IntegrateStates(); OVERRIDE;
- // Support for Harmonics Mode
+ // Support for Harmonics Mode
procedure InitHarmonics(); OVERRIDE;
procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
+ function UsingCIMDynamics(): Boolean;
- {Porperties}
property PresentIrradiance: Double READ Get_PresentIrradiance WRITE PVSystemVars.FIrradiance;
property PresentkW: Double READ Get_PresentkW WRITE kWRequested;
property Presentkvar: Double READ Get_Presentkvar WRITE kvarRequested;
- property PresentkV: Double READ PVSystemVars.kVPVSystemBase WRITE Set_PresentkV;
+ property PresentkV: Double READ PVSystemVars.kVPVSystemBase;
property PowerFactor: Double READ PFnominal WRITE Set_PowerFactor;
property kVARating: Double READ PVSystemVars.FkVARating WRITE Set_kVARating;
property Pmpp: Double READ PVSystemVars.FPmpp WRITE Set_pmpp;
@@ -334,7 +361,6 @@ TPVsystem2Obj = class(TPCElement)
property WVmode: Boolean READ FWVmode WRITE FWVmode;
property DRCmode: Boolean READ FDRCmode WRITE FDRCmode;
property InverterON: Boolean READ FInverterON WRITE FInverterON;
- property VarFollowInverter: Boolean READ FVarFollowInverter WRITE FVarFollowInverter;
property kvarLimit: Double READ PVSystemVars.Fkvarlimit WRITE Set_Maxkvar;
property kvarLimitneg: Double READ PVSystemVars.Fkvarlimitneg WRITE Set_Maxkvarneg;
property MinModelVoltagePU: Double READ VminPu;
@@ -345,7 +371,7 @@ TPVsystem2Obj = class(TPCElement)
implementation
uses
- ParserDel,
+ BufStream,
Circuit,
Sysutils,
Command,
@@ -357,70 +383,20 @@ implementation
DSSHelper,
DSSObjectHelper;
+type
+ TObj = TPVsystem2Obj;
+ TProp = TPVSystem2Prop;
const
-
-// ===========================================================================================
-{
- To add a property,
- 1) add a property constant to this list
- 2) add a handler to the CASE statement in the Edit FUNCTION
- 3) add a statement(s) to InitPropertyValues FUNCTION to initialize the string value
- 4) add any special handlers to DumpProperties and GetPropertyValue, If needed
-}
-// ===========================================================================================
-
- propKV = 3;
- propIrradiance = 4;
- propPF = 5;
- propMODEL = 6;
- propYEARLY = 7;
- propDAILY = 8;
- propDUTY = 9;
- propTYEARLY = 10;
- propTDAILY = 11;
- propTDUTY = 12;
- propCONNECTION = 13;
- propKVAR = 14;
- propPCTR = 15;
- propPCTX = 16;
- propCLASS = 17;
- propInvEffCurve = 18;
- propTemp = 19;
- propPmpp = 20;
- propP_T_Curve = 21;
- propCutin = 22;
- propCutout = 23;
- propVMINPU = 24;
- propVMAXPU = 25;
- propKVA = 26;
- propUSERMODEL = 27;
- propUSERDATA = 28;
- propDEBUGTRACE = 29;
- proppctPmpp = 30;
- propBalanced = 31;
- propLimited = 32;
- propVarFollowInverter = 33;
- propkvarLimit = 34;
- propDutyStart = 35;
- propPpriority = 36;
- propPFpriority = 37;
- propPminNoVars = 38;
- propPminkvarLimit = 39;
- propkvarLimitneg = 40;
-
- NumPropsThisClass = 40; // Make this agree with the last property constant
-
+ NumPropsThisClass = Ord(High(TProp));
var
- cBuffer: array[1..24] of Complex; // Temp buffer for calcs 24-phase PVSystem element?
- CDOUBLEONE: Complex;
+ PropInfo: Pointer = NIL;
-constructor TPVsystem2.Create(dssContext: TDSSContext); // Creates superstructure for all PVSystem elements
+constructor TPVsystem2.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'PVSystem';
- DSSClassType := DSSClassType + PVSYSTEM_ELEMENT; // In both PCelement and PVSystem element list
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- ActiveElement := 0;
+ inherited Create(dssContext, PVSYSTEM_ELEMENT, 'PVSystem');
// Set Register names
RegisterNames[1] := 'kWh';
@@ -429,208 +405,148 @@ constructor TPVsystem2.Create(dssContext: TDSSContext); // Creates superstructu
RegisterNames[4] := 'Max kVA';
RegisterNames[5] := 'Hours';
RegisterNames[6] := 'Price($)';
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
end;
destructor TPVsystem2.Destroy;
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
+function Getkvar(obj: TObj): Double;
+begin
+ Result := obj.kvar_out;
+end;
+
procedure TPVsystem2.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays; {see DSSClass}
-
- // Define Property names
- {
- Using the AddProperty FUNCTION, you can list the properties here in the order you want
- them to appear when properties are accessed sequentially without tags. Syntax:
-
- AddProperty( , , );
-
- }
- AddProperty('phases', 1,
- 'Number of Phases, this PVSystem element. Power is evenly divided among phases.');
- AddProperty('bus1', 2,
- 'Bus to which the PVSystem element is connected. May include specific node specification.');
- AddProperty('kv', propKV,
- 'Nominal rated (1.0 per unit) voltage, kV, for PVSystem element. For 2- and 3-phase PVSystem elements, specify phase-phase kV. ' +
- 'Otherwise, specify actual kV across each branch of the PVSystem element. ' +
- 'If 1-phase wye (star or LN), specify phase-neutral kV. ' +
- 'If 1-phase delta or phase-phase connected, specify phase-phase kV.'); // line-neutral voltage// base voltage
- AddProperty('irradiance', propIrradiance,
- 'Get/set the present irradiance value in kW/sq-m. Used as base value for shape multipliers. ' +
- 'Generally entered as peak value for the time period of interest and the yearly, daily, and duty load shape ' +
- 'objects are defined as per unit multipliers (just like Loads/Generators).');
- AddProperty('Pmpp', propPmpp,
- 'Get/set the rated max power of the PV array for 1.0 kW/sq-m irradiance and a user-selected array temperature. ' +
- 'The P-TCurve should be defined relative to the selected array temperature.');
- AddProperty('%Pmpp', proppctPmpp,
- 'Upper limit on active power as a percentage of Pmpp.');
- AddProperty('Temperature', propTemp,
- 'Get/set the present Temperature. Used as fixed value corresponding to PTCurve property. ' +
- 'A multiplier is obtained from the Pmpp-Temp curve and applied to the nominal Pmpp from the irradiance ' +
- 'to determine the net array output.');
- AddProperty('pf', propPF,
- 'Nominally, the power factor for the output power. Default is 1.0. ' +
- 'Setting this property will cause the inverter to operate in constant power factor mode.' +
- 'Enter negative when kW and kvar have opposite signs.' + CRLF +
- 'A positive power factor signifies that the PVSystem element produces vars ' + CRLF +
- 'as is typical for a generator. ');
- AddProperty('conn', propCONNECTION,
- '={wye|LN|delta|LL}. Default is wye.');
- AddProperty('kvar', propKVAR,
- 'Get/set the present kvar value. Setting this property forces the inverter to operate in constant kvar mode.');
- AddProperty('kVA', propKVA,
- 'kVA rating of inverter. Used as the base for Dynamics mode and Harmonics mode values.');
- AddProperty('%Cutin', propCutin,
- '% cut-in power -- % of kVA rating of inverter. ' +
- 'When the inverter is OFF, the power from the array must be greater than this for the inverter to turn on.');
- AddProperty('%Cutout', propCutout,
- '% cut-out power -- % of kVA rating of inverter. ' +
- 'When the inverter is ON, the inverter turns OFF when the power from the array drops below this value.');
-
- AddProperty('EffCurve', propInvEffCurve,
- 'An XYCurve object, previously defined, that describes the PER UNIT efficiency vs PER UNIT of rated kVA for the inverter. ' +
- 'Inverter output power is discounted by the multiplier obtained from this curve.');
-
- AddProperty('P-TCurve', propP_T_Curve,
- 'An XYCurve object, previously defined, that describes the PV array PER UNIT Pmpp vs Temperature curve. ' +
- 'Temperature units must agree with the Temperature property and the Temperature shapes used for simulations. ' +
- 'The Pmpp values are specified in per unit of the Pmpp value for 1 kW/sq-m irradiance. ' +
- 'The value for the temperature at which Pmpp is defined should be 1.0. ' +
- 'The net array power is determined by the irradiance * Pmpp * f(Temperature)');
- AddProperty('%R', propPCTR,
- 'Equivalent percent internal resistance, ohms. Default is 50%. Placed in series with internal voltage source' +
- ' for harmonics and dynamics modes. (Limits fault current to about 2 pu if not current limited -- see LimitCurrent) ');
- AddProperty('%X', propPCTX,
- 'Equivalent percent internal reactance, ohms. Default is 0%. Placed in series with internal voltage source' +
- ' for harmonics and dynamics modes. ');
- AddProperty('model', propMODEL,
- 'Integer code (default=1) for the model to use for power output variation with voltage. ' +
- 'Valid values are:' + CRLF + CRLF +
- '1:PVSystem element injects a CONSTANT kW at specified power factor.' + CRLF +
- '2:PVSystem element is modeled as a CONSTANT ADMITTANCE.' + CRLF +
- '3:Compute load injection from User-written Model.');
-
- AddProperty('Vminpu', propVMINPU,
- 'Default = 0.90. Minimum per unit voltage for which the Model is assumed to apply. ' +
- 'Below this value, the load model reverts to a constant impedance model except for Dynamics model. ' +
- 'In Dynamics mode, the current magnitude is limited to the value the power flow would compute for this voltage.');
- AddProperty('Vmaxpu', propVMAXPU,
- 'Default = 1.10. Maximum per unit voltage for which the Model is assumed to apply. ' +
- 'Above this value, the load model reverts to a constant impedance model.');
- AddProperty('Balanced', propBalanced,
- '{Yes | No*} Default is No. Force balanced current only for 3-phase PVSystems. Forces zero- and negative-sequence to zero. ');
- AddProperty('LimitCurrent', propLimited,
- 'Limits current magnitude to Vminpu value for both 1-phase and 3-phase PVSystems similar to Generator Model 7. For 3-phase, ' +
- 'limits the positive-sequence current but not the negative-sequence.');
- AddProperty('yearly', propYEARLY,
- 'Dispatch shape to use for yearly simulations. Must be previously defined ' +
- 'as a Loadshape object. If this is not specified, the Daily dispatch shape, if any, is repeated ' +
- 'during Yearly solution modes. In the default dispatch mode, ' +
- 'the PVSystem element uses this loadshape to trigger State changes.');
- AddProperty('daily', propDAILY,
- 'Dispatch shape to use for daily simulations. Must be previously defined ' +
- 'as a Loadshape object of 24 hrs, typically. In the default dispatch mode, ' +
- 'the PVSystem element uses this loadshape to trigger State changes.'); // daily dispatch (hourly)
- AddProperty('duty', propDUTY,
- 'Load shape to use for duty cycle dispatch simulations such as for solar ramp rate studies. ' +
- 'Must be previously defined as a Loadshape object. ' +
- 'Typically would have time intervals of 1-5 seconds. ' +
- 'Designate the number of points to solve using the Set Number=xxxx command. ' +
- 'If there are fewer points in the actual shape, the shape is assumed to repeat.'); // as for wind generation
-
- AddProperty('Tyearly', propTYEARLY,
- 'Temperature shape to use for yearly simulations. Must be previously defined ' +
- 'as a TShape object. If this is not specified, the Daily dispatch shape, if any, is repeated ' +
- 'during Yearly solution modes. ' +
- 'The PVSystem element uses this TShape to determine the Pmpp from the Pmpp vs T curve. ' +
- 'Units must agree with the Pmpp vs T curve.');
- AddProperty('Tdaily', propTDAILY,
- 'Temperature shape to use for daily simulations. Must be previously defined ' +
- 'as a TShape object of 24 hrs, typically. ' +
- 'The PVSystem element uses this TShape to determine the Pmpp from the Pmpp vs T curve. ' +
- 'Units must agree with the Pmpp vs T curve.'); // daily dispatch (hourly)
- AddProperty('Tduty', propTDUTY,
- 'Temperature shape to use for duty cycle dispatch simulations such as for solar ramp rate studies. ' +
- 'Must be previously defined as a TShape object. ' +
- 'Typically would have time intervals of 1-5 seconds. ' +
- 'Designate the number of points to solve using the Set Number=xxxx command. ' +
- 'If there are fewer points in the actual shape, the shape is assumed to repeat. ' +
- 'The PVSystem model uses this TShape to determine the Pmpp from the Pmpp vs T curve. ' +
- 'Units must agree with the Pmpp vs T curve.'); // Cloud transient simulation
- AddProperty('class', propCLASS,
- 'An arbitrary integer number representing the class of PVSystem element so that PVSystem values may ' +
- 'be segregated by class.'); // integer
-
- AddProperty('UserModel', propUSERMODEL,
- 'Name of DLL containing user-written model, which computes the terminal currents for Dynamics studies, ' +
- 'overriding the default model. Set to "none" to negate previous setting.');
- AddProperty('UserData', propUSERDATA,
- 'String (in quotes or parentheses) that gets passed to user-written model for defining the data required for that model.');
- AddProperty('debugtrace', propDEBUGTRACE,
- '{Yes | No } Default is no. Turn this on to capture the progress of the PVSystem model ' +
- 'for each iteration. Creates a separate file for each PVSystem element named "PVSystem_name.CSV".');
- AddProperty('VarFollowInverter', propVarFollowInverter,
- 'Boolean variable (Yes|No) or (True|False). Defaults to False which indicates that the reactive power generation/absorption does not respect the inverter status.' +
- 'When set to True, the PVSystem reactive power generation/absorption will cease when the inverter status is off, due to panel kW dropping below %Cutout. The reactive power ' +
- 'generation/absorption will begin again when the panel kW is above %Cutin. When set to False, the PVSystem will generate/absorb reactive power regardless of the status of the inverter.');
-
-
- AddProperty('DutyStart', propDutyStart,
- 'Starting time offset [hours] into the duty cycle shape for this PVSystem, defaults to 0');
- AddProperty('WattPriority', propPPriority,
- '{Yes/No*/True/False} Set inverter to watt priority instead of the default var priority');
- AddProperty('PFPriority', propPFPriority,
- '{Yes/No*/True/False} Set inverter to operate with PF priority when in constant PF mode. If "Yes", value assigned to "WattPriority"' +
- ' is neglected. If controlled by an InvControl with either Volt-Var or DRC or both functions activated, PF priority is neglected and "WattPriority" is considered. Default = No.');
-
- AddProperty('%PminNoVars', propPminNoVars,
- 'Minimum active power as percentage of Pmpp under which there is no vars production/absorption.');
-
- AddProperty('%PminkvarMax', propPminkvarLimit,
- 'Minimum active power as percentage of Pmpp that allows the inverter to produce/absorb reactive power up to its kvarMax or kvarMaxAbs.');
-
-
- AddProperty('kvarMax', propkvarLimit,
- 'Indicates the maximum reactive power GENERATION (un-signed numerical variable in kvar) for the inverter (as an un-signed value). Defaults to kVA rating of the inverter.');
-
- AddProperty('kvarMaxAbs', propkvarLimitneg,
- 'Indicates the maximum reactive power ABSORPTION (un-signed numerical variable in kvar) for the inverter (as an un-signed value). Defaults to kVA rating of the inverter.');
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // strings
+ PropertyType[ord(TProp.UserModel)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.UserModel)] := ptruint(@obj.UserModelNameStr);
+ PropertyType[ord(TProp.UserData)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.UserData)] := ptruint(@obj.UserModelEditStr);
+
+ // enum properties
+ PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.conn)] := ptruint(@obj.Connection);
+ PropertyOffset2[ord(TProp.conn)] := PtrInt(DSS.ConnectionEnum);
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+
+ // object properties
+ PropertyType[ord(TProp.yearly)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.daily)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.duty)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.Tyearly)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.Tdaily)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.Tduty)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.EffCurve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.P__TCurve)] := TPropertyType.DSSObjectReferenceProperty;
+
+ PropertyOffset[ord(TProp.yearly)] := ptruint(@obj.YearlyShapeObj);
+ PropertyOffset[ord(TProp.daily)] := ptruint(@obj.DailyShapeObj);
+ PropertyOffset[ord(TProp.duty)] := ptruint(@obj.DutyShapeObj);
+ PropertyOffset[ord(TProp.Tyearly)] := ptruint(@obj.YearlyTShapeObj);
+ PropertyOffset[ord(TProp.Tdaily)] := ptruint(@obj.DailyTShapeObj);
+ PropertyOffset[ord(TProp.Tduty)] := ptruint(@obj.DutyTShapeObj);
+ PropertyOffset[ord(TProp.EffCurve)] := ptruint(@obj.InverterCurveObj);
+ PropertyOffset[ord(TProp.P__TCurve)] := ptruint(@obj.Power_TempCurveObj);
+
+ PropertyOffset2[ord(TProp.yearly)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.daily)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.duty)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.Tyearly)] := ptruint(DSS.TShapeClass);
+ PropertyOffset2[ord(TProp.Tdaily)] := ptruint(DSS.TShapeClass);
+ PropertyOffset2[ord(TProp.Tduty)] := ptruint(DSS.TShapeClass);
+ PropertyOffset2[ord(TProp.EffCurve)] := ptruint(DSS.XYCurveClass);
+ PropertyOffset2[ord(TProp.P__TCurve)] := ptruint(DSS.XYCurveClass);
+
+ // boolean properties
+ PropertyType[ord(TProp.debugtrace)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.Balanced)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.LimitCurrent)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.VarFollowInverter)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.WattPriority)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.PFPriority)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.debugtrace)] := ptruint(@obj.DebugTrace);
+ PropertyOffset[ord(TProp.Balanced)] := ptruint(@obj.ForceBalanced);
+ PropertyOffset[ord(TProp.LimitCurrent)] := ptruint(@obj.CurrentLimited);
+ PropertyOffset[ord(TProp.VarFollowInverter)] := ptruint(@obj.VarFollowInverter);
+ PropertyOffset[ord(TProp.WattPriority)] := ptruint(@obj.PVSystemVars.P_priority);
+ PropertyOffset[ord(TProp.PFPriority)] := ptruint(@obj.PVSystemVars.PF_priority);
+
+ // integer properties
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ PropertyType[ord(TProp.cls)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.cls)] := ptruint(@obj.FClass);
+ PropertyType[ord(TProp.model)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.model)] := ptruint(@obj.VoltageModel); // TODO: enum?
+
+ // double properties
+ PropertyOffset[ord(TProp.irradiance)] := ptruint(@obj.PVSystemVars.FIrradiance);
+ PropertyOffset[ord(TProp.pf)] := ptruint(@obj.PFnominal);
+ PropertyOffset[ord(TProp.pctR)] := ptruint(@obj.pctR);
+ PropertyOffset[ord(TProp.pctX)] := ptruint(@obj.pctX);
+ PropertyOffset[ord(TProp.Temperature)] := ptruint(@obj.PVSystemVars.FTemperature);
+ PropertyOffset[ord(TProp.Pmpp)] := ptruint(@obj.PVSystemVars.FPmpp);
+ PropertyOffset[ord(TProp.pctCutin)] := ptruint(@obj.FpctCutIn);
+ PropertyOffset[ord(TProp.pctCutout)] := ptruint(@obj.FpctCutOut);
+ PropertyOffset[ord(TProp.Vminpu)] := ptruint(@obj.VMinPu);
+ PropertyOffset[ord(TProp.Vmaxpu)] := ptruint(@obj.VMaxPu);
+ PropertyOffset[ord(TProp.kVA)] := ptruint(@obj.PVSystemVars.FkVArating);
+ PropertyOffset[ord(TProp.DutyStart)] := ptruint(@obj.DutyStart);
+ PropertyOffset[ord(TProp.kv)] := ptruint(@obj.PVSystemVars.kVPVSystemBase);
+
+ PropertyOffset[ord(TProp.kvar)] := ptruint(@obj.kvarRequested);
+ PropertyReadFunction[ord(TProp.kvar)] := @Getkvar;
+ PropertyFlags[ord(TProp.kvar)] := [TPropertyFlag.ReadByFunction];
+
+ // double percent
+ PropertyScale[ord(TProp.pctPmpp)] := 0.01;
+ PropertyOffset[ord(TProp.pctPmpp)] := ptruint(@obj.PVSystemVars.FpuPmpp);
+
+ // advanced doubles
+ PropertyOffset[ord(TProp.pctPminNoVars)] := ptruint(@obj.FpctPminNoVars);
+ PropertyFlags[ord(TProp.pctPminNoVars)] := [TPropertyFlag.Transform_Abs];
+
+ PropertyOffset[ord(TProp.pctPminkvarMax)] := ptruint(@obj.FpctPminkvarLimit);
+ PropertyFlags[ord(TProp.pctPminkvarMax)] := [TPropertyFlag.Transform_Abs];
+
+ PropertyOffset[ord(TProp.kvarMax)] := ptruint(@obj.PVSystemVars.Fkvarlimit);
+ PropertyFlags[ord(TProp.kvarMax)] := [TPropertyFlag.Transform_Abs];
+
+ PropertyOffset[ord(TProp.kvarMaxAbs)] := ptruint(@obj.PVSystemVars.Fkvarlimitneg);
+ PropertyFlags[ord(TProp.kvarMaxAbs)] := [TPropertyFlag.Transform_Abs];
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- // Override default help string
- PropertyHelp[NumPropsThisClass + 1] := 'Name of harmonic voltage or current spectrum for this PVSystem element. ' +
- 'A harmonic voltage source is assumed for the inverter. ' +
- 'Default value is "default", which is defined when the DSS starts.';
-
+ inherited DefineProperties;
end;
-function TPVsystem2.NewObject(const ObjName: String): Integer;
+function TPVsystem2.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new PVSystem element and add it to PVSystem class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TPVsystem2Obj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-procedure TPVsystem2.SetNcondsForConnection;
+procedure SetNcondsForConnection(Obj: TObj);
begin
- with DSS.ActivePVsystem2Obj do
- begin
+ with Obj do
case Connection of
0:
NConds := Fnphases + 1;
@@ -642,7 +558,6 @@ procedure TPVsystem2.SetNcondsForConnection;
NConds := Fnphases;
end;
end;
- end;
end;
procedure TPVsystem2.UpdateAll;
@@ -655,367 +570,186 @@ procedure TPVsystem2.UpdateAll;
UpdatePVSystem;
end;
-procedure TPVsystem2.InterpretConnection(const S: String);
- // Accepts
- // delta or LL (Case insensitive)
- // Y, wye, or LN
-var
- TestS: String;
-
-begin
- with DSS.ActivePVsystem2Obj do
- begin
- TestS := lowercase(S);
- case TestS[1] of
- 'y', 'w':
- Connection := 0; {Wye}
- 'd':
- Connection := 1; {Delta or line-Line}
- 'l':
- case Tests[2] of
- 'n':
- Connection := 0;
- 'l':
- Connection := 1;
- end;
- end;
-
- SetNCondsForConnection;
-
- {VBase is always L-N voltage unless 1-phase device or more than 3 phases}
-
- with PVSystemVars do
- case Fnphases of
- 2, 3:
- VBase := kVPVSystemBase * InvSQRT3x1000; // L-N Volts
- else
- VBase := kVPVSystemBase * 1000.0; // Just use what is supplied
- end;
-
- VBaseMin := Vminpu * VBase;
- VBaseMax := Vmaxpu * VBase;
-
- Yorder := Fnconds * Fnterms;
- YprimInvalid := TRUE;
- end;
-end;
-
-
-//- - - - - - - - - - - - - - -MAIN EDIT FUNCTION - - - - - - - - - - - - - - -
-
-function TPVsystem2.Edit(): Integer;
-
+procedure TPVsystem2Obj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- i, iCase, ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
+ i: Integer;
begin
+ case Idx of
+ ord(TProp.UserModel):
+ UserModel.Name := UserModelNameStr; // Connect to user written models
+ ord(TProp.UserData):
+ if UserModel.Exists then
+ UserModel.Edit := UserModelEditStr; // Send edit string to user model
- // continue parsing with contents of Parser
- DSS.ActivePVsystem2Obj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActivePVsystem2Obj;
-
- Result := 0;
+ ord(TProp.phases):
+ SetNcondsForConnection(self); // Force Reallocation of terminal info
- with DSS.ActivePVsystem2Obj do
- begin
- ParamPointer := 0;
- ParamName := Parser.NextParam; // Parse next property off the command line
- Param := Parser.StrValue; // Put the string value of the property value in local memory for faster access
- while Length(Param) > 0 do
+ ord(TProp.conn):
begin
+ SetNCondsForConnection(self);
- if (Length(ParamName) = 0) then
- Inc(ParamPointer) // If it is not a named property, assume the next property
- else
- ParamPointer := CommandList.GetCommand(ParamName); // Look up the name in the list for this class
+ // VBase is always L-N voltage unless 1-phase device or more than 3 phases
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[PropertyIdxMap[ParamPointer]] := Param // Update the string value of the property
- else
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for PVSystem "' + Name + '"', 560);
+ with PVSystemVars do
+ case Fnphases of
+ 2, 3:
+ VBase := kVPVSystemBase * InvSQRT3x1000; // L-N Volts
+ else
+ VBase := kVPVSystemBase * 1000.0; // Just use what is supplied
+ end;
- if (ParamPointer > 0) then
- begin
- iCase := PropertyIdxMap[ParamPointer];
- case iCASE of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 561);
- 1:
- NPhases := Parser.Intvalue; // num phases
- 2:
- SetBus(1, param);
- propKV:
- PresentkV := Parser.DblValue;
- propIrradiance:
- PVSystemVars.FIrradiance := Parser.DblValue;
- propPF:
- begin
- varMode := VARMODEPF;
- PFnominal := Parser.DblValue;
- end;
- propMODEL:
- VoltageModel := Parser.IntValue;
- propYEARLY:
- YearlyShape := Param;
- propDAILY:
- DailyShape := Param;
- propDUTY:
- DutyShape := Param;
- propTYEARLY:
- YearlyTShape := Param;
- propTDAILY:
- DailyTShape := Param;
- propTDUTY:
- DutyTShape := Param;
- propCONNECTION:
- InterpretConnection(Param);
- propKVAR:
- begin
- varMode := VARMODEKVAR;
- Presentkvar := Parser.DblValue;
- end;
- propPCTR:
- pctR := Parser.DblValue;
- propPCTX:
- pctX := Parser.DblValue;
- propCLASS:
- FClass := Parser.IntValue;
- propInvEffCurve:
- InverterCurve := Param;
- propTemp:
- PVSystemVars.FTemperature := Parser.DblValue;
- propPmpp:
- PVSystemVars.FPmpp := Parser.DblValue;
- propP_T_Curve:
- Power_TempCurve := Param;
- propCutin:
- FpctCutIn := Parser.DblValue;
- propCutout:
- FpctCutOut := Parser.DblValue;
- propVMINPU:
- VMinPu := Parser.DblValue;
- propVMAXPU:
- VMaxPu := Parser.DblValue;
- propKVA:
- with PVSystemVars do
- begin
- FkVArating := Parser.DblValue;
- if not kvarLimitSet then
- PVSystemVars.Fkvarlimit := FkVArating;
- if not kvarLimitSet and not kvarLimitNegSet then
- PVSystemVars.Fkvarlimitneg := FkVArating;
- end;
- propUSERMODEL:
- UserModel.Name := Parser.StrValue; // Connect to user written models
- propUSERDATA:
- UserModel.Edit := Parser.StrValue; // Send edit string to user model
- propDEBUGTRACE:
- DebugTrace := InterpretYesNo(Param);
- proppctPmpp:
- PVSystemVars.FpuPmpp := Parser.DblValue / 100.0; // convert to pu
- propBalanced:
- ForceBalanced := InterpretYesNo(Param);
- propLimited:
- CurrentLimited := InterpretYesNo(Param);
- propVarFollowInverter:
- FVarFollowInverter := InterpretYesNo(Param);
- propkvarLimit:
- begin
- PVSystemVars.Fkvarlimit := Abs(Parser.DblValue);
- kvarLimitSet := TRUE;
- if not kvarLimitNegSet then
- PVSystemVars.Fkvarlimitneg := Abs(PVSystemVars.Fkvarlimit);
+ VBaseMin := Vminpu * VBase;
+ VBaseMax := Vmaxpu * VBase;
- end;
- propDutyStart:
- DutyStart := Parser.DblValue;
- propPPriority:
- PVSystemVars.P_priority := InterpretYesNo(Param); // set watt priority flag
- propPFPriority:
- PVSystemVars.PF_priority := InterpretYesNo(Param);
-
- propPminNoVars:
- FpctPminNoVars := abs(Parser.DblValue);
- propPminkvarLimit:
- FpctPminkvarLimit := abs(Parser.DblValue);
-
- propkvarLimitneg:
- begin
- PVSystemVars.Fkvarlimitneg := Abs(Parser.DblValue);
- kvarLimitNegSet := TRUE;
- end;
+ Yorder := Fnconds * Fnterms;
+ YprimInvalid := TRUE;
+ end;
+ ord(TProp.pf):
+ varMode := VARMODEPF;
+ ord(TProp.kvar):
+ varMode := VARMODEKVAR;
+ ord(TProp.kv):
+ begin
+ with PVSystemVars do
+ case FNphases of
+ 2, 3:
+ VBase := kVPVSystemBase * InvSQRT3x1000;
else
- // Inherited parameters
- ClassEdit(DSS.ActivePVsystem2Obj, ParamPointer - NumPropsThisClass)
+ VBase := kVPVSystemBase * 1000.0;
end;
+ end;
- case iCase of
- 1:
- SetNcondsForConnection; // Force Reallocation of terminal info
-
- {Set loadshape objects; returns nil If not valid}
- propYEARLY:
- YearlyShapeObj := DSS.LoadShapeClass.Find(YearlyShape);
- propDAILY:
- DailyShapeObj := DSS.LoadShapeClass.Find(DailyShape);
- propDUTY:
- DutyShapeObj := DSS.LoadShapeClass.Find(DutyShape);
-
- propTYEARLY:
- YearlyTShapeObj := DSS.TShapeClass.Find(YearlyTShape);
- propTDAILY:
- DailyTShapeObj := DSS.TShapeClass.Find(DailyTShape);
- propTDUTY:
- DutyTShapeObj := DSS.TShapeClass.Find(DutyTShape);
-
- propInvEffCurve:
- InverterCurveObj := DSS.XYCurveClass.Find(InverterCurve);
- propP_T_Curve:
- Power_TempCurveObj := DSS.XYCurveClass.Find(Power_TempCurve);
-
- propDEBUGTRACE:
- if DebugTrace then
- begin // Init trace file
- FreeAndNil(TraceFile);
- TraceFile := TFileStream.Create(DSS.OutputDirectory + 'STOR_' + Name + '.CSV', fmCreate);
- FSWrite(TraceFile, 't, Iteration, LoadMultiplier, Mode, LoadModel, PVSystemModel, Qnominalperphase, Pnominalperphase, CurrentType');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Iinj' + IntToStr(i) + '|');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Iterm' + IntToStr(i) + '|');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Vterm' + IntToStr(i) + '|');
- FSWrite(TraceFile, ',Vthev, Theta');
- FSWriteln(TraceFile);
- FSFlush(Tracefile);
- end
- else
- begin
- FreeAndNil(TraceFile);
- end;
-
- end;
+ ord(TProp.kVA):
+ with PVSystemVars do
+ begin
+ if not kvarLimitSet then
+ PVSystemVars.Fkvarlimit := FkVArating;
+ if not kvarLimitSet and not kvarLimitNegSet then
+ PVSystemVars.Fkvarlimitneg := FkVArating;
end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
+ ord(TProp.kvarMaxAbs):
+ kvarLimitNegSet := TRUE;
- RecalcElementData();
- YprimInvalid := TRUE;
+ ord(TProp.kvarMax):
+ begin
+ kvarLimitSet := TRUE;
+ if not kvarLimitNegSet then
+ PVSystemVars.Fkvarlimitneg := Abs(PVSystemVars.Fkvarlimit);
+ end;
+ ord(TProp.debugtrace):
+ if DebugTrace then
+ begin // Init trace file
+ FreeAndNil(TraceFile);
+ TraceFile := TBufferedFileStream.Create(DSS.OutputDirectory + 'STOR_' + Name + '.csv', fmCreate);
+ FSWrite(TraceFile, 't, Iteration, LoadMultiplier, Mode, LoadModel, PVSystemModel, Qnominalperphase, Pnominalperphase, CurrentType');
+ for i := 1 to nphases do
+ FSWrite(Tracefile, ', |Iinj' + IntToStr(i) + '|');
+ for i := 1 to nphases do
+ FSWrite(Tracefile, ', |Iterm' + IntToStr(i) + '|');
+ for i := 1 to nphases do
+ FSWrite(Tracefile, ', |Vterm' + IntToStr(i) + '|');
+ FSWrite(TraceFile, ',Vthev, Theta');
+ FSWriteln(TraceFile);
+ FSFlush(Tracefile);
+ end
+ else
+ begin
+ FreeAndNil(TraceFile);
+ end;
end;
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-function TPVsystem2.MakeLike(const OtherPVsystemObjName: String): Integer;
-
-// Copy over essential properties from other object
+function TPVsystem2.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
+ begin
+ RecalcElementData;
+ YPrimInvalid := TRUE;
+ Exclude(Flags, Flg.EditionActive);
+ end;
+ Result := True;
+end;
+procedure TPVsystem2Obj.MakeLike(OtherPtr: Pointer);
var
- OtherPVsystemObj: TPVsystem2Obj;
- i: Integer;
-
+ Other: TObj;
begin
- Result := 0;
- {See If we can find this line name in the present collection}
- OtherPVsystemObj := Find(OtherPVsystemObjName);
- if (OtherPVsystemObj <> NIL) then
+ inherited MakeLike(OtherPtr);
+
+ Other := TObj(OtherPtr);
+ if (Fnphases <> Other.Fnphases) then
begin
- with DSS.ActivePVsystem2Obj do
- begin
- if (Fnphases <> OtherPVsystemObj.Fnphases) then
- begin
- Nphases := OtherPVsystemObj.Fnphases;
- NConds := Fnphases; // Forces reallocation of terminal stuff
- Yorder := Fnconds * Fnterms;
- YprimInvalid := TRUE;
- end;
+ FNphases := Other.Fnphases;
+ NConds := Fnphases; // Forces reallocation of terminal stuff
+ Yorder := Fnconds * Fnterms;
+ YprimInvalid := TRUE;
+ end;
- PVSystemVars.kVPVSystemBase := OtherPVsystemObj.PVSystemVars.kVPVSystemBase;
- Vbase := OtherPVsystemObj.Vbase;
- Vminpu := OtherPVsystemObj.Vminpu;
- Vmaxpu := OtherPVsystemObj.Vmaxpu;
- VBaseMin := OtherPVsystemObj.VBaseMin;
- VBaseMax := OtherPVsystemObj.VBaseMax;
- kW_out := OtherPVsystemObj.kW_out;
- kvar_out := OtherPVsystemObj.kvar_out;
- Pnominalperphase := OtherPVsystemObj.Pnominalperphase;
- PFnominal := OtherPVsystemObj.PFnominal;
- Qnominalperphase := OtherPVsystemObj.Qnominalperphase;
- Connection := OtherPVsystemObj.Connection;
- YearlyShape := OtherPVsystemObj.YearlyShape;
- YearlyShapeObj := OtherPVsystemObj.YearlyShapeObj;
- DailyShape := OtherPVsystemObj.DailyShape;
- DailyShapeObj := OtherPVsystemObj.DailyShapeObj;
- DutyShape := OtherPVsystemObj.DutyShape;
- DutyShapeObj := OtherPVsystemObj.DutyShapeObj;
- DutyStart := OtherPVsystemObj.DutyStart;
- YearlyTShape := OtherPVsystemObj.YearlyTShape;
- YearlyTShapeObj := OtherPVsystemObj.YearlyTShapeObj;
- DailyTShape := OtherPVsystemObj.DailyTShape;
- DailyTShapeObj := OtherPVsystemObj.DailyTShapeObj;
- DutyTShape := OtherPVsystemObj.DutyTShape;
- DutyTShapeObj := OtherPVsystemObj.DutyTShapeObj;
- InverterCurve := OtherPVsystemObj.InverterCurve;
- InverterCurveObj := OtherPVsystemObj.InverterCurveObj;
- Power_TempCurve := OtherPVsystemObj.Power_TempCurve;
- Power_TempCurveObj := OtherPVsystemObj.Power_TempCurveObj;
- FClass := OtherPVsystemObj.FClass;
- VoltageModel := OtherPVsystemObj.VoltageModel;
-
- PVSystemVars.FTemperature := OtherPVsystemObj.PVSystemVars.FTemperature;
- PVSystemVars.FPmpp := OtherPVsystemObj.PVSystemVars.FPmpp;
- FpctCutin := OtherPVsystemObj.FpctCutin;
- FpctCutout := OtherPVsystemObj.FpctCutout;
- FVarFollowInverter := OtherPVsystemObj.FVarFollowInverter;
- PVSystemVars.Fkvarlimit := OtherPVsystemObj.PVSystemVars.Fkvarlimit;
- PVSystemVars.Fkvarlimitneg := OtherPVsystemObj.PVSystemVars.Fkvarlimitneg;
- FpctPminNoVars := OtherPVsystemObj.FpctPminNoVars;
- FpctPminkvarLimit := OtherPVsystemObj.FpctPminkvarLimit;
- kvarLimitSet := OtherPVsystemObj.kvarLimitSet;
- kvarLimitNegSet := OtherPVsystemObj.kvarLimitNegSet;
-
-
- PVSystemVars.FIrradiance := OtherPVsystemObj.PVSystemVars.FIrradiance;
-
- PVSystemVars.FkVArating := OtherPVsystemObj.PVSystemVars.FkVArating;
-
- pctR := OtherPVsystemObj.pctR;
- pctX := OtherPVsystemObj.pctX;
-
- RandomMult := OtherPVsystemObj.RandomMult;
- FVWMode := OtherPVsystemObj.FVWMode;
- FVVMode := OtherPVsystemObj.FVVMode;
- FWPMode := OtherPVsystemObj.FWPMode;
- FWVMode := OtherPVsystemObj.FWPMode;
- FDRCMode := OtherPVsystemObj.FDRCMode;
- AVRMode := OtherPVsystemObj.AVRMode;
- UserModel.Name := OtherPVsystemObj.UserModel.Name; // Connect to user written models
-
- ForceBalanced := OtherPVsystemObj.ForceBalanced;
- CurrentLimited := OtherPVsystemObj.CurrentLimited;
-
- ClassMakeLike(OtherPVsystemObj);
-
- for i := 1 to ParentClass.NumProperties do
- FPropertyValue^[i] := OtherPVsystemObj.FPropertyValue^[i];
-
- Result := 1;
- end;
- end
- else
- DoSimpleMsg('Error in PVSystem MakeLike: "' + OtherPVsystemObjName + '" Not Found.', 562);
+ PVSystemVars.kVPVSystemBase := Other.PVSystemVars.kVPVSystemBase;
+ Vbase := Other.Vbase;
+ Vminpu := Other.Vminpu;
+ Vmaxpu := Other.Vmaxpu;
+ VBaseMin := Other.VBaseMin;
+ VBaseMax := Other.VBaseMax;
+ kW_out := Other.kW_out;
+ kvar_out := Other.kvar_out;
+ Pnominalperphase := Other.Pnominalperphase;
+ PFnominal := Other.PFnominal;
+ Qnominalperphase := Other.Qnominalperphase;
+ Connection := Other.Connection;
+ YearlyShapeObj := Other.YearlyShapeObj;
+ DailyShapeObj := Other.DailyShapeObj;
+ DutyShapeObj := Other.DutyShapeObj;
+ DutyStart := Other.DutyStart;
+ YearlyTShapeObj := Other.YearlyTShapeObj;
+ DailyTShapeObj := Other.DailyTShapeObj;
+ DutyTShapeObj := Other.DutyTShapeObj;
+ InverterCurveObj := Other.InverterCurveObj;
+ Power_TempCurveObj := Other.Power_TempCurveObj;
+ FClass := Other.FClass;
+ VoltageModel := Other.VoltageModel;
+
+ PVSystemVars.FTemperature := Other.PVSystemVars.FTemperature;
+ PVSystemVars.FPmpp := Other.PVSystemVars.FPmpp;
+ FpctCutin := Other.FpctCutin;
+ FpctCutout := Other.FpctCutout;
+ VarFollowInverter := Other.VarFollowInverter;
+ PVSystemVars.Fkvarlimit := Other.PVSystemVars.Fkvarlimit;
+ PVSystemVars.Fkvarlimitneg := Other.PVSystemVars.Fkvarlimitneg;
+ FpctPminNoVars := Other.FpctPminNoVars;
+ FpctPminkvarLimit := Other.FpctPminkvarLimit;
+ kvarLimitSet := Other.kvarLimitSet;
+ kvarLimitNegSet := Other.kvarLimitNegSet;
+
+ PVSystemVars.FIrradiance := Other.PVSystemVars.FIrradiance;
+
+ PVSystemVars.FkVArating := Other.PVSystemVars.FkVArating;
+
+ pctR := Other.pctR;
+ pctX := Other.pctX;
+
+ RandomMult := Other.RandomMult;
+ FVWMode := Other.FVWMode;
+ FVVMode := Other.FVVMode;
+ FWPMode := Other.FWPMode;
+ FWVMode := Other.FWPMode;
+ FDRCMode := Other.FDRCMode;
+ AVRMode := Other.AVRMode;
+ UserModel.Name := Other.UserModel.Name; // Connect to user written models
+ UserModelNameStr := Other.UserModelNameStr;
+ //UserModelEditStr := Other.UserModelEditStr;
+
+ ForceBalanced := Other.ForceBalanced;
+ CurrentLimited := Other.CurrentLimited;
end;
procedure TPVsystem2.ResetRegistersAll; // Force all EnergyMeters in the circuit to reset
-
var
idx: Integer;
-
begin
idx := First;
while (idx > 0) do
@@ -1026,7 +760,6 @@ procedure TPVsystem2.ResetRegistersAll; // Force all EnergyMeters in the circui
end;
procedure TPVsystem2.SampleAll(); // Force all active PV System energy meters to take a sample
-
var
i: Integer;
begin
@@ -1039,37 +772,29 @@ procedure TPVsystem2.SampleAll(); // Force all active PV System energy meters
constructor TPVsystem2Obj.Create(ParClass: TDSSClass; const SourceName: String);
begin
inherited create(ParClass);
- Name := LowerCase(SourceName);
+ Name := AnsiLowerCase(SourceName);
DSSObjType := ParClass.DSSClassType; // + PVSystem_ELEMENT; // In both PCelement and PVSystemelement list
TraceFile := nil;
- Nphases := 3;
+ FNphases := 3;
Fnconds := 4; // defaults to wye
Yorder := 0; // To trigger an initial allocation
Nterms := 1; // forces allocations
- YearlyShape := '';
YearlyShapeObj := NIL; // If YearlyShapeobj = nil Then the Irradiance alway stays nominal
- DailyShape := '';
DailyShapeObj := NIL; // If DaillyShapeobj = nil Then the Irradiance alway stays nominal
- DutyShape := '';
DutyShapeObj := NIL; // If DutyShapeobj = nil Then the Irradiance alway stays nominal
DutyStart := 0.0;
- YearlyTShape := '';
YearlyTShapeObj := NIL; // If YearlyShapeobj = nil Then the Temperature always stays nominal
- DailyTShape := '';
DailyTShapeObj := NIL; // If DaillyShapeobj = nil Then the Temperature always stays nominal
- DutyTShape := '';
DutyTShapeObj := NIL; // If DutyShapeobj = nil Then the Temperature always stays nominal
InverterCurveObj := NIL;
Power_TempCurveObj := NIL;
- InverterCurve := '';
- Power_TempCurve := '';
Connection := 0; // Wye (star, L-N)
- VoltageModel := 1; {Typical fixed kW negative load}
+ VoltageModel := 1; // Typical fixed kW negative load
FClass := 1;
PVSystemSolutionCount := -1; // For keep track of the present solution in Injcurrent calcs
@@ -1087,7 +812,7 @@ constructor TPVsystem2Obj.Create(ParClass: TDSSClass; const SourceName: String);
varMode := VARMODEPF;
FInverterON := TRUE; // start with inverterON
- FVarFollowInverter := FALSE;
+ VarFollowInverter := FALSE;
ForceBalanced := FALSE;
CurrentLimited := FALSE;
@@ -1121,13 +846,12 @@ constructor TPVsystem2Obj.Create(ParClass: TDSSClass; const SourceName: String);
Fpf_wp_nominal := 1.0;
- {Output rating stuff}
+ // Output rating stuff
kW_out := 500.0;
kvar_out := 0.0;
PFnominal := 1.0;
pctR := 50.0;
- ;
pctX := 0.0;
PublicDataStruct := @PVSystemVars;
@@ -1138,6 +862,8 @@ constructor TPVsystem2Obj.Create(ParClass: TDSSClass; const SourceName: String);
UserModel := TPVsystemUserModel.Create(DSS);
+ UserModelNameStr := '';
+ UserModelEditStr := '';
Reg_kWh := 1;
Reg_kvarh := 2;
@@ -1148,159 +874,14 @@ constructor TPVsystem2Obj.Create(ParClass: TDSSClass; const SourceName: String);
DebugTrace := FALSE;
PVsystemObjSwitchOpen := FALSE;
- Spectrum := ''; // override base class
- SpectrumObj := NIL;
+ SpectrumObj := NIL; // override base class
FVWMode := FALSE;
FVVMode := FALSE;
FWVMode := FALSE;
FWPMode := FALSE;
FDRCMode := FALSE;
AVRMode := FALSE;
- InitPropertyValues(0);
RecalcElementData();
-
-end;
-
-procedure TPVsystem2Obj.InitPropertyValues(ArrayOffset: Integer);
-
-// Define default values for the properties
-begin
-
- with PVSystemVars do
- begin
- PropertyValue[1] := '3'; //'phases';
- PropertyValue[2] := Getbus(1); //'bus1';
-
- PropertyValue[propKV] := Format('%-g', [kVPVSystemBase]);
- PropertyValue[propIrradiance] := Format('%-g', [FIrradiance]);
- PropertyValue[propPF] := Format('%-g', [PFnominal]);
- PropertyValue[propMODEL] := '1';
- PropertyValue[propYEARLY] := '';
- PropertyValue[propDAILY] := '';
- PropertyValue[propDUTY] := '';
- PropertyValue[propTYEARLY] := '';
- PropertyValue[propTDAILY] := '';
- PropertyValue[propTDUTY] := '';
- PropertyValue[propCONNECTION] := 'wye';
- PropertyValue[propKVAR] := Format('%-g', [Presentkvar]);
-
- PropertyValue[propPCTR] := Format('%-g', [pctR]);
- PropertyValue[propPCTX] := Format('%-g', [pctX]);
-
- PropertyValue[propCLASS] := '1'; //'class'
-
- PropertyValue[propInvEffCurve] := '';
- PropertyValue[propTemp] := Format('%-g', [FTemperature]);
- PropertyValue[propPmpp] := Format('%-g', [FPmpp]);
- PropertyValue[propP_T_Curve] := '';
- PropertyValue[propCutin] := '20';
- PropertyValue[propCutout] := '20';
- PropertyValue[propVarFollowInverter] := 'NO';
-
- PropertyValue[propVMINPU] := '0.90';
- PropertyValue[propVMAXPU] := '1.10';
- PropertyValue[propKVA] := Format('%-g', [FkVArating]);
-
- PropertyValue[propUSERMODEL] := ''; // Usermodel
- PropertyValue[propUSERDATA] := ''; // Userdata
- PropertyValue[propDEBUGTRACE] := 'NO';
- PropertyValue[proppctPmpp] := '100';
- PropertyValue[propBalanced] := 'NO';
- PropertyValue[propLimited] := 'NO';
- PropertyValue[propkvarLimit] := Format('%-g', [Fkvarlimit]);
- PropertyValue[propkvarLimitneg] := Format('%-g', [Fkvarlimitneg]);
- PropertyValue[propPpriority] := 'NO';
- PropertyValue[propPFpriority] := 'NO';
- end;
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-function TPVsystem2Obj.GetPropertyValue(Index: Integer): String;
-
-begin
- Result := '';
- with PVSystemVars do
- case Index of
- propKV:
- Result := Format('%.6g', [kVPVSystemBase]);
- propIrradiance:
- Result := Format('%.6g', [FIrradiance]);
- propPF:
- Result := Format('%.6g', [PFnominal]);
- propMODEL:
- Result := Format('%d', [VoltageModel]);
- propYEARLY:
- Result := YearlyShape;
- propDAILY:
- Result := DailyShape;
- propDUTY:
- Result := DutyShape;
-
- propTYEARLY:
- Result := YearlyTShape;
- propTDAILY:
- Result := DailyTShape;
- propTDUTY:
- Result := DutyTShape;
-
- {propCONNECTION :;}
- propKVAR:
- Result := Format('%.6g', [kvar_out]);
- propPCTR:
- Result := Format('%.6g', [pctR]);
- propPCTX:
- Result := Format('%.6g', [pctX]);
- {propCLASS = 17;}
- propInvEffCurve:
- Result := InverterCurve;
- propTemp:
- Result := Format('%.6g', [FTemperature]);
- propPmpp:
- Result := Format('%.6g', [FPmpp]);
- propP_T_Curve:
- Result := Power_TempCurve;
- propCutin:
- Result := Format('%.6g', [FpctCutin]);
- propCutOut:
- Result := Format('%.6g', [FpctCutOut]);
- propVarFollowInverter:
- Result := StrYorN(FVarFollowInverter);
- propPminNoVars:
- Result := Format('%.6g', [FpctPminNoVars]);
- propPminkvarLimit:
- Result := Format('%.6g', [FpctPminkvarLimit]);
-
- propVMINPU:
- Result := Format('%.6g', [VMinPu]);
- propVMAXPU:
- Result := Format('%.6g', [VMaxPu]);
- propKVA:
- Result := Format('%.6g', [FkVArating]);
-
- propUSERMODEL:
- Result := UserModel.Name;
- propUSERDATA:
- Result := '(' + inherited GetPropertyValue(index) + ')';
- proppctPmpp:
- Result := Format('%.6g', [FpuPmpp * 100.0]);
- propBalanced:
- Result := StrYorN(ForceBalanced);
- propLimited:
- Result := StrYorN(CurrentLimited);
- propkvarLimit:
- Result := Format('%.6g', [Fkvarlimit]);
- propkvarLimitneg:
- Result := Format('%.6g', [Fkvarlimitneg]);
- propDutyStart:
- Result := Format('%.6g', [DutyStart]);
-
-
- {propDEBUGTRACE = 33;}
- else // take the generic handler
- Result := inherited GetPropertyValue(index);
- end;
end;
destructor TPVsystem2Obj.Destroy;
@@ -1416,49 +997,18 @@ procedure TPVsystem2Obj.RecalcElementData();
SetNominalPVSystemOuput();
- {Now check for errors. If any of these came out nil and the string was not nil, give warning}
- if YearlyShapeObj = NIL then
- if Length(YearlyShape) > 0 then
- DoSimpleMsg('WARNING! Yearly load shape: "' + YearlyShape + '" Not Found.', 563);
- if DailyShapeObj = NIL then
- if Length(DailyShape) > 0 then
- DoSimpleMsg('WARNING! Daily load shape: "' + DailyShape + '" Not Found.', 564);
- if DutyShapeObj = NIL then
- if Length(DutyShape) > 0 then
- DoSimpleMsg('WARNING! Duty load shape: "' + DutyShape + '" Not Found.', 565);
- if YearlyTShapeObj = NIL then
- if Length(YearlyTShape) > 0 then
- DoSimpleMsg('WARNING! Yearly temperature shape: "' + YearlyTShape + '" Not Found.', 5631);
- if DailyTShapeObj = NIL then
- if Length(DailyTShape) > 0 then
- DoSimpleMsg('WARNING! Daily temperature shape: "' + DailyTShape + '" Not Found.', 5641);
- if DutyTShapeObj = NIL then
- if Length(DutyTShape) > 0 then
- DoSimpleMsg('WARNING! Duty temperature shape: "' + DutyTShape + '" Not Found.', 5651);
-
- if Length(Spectrum) > 0 then
- begin
- SpectrumObj := DSS.SpectrumClass.Find(Spectrum);
- if SpectrumObj = NIL then
- DoSimpleMsg('ERROR! Spectrum "' + Spectrum + '" Not Found.', 566);
- end
- else
- SpectrumObj := NIL;
-
// Initialize to Zero - defaults to PQ PVSystem element
// Solution object will reset after circuit modifications
Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
- {Update any user-written models}
+ // Update any user-written models
if Usermodel.Exists then
UserModel.FUpdateModel;
-
end;
procedure TPVsystem2Obj.SetNominalPVSystemOuput();
begin
-
ShapeFactor := CDOUBLEONE; // init here; changed by curve routine
TShapeValue := PVSystemVars.FTemperature; // init here; changed by curve routine
@@ -1470,7 +1020,7 @@ procedure TPVsystem2Obj.SetNominalPVSystemOuput();
// Check dispatch to see what state the PVSystem element should be in
with Solution do
case Mode of
- TSolveMode.SNAPSHOT: ; {Just solve for the present kW, kvar} // Don't check for state change
+ TSolveMode.SNAPSHOT: ; // Just solve for the present kW, kvar // Don't check for state change
TSolveMode.DAILYMODE:
begin
CalcDailyMult(DynaVars.dblHour);
@@ -1481,12 +1031,10 @@ procedure TPVsystem2Obj.SetNominalPVSystemOuput();
CalcYearlyMult(DynaVars.dblHour);
CalcYearlyTemperature(DynaVars.dblHour);
end;
- (*
- MONTECARLO1,
- MONTEFAULT,
- FAULTSTUDY,
- DYNAMICMODE: ; // {do nothing yet}
- *)
+ // TSolveMode.MONTECARLO1,
+ // TSolveMode.MONTEFAULT,
+ // TSolveMode.FAULTSTUDY,
+ // TSolveMode.DYNAMICMODE: ; // // do nothing yet
TSolveMode.GENERALTIME:
begin
// This mode allows use of one class of load shape
@@ -1531,7 +1079,7 @@ procedure TPVsystem2Obj.SetNominalPVSystemOuput();
CalcDutyMult(DynaVars.dblHour);
CalcDutyTemperature(DynaVars.dblHour);
end;
- {AUTOADDFLAG: ; }
+ // AUTOADDFLAG: ;
end;
ComputekWkvar;
@@ -1545,15 +1093,15 @@ procedure TPVsystem2Obj.SetNominalPVSystemOuput();
else
- YEQ := CDivReal(Cmplx(Pnominalperphase, -Qnominalperphase), Sqr(Vbase)); // Vbase must be L-N for 3-phase
+ YEQ := Cmplx(Pnominalperphase, -Qnominalperphase) / Sqr(Vbase); // Vbase must be L-N for 3-phase
if (Vminpu <> 0.0) then
- YEQ_Min := CDivReal(YEQ, SQR(Vminpu)) // at 95% voltage
+ YEQ_Min := YEQ / SQR(Vminpu) // at 95% voltage
else
YEQ_Min := YEQ; // Always a constant Z model
if (Vmaxpu <> 0.0) then
- YEQ_Max := CDivReal(YEQ, SQR(Vmaxpu)) // at 105% voltage
+ YEQ_Max := YEQ / SQR(Vmaxpu) // at 105% voltage
else
YEQ_Max := YEQ;
@@ -1561,29 +1109,23 @@ procedure TPVsystem2Obj.SetNominalPVSystemOuput();
}
with PVSystemvars do
begin
- PhaseCurrentLimit := Cdivreal(Cmplx(Pnominalperphase, Qnominalperphase), VBaseMin);
+ PhaseCurrentLimit := Cmplx(Pnominalperphase, Qnominalperphase) / VBaseMin;
MaxDynPhaseCurrent := Cabs(PhaseCurrentLimit);
end;
end;
- { When we leave here, all the YEQ's are in L-N values}
-
- end; {If NOT (IsDynamicModel or IsHarmonicModel)}
- end; {With ActiveCircuit}
-
+ // When we leave here, all the YEQ's are in L-N values
+ end; // If NOT (IsDynamicModel or IsHarmonicModel)
+ end; // With ActiveCircuit
end;
-
-// ===========================================================================================
procedure TPVsystem2Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
-
var
Y, Yij: Complex;
i, j: Integer;
FreqMultiplier: Double;
-
begin
FYprimFreq := ActiveCircuit.Solution.Frequency;
FreqMultiplier := FYprimFreq / BaseFrequency;
@@ -1591,13 +1133,13 @@ procedure TPVsystem2Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
with ActiveCircuit.solution do
if IsDynamicModel or IsHarmonicModel then
begin
- {YEQ is computed from %R and %X -- inverse of Rthev + j Xthev}
+ // YEQ is computed from %R and %X -- inverse of Rthev + j Xthev
Y := YEQ; // L-N value computed in initialization routines
if Connection = 1 then
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
+ Y := Y / 3.0; // Convert to delta impedance
Y.im := Y.im / FreqMultiplier;
- Yij := Cnegate(Y);
+ Yij := -Y;
for i := 1 to Fnphases do
begin
@@ -1610,7 +1152,7 @@ procedure TPVsystem2Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
end;
1:
- begin {Delta connection}
+ begin // Delta connection
Ymatrix.SetElement(i, i, Y);
Ymatrix.AddElement(i, i, Y); // put it in again
for j := 1 to i - 1 do
@@ -1622,19 +1164,17 @@ procedure TPVsystem2Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
else
begin // Regular power flow PVSystem element model
+ // YEQ is always expected as the equivalent line-neutral admittance
+ Y := -YEQ; // negate for generation YEQ is L-N quantity
- {YEQ is always expected as the equivalent line-neutral admittance}
-
- Y := cnegate(YEQ); // negate for generation YEQ is L-N quantity
-
- // ****** Need to modify the base admittance for real harmonics calcs
+ // ****** Need to modify the base admittance for real harmonics calcs
Y.im := Y.im / FreqMultiplier;
case Connection of
0:
with YMatrix do
begin // WYE
- Yij := Cnegate(Y);
+ Yij := -Y;
for i := 1 to Fnphases do
begin
SetElement(i, i, Y);
@@ -1646,8 +1186,8 @@ procedure TPVsystem2Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
1:
with YMatrix do
begin // Delta or L-L
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
- Yij := Cnegate(Y);
+ Y := Y / 3.0; // Convert to delta impedance
+ Yij := -Y;
for i := 1 to Fnphases do
begin
j := i + 1;
@@ -1660,16 +1200,15 @@ procedure TPVsystem2Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
end;
end;
- end; {ELSE IF Solution.mode}
-
+ end; // ELSE IF Solution.mode
end;
procedure TPVsystem2Obj.ComputeInverterPower;
var
kVA_Gen: Double;
- Qramp_limit: Double;
- TempPF: Double;
+ Qramp_limit: Double = 0.0;
+ TempPF: Double = 0.0;
CutOutkWAC: Double;
// CutInkWAC: Double;
@@ -1831,7 +1370,7 @@ procedure TPVsystem2Obj.ComputeInverterPower;
kvar_Out := kvarRequested;
end;
- if (FInverterON = FALSE) and (FVarFollowInverter = TRUE) then
+ if (FInverterON = FALSE) and (VarFollowInverter = TRUE) then
kvar_out := 0.0;
// Limit kvar and kW so that kVA of inverter is not exceeded
@@ -1881,9 +1420,9 @@ procedure TPVsystem2Obj.ComputeInverterPower;
kW_Out := Sqrt(SQR(FkVArating) - SQR(kvar_Out)) * sign(kW_Out);
end;
end;
- if (FInverterON = FALSE) and (FVarFollowInverter = TRUE) then
+ if (FInverterON = FALSE) and (VarFollowInverter = TRUE) then
kvar_out := 0.0;
- end; {With PVSystemvars}
+ end; // With PVSystemvars
end;
@@ -1893,7 +1432,6 @@ procedure TPVsystem2Obj.ComputekWkvar;
ComputeInverterPower; // apply inverter eff after checking for cutin/cutout
end;
-// ===========================================================================================
procedure TPVsystem2Obj.ComputePanelPower;
begin
with PVSystemVars do
@@ -1940,20 +1478,18 @@ procedure TPVsystem2Obj.CalcYPrim();
// Set YPrim_Series based on diagonals of YPrim_shunt so that CalcVoltages Doesn't fail
for i := 1 to Yorder do
- Yprim_Series.SetElement(i, i, CmulReal(Yprim_Shunt.Getelement(i, i), 1.0e-10));
+ Yprim_Series.SetElement(i, i, Yprim_Shunt.Getelement(i, i) * 1.0e-10);
YPrim.CopyFrom(YPrim_Shunt);
// Account for Open Conductors
inherited CalcYPrim();
-
end;
-// ===========================================================================================
procedure TPVsystem2Obj.StickCurrInTerminalArray(TermArray: pComplexArray; const Curr: Complex; i: Integer);
- {Add the current into the proper location according to connection}
+ // Add the current into the proper location according to connection
- {Reverse of similar routine in load (Cnegates are switched)}
+ // Reverse of similar routine in load (Cnegates are switched)
var
j: Integer;
@@ -1962,22 +1498,21 @@ procedure TPVsystem2Obj.StickCurrInTerminalArray(TermArray: pComplexArray; const
case Connection of
0:
begin //Wye
- Caccum(TermArray^[i], Curr);
- Caccum(TermArray^[Fnconds], Cnegate(Curr)); // Neutral
+ TermArray^[i] += Curr;
+ TermArray^[Fnconds] -= Curr; // Neutral
end;
1:
begin //DELTA
- Caccum(TermArray^[i], Curr);
+ TermArray^[i] += Curr;
j := i + 1;
if j > Fnconds then
j := 1;
- Caccum(TermArray^[j], Cnegate(Curr));
+ TermArray^[j] -= Curr;
end;
end;
end;
-// ===========================================================================================
procedure TPVsystem2Obj.WriteTraceRecord(const s: String);
var
@@ -1991,8 +1526,8 @@ procedure TPVsystem2Obj.WriteTraceRecord(const s: String);
[ActiveCircuit.Solution.DynaVARs.t,
ActiveCircuit.Solution.Iteration,
ActiveCircuit.LoadMultiplier]),
- GetSolutionModeID(DSS), ', ',
- GetLoadModel(DSS), ', ',
+ DSS.SolveModeEnum.OrdinalToString(ord(DSS.ActiveCircuit.Solution.mode)), ', ',
+ DSS.DefaultLoadModelEnum.OrdinalToString(DSS.ActiveCircuit.Solution.LoadModel), ', ',
VoltageModel: 0, ', ',
(Qnominalperphase * 3.0 / 1.0e6): 8: 2, ', ',
(Pnominalperphase * 3.0 / 1.0e6): 8: 2, ', ',
@@ -2020,17 +1555,15 @@ procedure TPVsystem2Obj.WriteTraceRecord(const s: String);
except
On E: Exception do
begin
-
end;
end;
end;
-// ===========================================================================================
procedure TPVsystem2Obj.DoConstantPQPVsystemObj();
-{Compute total terminal current for Constant PQ}
+// Compute total terminal current for Constant PQ
var
i: Integer;
@@ -2059,53 +1592,52 @@ procedure TPVsystem2Obj.DoConstantPQPVsystemObj();
for i := 1 to Fnphases do
begin
-
case Connection of
0:
- begin {Wye}
+ begin // Wye
VLN := Vterminal^[i];
VMagLN := Cabs(VLN);
if CurrentLimited then
begin
- {Current-Limited Model}
- PhaseCurr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLN));
+ // Current-Limited Model
+ PhaseCurr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLN);
if Cabs(PhaseCurr) > PVSystemVars.MaxDynPhaseCurrent then
- PhaseCurr := Conjg(Cdiv(PhaseCurrentLimit, CDivReal(VLN, VMagLN)));
+ PhaseCurr := cong(PhaseCurrentLimit / (VLN / VMagLN));
end
else
begin
- {The usual model}
+ // The usual model
if (VMagLN <= VBaseMin) then
- PhaseCurr := Cmul(YEQ_Min, VLN) // Below Vminpu use an impedance model
+ PhaseCurr := YEQ_Min * VLN // Below Vminpu use an impedance model
else
if (VMagLN > VBaseMax) then
- PhaseCurr := Cmul(YEQ_Max, VLN) // above Vmaxpu use an impedance model
+ PhaseCurr := YEQ_Max * VLN // above Vmaxpu use an impedance model
else
- PhaseCurr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLN)); // Between Vminpu and Vmaxpu, constant PQ
+ PhaseCurr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLN); // Between Vminpu and Vmaxpu, constant PQ
end;
- StickCurrInTerminalArray(ITerminal, Cnegate(PhaseCurr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -PhaseCurr, i); // Put into Terminal array taking into account connection
set_ITerminalUpdated(TRUE);
StickCurrInTerminalArray(InjCurrent, PhaseCurr, i); // Put into Terminal array taking into account connection
end;
1:
- begin {Delta}
+ begin // Delta
VLL := Vterminal^[i];
VMagLL := Cabs(VLL);
if CurrentLimited then
begin
- {Current-Limited Model}
- DeltaCurr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLL));
+ // Current-Limited Model
+ DeltaCurr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLL);
if Cabs(DeltaCurr) * SQRT3 > PVSystemVars.MaxDynPhaseCurrent then
- DeltaCurr := Conjg(Cdiv(PhaseCurrentLimit, CDivReal(VLL, VMagLL / SQRT3)));
+ DeltaCurr := cong(PhaseCurrentLimit / (VLL / (VMagLL / SQRT3)));
end
else
begin
- {The usual model}
+ // The usual model
case Fnphases of
2, 3:
VMagLN := VmagLL / SQRT3;
@@ -2114,15 +1646,15 @@ procedure TPVsystem2Obj.DoConstantPQPVsystemObj();
end;
if VMagLN <= VBaseMin then
- DeltaCurr := Cmul(CdivReal(YEQ_Min, 3.0), VLL) // Below 95% use an impedance model
+ DeltaCurr := (YEQ_Min / 3.0) * VLL // Below 95% use an impedance model
else
if VMagLN > VBaseMax then
- DeltaCurr := Cmul(CdivReal(YEQ_Max, 3.0), VLL) // above 105% use an impedance model
+ DeltaCurr := (YEQ_Max / 3.0) * VLL // above 105% use an impedance model
else
- DeltaCurr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLL)); // Between 95% -105%, constant PQ
+ DeltaCurr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLL); // Between 95% -105%, constant PQ
end;
- StickCurrInTerminalArray(ITerminal, Cnegate(DeltaCurr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -DeltaCurr, i); // Put into Terminal array taking into account connection
set_ITerminalUpdated(TRUE);
StickCurrInTerminalArray(InjCurrent, DeltaCurr, i); // Put into Terminal array taking into account connection
end;
@@ -2130,12 +1662,10 @@ procedure TPVsystem2Obj.DoConstantPQPVsystemObj();
end;
end;
-
end;
-// ===========================================================================================
procedure TPVsystem2Obj.DoConstantZPVsystemObj;
-{constant Z model}
+// constant Z model
var
i: Integer;
Curr,
@@ -2143,7 +1673,6 @@ procedure TPVsystem2Obj.DoConstantZPVsystemObj;
V012: array[0..2] of Complex; // Sequence voltages
begin
-
// Assume YEQ is kept up to date
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase(); // get actual voltage across each phase of the load
@@ -2161,27 +1690,23 @@ procedure TPVsystem2Obj.DoConstantZPVsystemObj;
if (Connection = 0) then
YEQ2 := YEQ // YEQ is always line to neutral
else
- YEQ2 := CdivReal(YEQ, 3.0); // YEQ for delta connection
+ YEQ2 := YEQ / 3.0; // YEQ for delta connection
for i := 1 to Fnphases do
begin
- Curr := Cmul(YEQ2, Vterminal^[i]);
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ Curr := YEQ2 * Vterminal^[i];
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
set_ITerminalUpdated(TRUE);
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
-
end;
-
-// =================================================================DOUSERMODEL==========================
procedure TPVsystem2Obj.DoUserModel;
-{Compute total terminal Current from User-written model}
+// Compute total terminal Current from User-written model
var
i: Integer;
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
if UserModel.Exists then // Check automatically selects the usermodel If true
@@ -2191,17 +1716,15 @@ procedure TPVsystem2Obj.DoUserModel;
with ActiveCircuit.Solution do
begin // Negate currents from user model for power flow PVSystem element model
for i := 1 to FnConds do
- Caccum(InjCurrent^[i], Cnegate(Iterminal^[i]));
+ InjCurrent^[i] -= Iterminal^[i];
end;
end
else
- DoSimpleMsg('PVSystem.' + name + ' model designated to use user-written model, but user-written model is not defined.', 567);
-
+ DoSimpleMsg('%s model designated to use user-written model, but user-written model is not defined.', [FullName], 567);
end;
-// ===============================================================DoDynamicMode============================
procedure TPVsystem2Obj.DoDynamicMode;
-{Compute Total Current and add into InjTemp}
+// Compute Total Current and add into InjTemp
var
i: Integer;
V012,
@@ -2209,19 +1732,16 @@ procedure TPVsystem2Obj.DoDynamicMode;
Vthev: Complex;
Theta: Double; // phase angle of thevinen source
- {-------------- Internal Proc -----------------------}
+ // -------------- Internal Proc -----------------------
procedure CalcVthev_Dyn(const V: Complex);
- {
- If the voltage magnitude drops below 15% or so, the accuracy of determining the
- phase angle gets flaky. This algorithm approximates the action of a PLL that will
- hold the last phase angle until the voltage recovers.
- }
+ // If the voltage magnitude drops below 15% or so, the accuracy of determining the
+ // phase angle gets flaky. This algorithm approximates the action of a PLL that will
+ // hold the last phase angle until the voltage recovers.
begin
- {Try to keep in phase with terminal voltage}
+ // Try to keep in phase with terminal voltage
with PVSystemVars do
begin
-
if Cabs(V) > 0.20 * Vbase then
Theta := ThetaDyn + (Cang(V) - InitialVangle)
else
@@ -2233,44 +1753,41 @@ procedure TPVsystem2Obj.DoDynamicMode;
end;
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array and computes VTerminal
- {Inj = -Itotal (in) - Yprim*Vtemp}
+ // Inj = -Itotal (in) - Yprim*Vtemp
case VoltageModel of
3:
if UserModel.Exists then // auto selects model
- begin {We have total currents in Iterminal}
+ begin // We have total currents in Iterminal
UserModel.FCalc(Vterminal, Iterminal); // returns terminal currents in Iterminal
end
else
begin
- DoSimpleMsg(Format('Dynamics model missing for PVSystem.%s ', [Name]), 5671);
+ DoSimpleMsg('Dynamics model missing for %s ', [FullName], 5671);
DSS.SolutionAbort := TRUE;
end;
- else {All other models -- current-limited like Generator Model 7}
- {
- This is a simple model that is basically a thevinen equivalent without inertia
- }
+ else // All other models -- current-limited like Generator Model 7
+ // This is a simple model that is basically a thevinen equivalent without inertia
- case Fnphases of {No user model, use default Thevinen equivalent for standard Generator model}
+ case Fnphases of // No user model, use default Thevinen equivalent for standard Generator model
1:
with PVSystemVars do
begin
// 1-phase generators have 2 conductors
// Assume inverter stays in phase with terminal voltage
- CalcVthev_Dyn(CSub(VTerminal^[1], VTerminal^[2])); // see internal proc above
+ CalcVthev_Dyn(VTerminal^[1] - VTerminal^[2]); // see internal proc above
- ITerminal^[1] := CDiv(CSub(Csub(VTerminal^[1], Vthev), VTerminal^[2]), Zthev);
+ ITerminal^[1] := (VTerminal^[1] - Vthev - VTerminal^[2]) / Zthev;
if CurrentLimited then
if Cabs(Iterminal^[1]) > MaxDynPhaseCurrent then // Limit the current but keep phase angle
ITerminal^[1] := ptocomplex(topolar(MaxDynPhaseCurrent, cang(Iterminal^[1])));
- ITerminal^[2] := Cnegate(ITerminal^[1]);
+ ITerminal^[2] := -ITerminal^[1];
end;
3:
@@ -2284,7 +1801,7 @@ procedure TPVsystem2Obj.DoDynamicMode;
CalcVthev_Dyn(V012[1]);
// Positive Sequence Contribution to Iterminal
- I012[1] := CDiv(Csub(V012[1], Vthev), Zthev);
+ I012[1] := (V012[1] - Vthev) / Zthev;
if CurrentLimited and (Cabs(I012[1]) > MaxDynPhaseCurrent) then // Limit the pos seq current but keep phase angle
I012[1] := ptocomplex(topolar(MaxDynPhaseCurrent, cang(I012[1])));
@@ -2294,25 +1811,25 @@ procedure TPVsystem2Obj.DoDynamicMode;
I012[2] := CZERO;
end
else
- I012[2] := Cdiv(V012[2], Zthev); // for inverter
+ I012[2] := V012[2] / Zthev; // for inverter
//End; //HERE
- {Adjust for generator connection}
+ // Adjust for generator connection
if (Connection = 1) or ForceBalanced then
I012[0] := CZERO
else
- I012[0] := Cdiv(V012[0], Zthev);
+ I012[0] := V012[0] / Zthev;
SymComp2Phase(ITerminal, pComplexArray(@I012)); // Convert back to phase components
// Neutral current
if Connection = 0 then
- ITerminal^[FnConds] := Cnegate(CmulReal(I012[0], 3.0));
+ ITerminal^[FnConds] := -I012[0] * 3.0;
end;
else
- DoSimpleMsg(Format('Dynamics mode is implemented only for 1- or 3-phase Generators. Generator.%s has %d phases.', [name, Fnphases]), 5671);
+ DoSimpleMsg('Dynamics mode is implemented only for 1- or 3-phase Generators. %s has %d phases.', [FullName, Fnphases], 5671);
DSS.SolutionAbort := TRUE;
end;
@@ -2320,61 +1837,58 @@ procedure TPVsystem2Obj.DoDynamicMode;
set_ITerminalUpdated(TRUE);
- {Add it into inj current array}
+ // Add it into inj current array
for i := 1 to FnConds do
- Caccum(InjCurrent^[i], Cnegate(Iterminal^[i]));
+ InjCurrent^[i] -= Iterminal^[i];
end;
-// ====================================================================DoHarmonicMode=======================
procedure TPVsystem2Obj.DoHarmonicMode();
+// Compute Injection Current Only when in harmonics mode
-{Compute Injection Current Only when in harmonics mode}
-
-{Assumes spectrum is a voltage source behind subtransient reactance and YPrim has been built}
-{Vd is the fundamental frequency voltage behind Xd" for phase 1}
-
+// Assumes spectrum is a voltage source behind subtransient reactance and YPrim has been built
+// Vd is the fundamental frequency voltage behind Xd" for phase 1
var
i: Integer;
E: Complex;
PVSystemHarmonic: Double;
-
+ pBuffer: PCBuffer24;
begin
+ pBuffer := @TPVsystem2(ParentClass).cBuffer;
+
ComputeVterminal();
with ActiveCircuit.Solution, PVSystemVars do
begin
PVSystemHarmonic := Frequency / PVSystemFundamental;
if SpectrumObj <> NIL then
- E := CmulReal(SpectrumObj.GetMult(PVSystemHarmonic), VThevHarm) // Get base harmonic magnitude
+ E := SpectrumObj.GetMult(PVSystemHarmonic) * VThevHarm // Get base harmonic magnitude
else
E := CZERO;
RotatePhasorRad(E, PVSystemHarmonic, ThetaHarm); // Time shift by fundamental frequency phase shift
for i := 1 to Fnphases do
begin
- cBuffer[i] := E;
+ pBuffer[i] := E;
if i < Fnphases then
RotatePhasorDeg(E, PVSystemHarmonic, -120.0); // Assume 3-phase PVSystem element
end;
end;
- {Handle Wye Connection}
+ // Handle Wye Connection
if Connection = 0 then
- cbuffer[Fnconds] := Vterminal^[Fnconds]; // assume no neutral injection voltage
+ pBuffer[Fnconds] := Vterminal^[Fnconds]; // assume no neutral injection voltage
- {Inj currents = Yprim (E) }
- YPrim.MVMult(InjCurrent, pComplexArray(@cBuffer));
+ // Inj currents = Yprim (E)
+ YPrim.MVMult(InjCurrent, pComplexArray(pBuffer));
end;
-// ===========================================================================================
procedure TPVsystem2Obj.CalcVTerminalPhase();
var
i, j: Integer;
begin
-
- { Establish phase voltages and stick in Vterminal}
+ // Establish phase voltages and stick in Vterminal
case Connection of
0:
@@ -2398,26 +1912,11 @@ procedure TPVsystem2Obj.CalcVTerminalPhase();
end;
PVSystemSolutionCount := ActiveCircuit.Solution.SolutionCount;
-
end;
-// ===========================================================================================
-(*
-PROCEDURE TPVsystem2Obj.CalcVTerminal;
-{Put terminal voltages in an array}
-Begin
- ComputeVTerminal;
- PVSystemSolutionCount := ActiveCircuit.Solution.SolutionCount;
-End;
-*)
-
-
-// ============================================CalcPVSystemModelContribution===============================================
procedure TPVsystem2Obj.CalcPVSystemModelContribution();
-
// Calculates PVSystem element current and adds it properly into the injcurrent array
// routines may also compute ITerminal (ITerminalUpdated flag)
-
begin
set_ITerminalUpdated(FALSE);
with ActiveCircuit, ActiveCircuit.Solution do
@@ -2441,15 +1940,14 @@ procedure TPVsystem2Obj.CalcPVSystemModelContribution();
else
DoConstantPQPVsystemObj(); // for now, until we implement the other models.
end;
- end; {ELSE}
- end; {WITH}
+ end;
+ end;
- {When this is Done, ITerminal is up to date}
+ // When this is Done, ITerminal is up to date
end;
-// ==========================================CalcInjCurrentArray=================================================
procedure TPVsystem2Obj.CalcInjCurrentArray();
- // Difference between currents in YPrim and total current
+// Difference between currents in YPrim and total current
begin
// Now Get Injection Currents
if PVsystemObjSwitchOpen then
@@ -2458,10 +1956,8 @@ procedure TPVsystem2Obj.CalcInjCurrentArray();
CalcPVSystemModelContribution();
end;
-// =========================================GetTerminalCurrents==================================================
procedure TPVsystem2Obj.GetTerminalCurrents(Curr: pComplexArray);
// Compute total Currents
-
begin
with ActiveCircuit.Solution do
begin
@@ -2475,12 +1971,9 @@ procedure TPVsystem2Obj.GetTerminalCurrents(Curr: pComplexArray);
if (DebugTrace) then
WriteTraceRecord('TotalCurrent');
-
end;
-// ===========================================INJCURRENTS================================================
function TPVsystem2Obj.InjCurrents(): Integer;
-
begin
with ActiveCircuit.Solution do
begin
@@ -2498,12 +1991,9 @@ function TPVsystem2Obj.InjCurrents(): Integer;
end;
end;
-// ===========================================================================================
procedure TPVsystem2Obj.ResetRegisters;
-
var
i: Integer;
-
begin
for i := 1 to NumPVSystem2Registers do
Registers[i] := 0.0;
@@ -2512,32 +2002,26 @@ procedure TPVsystem2Obj.ResetRegisters;
FirstSampleAfterReset := TRUE; // initialize for trapezoidal integration
end;
-// ===========================================================================================
procedure TPVsystem2Obj.Integrate(Reg: Integer; const Deriv: Double; const Interval: Double);
-
begin
if ActiveCircuit.TrapezoidalIntegration then
begin
- {Trapezoidal Rule Integration}
+ // Trapezoidal Rule Integration
if not FirstSampleAfterReset then
Registers[Reg] := Registers[Reg] + 0.5 * Interval * (Deriv + Derivatives[Reg]);
end
- else {Plain Euler integration}
+ else // Plain Euler integration
Registers[Reg] := Registers[Reg] + Interval * Deriv;
Derivatives[Reg] := Deriv;
end;
-// ===========================================================================================
procedure TPVsystem2Obj.TakeSample();
// Update Energy from metered zone
-
var
S: Complex;
Smag: Double;
HourValue: Double;
-
begin
-
// Compute energy in PVSystem element branch
if Enabled then
begin
@@ -2545,12 +2029,11 @@ procedure TPVsystem2Obj.TakeSample();
Smag := Cabs(S);
HourValue := 1.0;
-
with ActiveCircuit.Solution do
begin
if ActiveCircuit.PositiveSequence then
begin
- S := CmulReal(S, 3.0);
+ S := S * 3;
Smag := 3.0 * Smag;
end;
Integrate(Reg_kWh, S.re, IntervalHrs); // Accumulate the power
@@ -2565,11 +2048,9 @@ procedure TPVsystem2Obj.TakeSample();
end;
procedure TPVsystem2Obj.UpdatePVSystem;
-{Update PVSystem levels}
+// Update PVSystem levels
begin
-
- { Do Nothing}
-
+ // Do Nothing
end;
function TPVsystem2Obj.Get_PresentkW: Double;
@@ -2587,43 +2068,15 @@ function TPVsystem2Obj.Get_Presentkvar: Double;
Result := Qnominalperphase * 0.001 * Fnphases;
end;
-procedure TPVsystem2Obj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i, idx: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- begin // HERE
- for i := 1 to NumProperties do
- begin
- idx := PropertyIdxMap[i];
- case idx of
- propUSERDATA:
- FSWriteln(F, '~ ' + PropertyName^[i] + '=(' + PropertyValue[idx] + ')')
- else
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[idx]);
- end;
- end;
- end;
-
- FSWriteln(F);
-end;
-
-
-// ============================================================InitHarmonics===============================
procedure TPVsystem2Obj.InitHarmonics();
// This routine makes a thevenin equivalent behis the reactance spec'd in %R and %X
var
E, Va: complex;
-
begin
YprimInvalid := TRUE; // Force rebuild of YPrims
PVSystemFundamental := ActiveCircuit.Solution.Frequency; // Whatever the frequency is when we enter here.
- {Compute reference Thevinen voltage from phase 1 current}
+ // Compute reference Thevinen voltage from phase 1 current
ComputeIterminal(); // Get present value of current
@@ -2631,12 +2084,12 @@ procedure TPVsystem2Obj.InitHarmonics();
begin
case Connection of
0:
- begin {wye - neutral is explicit}
- Va := Csub(NodeV^[NodeRef^[1]], NodeV^[NodeRef^[Fnconds]]);
+ begin // wye - neutral is explicit
+ Va := NodeV^[NodeRef^[1]] - NodeV^[NodeRef^[Fnconds]];
end;
1:
- begin {delta -- assume neutral is at zero}
+ begin // delta -- assume neutral is at zero
Va := NodeV^[NodeRef^[1]];
end;
end;
@@ -2645,17 +2098,13 @@ procedure TPVsystem2Obj.InitHarmonics();
with PVSystemVars do
begin
YEQ := Cinv(Cmplx(RThev, XThev)); // used for current calcs Always L-N
- E := Csub(Va, Cmul(Iterminal^[1], cmplx(Rthev, Xthev)));
+ E := Va - Iterminal^[1] * cmplx(Rthev, Xthev);
Vthevharm := Cabs(E); // establish base mag and angle
ThetaHarm := Cang(E);
end;
-
end;
-
-// ===============================================================InitStateVars============================
procedure TPVsystem2Obj.InitStateVars();
-
// for going into dynamics mode
var
// VNeut,
@@ -2685,9 +2134,9 @@ procedure TPVsystem2Obj.InitStateVars();
1:
begin
- V12 := CSub(NodeV^[NodeRef^[1]], NodeV^[NodeRef^[2]]);
+ V12 := NodeV^[NodeRef^[1]] - NodeV^[NodeRef^[2]];
InitialVAngle := Cang(V12);
- Edp := Csub(V12, Cmul(ITerminal^[1], Zthev));
+ Edp := V12 - ITerminal^[1] * Zthev;
VthevmagDyn := Cabs(Edp);
ThetaDyn := Cang(Edp); // initial thev equivalent phase angle
end;
@@ -2702,12 +2151,12 @@ procedure TPVsystem2Obj.InitStateVars();
Vabc[i] := NodeV^[NodeRef^[i]]; // Wye Voltage
Phase2SymComp(pComplexArray(@Vabc), pComplexArray(@V012));
InitialVAngle := Cang(V012[1]);
- Edp := Csub(V012[1], Cmul(I012[1], Zthev)); // Pos sequence
+ Edp := V012[1] - I012[1] * Zthev; // Pos sequence
VthevmagDyn := Cabs(Edp);
ThetaDyn := Cang(Edp); // initial thev equivalent phase angle
end;
else
- DoSimpleMsg(Format('Dynamics mode is implemented only for 1- or 3-phase Generators. PVSystem.' + name + ' has %d phases.', [Fnphases]), 5673);
+ DoSimpleMsg('Dynamics mode is implemented only for 1- or 3-phase Generators. %s has %d phases.', [FullName, Fnphases], 5673);
DSS.SolutionAbort := TRUE;
end;
@@ -2715,14 +2164,8 @@ procedure TPVsystem2Obj.InitStateVars();
end;
end;
-// ===========================================================================================
procedure TPVsystem2Obj.IntegrateStates();
-
// dynamics mode integration routine
-
-// VAR
-// TracePower:Complex;
-
begin
// Compute Derivatives and Then integrate
ComputeIterminal();
@@ -2730,19 +2173,16 @@ procedure TPVsystem2Obj.IntegrateStates();
if Usermodel.Exists then
Usermodel.Integrate // Checks for existence and Selects
else
- with ActiveCircuit.Solution {, StorageVars} do
+ with ActiveCircuit.Solution do
begin
end;
end;
-// ===========================================================Get_Variable================================
function TPVsystem2Obj.Get_Variable(i: Integer): Double;
-{Return variables one at a time}
-
+// Return variables one at a time
var
N, k: Integer;
-
begin
Result := -9999.99; // error return value; no state fars
@@ -2778,7 +2218,6 @@ function TPVsystem2Obj.Get_Variable(i: Integer): Double;
Result := WVOperation;
13:
Result := PanelkW * EffFactor;
-
else
begin
if UserModel.Exists then
@@ -2795,13 +2234,10 @@ function TPVsystem2Obj.Get_Variable(i: Integer): Double;
end;
end;
-// ============================================================kWOut_Calc===============================
procedure TPVsystem2Obj.kWOut_Calc;
-
var
Pac: Double;
PpctLimit: Double;
-
begin
with PVSystemVars do
begin
@@ -2825,12 +2261,9 @@ procedure TPVsystem2Obj.kWOut_Calc;
end;
end;
-// ============================================================Set_Variable===============================
procedure TPVsystem2Obj.Set_Variable(i: Integer; Value: Double);
-
var
N, k: Integer;
-
begin
if i < 1 then
Exit; // No variables to set
@@ -2861,27 +2294,24 @@ procedure TPVsystem2Obj.Set_Variable(i: Integer; Value: Double);
13: ; //ReadOnly //kW_out_desired := Value;
else
- begin
- if UserModel.Exists then
begin
- N := UserModel.FNumVars;
- k := (i - NumPVSystem2Variables);
- if k <= N then
+ if UserModel.Exists then
begin
- UserModel.FSetVariable(k, Value);
- Exit;
+ N := UserModel.FNumVars;
+ k := (i - NumPVSystem2Variables);
+ if k <= N then
+ begin
+ UserModel.FSetVariable(k, Value);
+ Exit;
+ end;
end;
end;
end;
- end;
-
end;
-// ===========================================================================================
procedure TPVsystem2Obj.GetAllVariables(States: pDoubleArray);
-
var
- i{, N}: Integer;
+ i: Integer;
begin
for i := 1 to NumPVSystem2Variables do
States^[i] := Variable[i];
@@ -2890,7 +2320,6 @@ procedure TPVsystem2Obj.GetAllVariables(States: pDoubleArray);
UserModel.FGetAllVars(pDoubleArray(@States^[NumPVSystem2Variables + 1]));
end;
-// ===========================================================================================
function TPVsystem2Obj.NumVariables: Integer;
begin
Result := NumPVSystem2Variables;
@@ -2898,18 +2327,14 @@ function TPVsystem2Obj.NumVariables: Integer;
Result := Result + UserModel.FNumVars;
end;
-// ===========================================================================================
function TPVsystem2Obj.VariableName(i: Integer): String;
-
const
BuffSize = 255;
-
var
n,
i2: Integer;
Buff: array[0..BuffSize] of AnsiChar;
pName: pAnsichar;
-
begin
if i < 1 then
Exit; // Someone goofed
@@ -2941,57 +2366,60 @@ function TPVsystem2Obj.VariableName(i: Integer): String;
Result := 'watt-var';
13:
Result := 'kW_out_desired'
-
else
- begin
- if UserModel.Exists then
begin
- pName := PAnsiChar(@Buff);
- n := UserModel.FNumVars;
- i2 := i - NumPVSystem2Variables;
- if (i2 <= n) then
+ if UserModel.Exists then
begin
- UserModel.FGetVarName(i2, pName, BuffSize);
- Result := String(pName);
- Exit;
+ pName := PAnsiChar(@Buff);
+ n := UserModel.FNumVars;
+ i2 := i - NumPVSystem2Variables;
+ if (i2 <= n) then
+ begin
+ UserModel.FGetVarName(i2, pName, BuffSize);
+ Result := String(pName);
+ Exit;
+ end;
end;
end;
end;
- end;
end;
-// ===========================================================================================
procedure TPVsystem2Obj.MakePosSequence();
-
var
- S: String;
- V: Double;
-
+ newkVA, newPF, V: Double;
+ oldPhases, changes: Integer;
begin
-
- S := 'Phases=1 conn=wye';
-
with PVSystemVars do
begin
+ BeginEdit(True);
// Make sure voltage is line-neutral
if (Fnphases > 1) or (connection <> 0) then
V := kVPVSystemBase / SQRT3
else
V := kVPVSystemBase;
- S := S + Format(' kV=%-.5g', [V]);
-
- if (Fnphases > 1) then
- S := S + Format(' kva=%-.5g PF=%-.5g', [FkVArating / Fnphases, PFnominal]);
-
- Parser.CmdString := S;
- Edit();
+ oldPhases := FnPhases;
+ changes := 3;
+ if oldPhases > 1 then
+ begin
+ newkVA := kVArating / Fnphases;
+ newPF := PFNominal;
+ changes := changes + 2;
+ end;
+ SetInteger(ord(TProp.Phases), 1);
+ SetInteger(ord(TProp.conn), 0);
+ SetDouble(ord(TProp.kV), V);
+ if oldPhases > 1 then
+ begin
+ SetDouble(ord(TProp.kVA), newkVA);
+ SetDouble(ord(TProp.PF), newPF);
+ end;
+ EndEdit(changes);
end;
inherited; // write out other properties
end;
-// ===========================================================================================
procedure TPVsystem2Obj.Set_ConductorClosed(Index: Integer; Value: Boolean);
begin
inherited;
@@ -3003,26 +2431,25 @@ procedure TPVsystem2Obj.Set_ConductorClosed(Index: Integer; Value: Boolean);
procedure TPVsystem2Obj.Set_Maxkvar(const Value: Double);
begin
PVSystemVars.Fkvarlimit := Value;
- PropertyValue[propkvarLimit] := Format('%-g', [PVSystemVars.Fkvarlimit]);
+ SetAsNextSeq(ord(TProp.kvarMax));
end;
procedure TPVsystem2Obj.Set_Maxkvarneg(const Value: Double);
begin
PVSystemVars.Fkvarlimitneg := Value;
- PropertyValue[propkvarLimitneg] := Format('%-g', [PVSystemVars.Fkvarlimitneg]);
+ SetAsNextSeq(ord(TProp.kvarMaxAbs));
end;
procedure TPVsystem2Obj.Set_kVARating(const Value: Double);
begin
PVSystemVars.FkVARating := Value;
- PropertyValue[propKVA] := Format('%-g', [PVSystemVars.FkVArating]);
+ SetAsNextSeq(ord(TProp.kVA));
end;
-// ===========================================================================================
procedure TPVsystem2Obj.Set_Pmpp(const Value: Double);
begin
PVSystemVars.FPmpp := Value;
- PropertyValue[propPmpp] := Format('%-g', [PVSystemVars.FkVArating]);
+ SetAsNextSeq(ord(TProp.Pmpp));
end;
procedure TPVsystem2Obj.Set_PowerFactor(const Value: Double);
@@ -3031,20 +2458,6 @@ procedure TPVsystem2Obj.Set_PowerFactor(const Value: Double);
varMode := VARMODEPF;
end;
-procedure TPVsystem2Obj.Set_PresentkV(const Value: Double);
-begin
- with PVSystemVars do
- begin
- kVPVSystemBase := Value;
- case FNphases of
- 2, 3:
- VBase := kVPVSystemBase * InvSQRT3x1000;
- else
- VBase := kVPVSystemBase * 1000.0;
- end;
- end;
-end;
-
procedure TPVsystem2Obj.Set_pf_wp_nominal(const Value: Double);
begin
Fpf_wp_nominal := Value;
@@ -3056,8 +2469,9 @@ procedure TPVsystem2Obj.SetDragHandRegister(Reg: Integer; const Value: Double);
Registers[Reg] := Value;
end;
-initialization
-
- CDOUBLEONE := Cmplx(1.0, 1.0);
+function TPVSystem2Obj.UsingCIMDynamics(): Boolean;
+begin
+ Result := VWMode or VVMode or WVMode or AVRMode or DRCMode; // FWPMode not in CIM Dynamics
+end;
-end.
+end.
\ No newline at end of file
diff --git a/src/PCElements/Storage.pas b/src/PCElements/Storage.pas
index 565db3305..6db25bfff 100644
--- a/src/PCElements/Storage.pas
+++ b/src/PCElements/Storage.pas
@@ -6,47 +6,83 @@
All rights reserved.
----------------------------------------------------------
}
-
-{ Change Log
-
- 10/04/2009 Created from Generator Model
-
-
- To Do:
- Make connection to User model
- Yprim for various modes
- Define state vars and dynamics mode behavior
- Complete Harmonics mode algorithm (generator mode is implemented)
-}
-{
- The storage element is essentially a generator that can be dispatched
- to either produce power or consume power commensurate with rating and
- amount of stored energy.
-
- The storage element can also produce or absorb vars within the kVA rating of the inverter.
- That is, a StorageController object requests kvar and the storage element provides them if
- it has any capacity left. The storage element can produce/absorb kvar while idling.
-}
+// To Do:
+// Make connection to User model
+// Yprim for various modes
+// Define state vars and dynamics mode behavior
+// Complete Harmonics mode algorithm (generator mode is implemented)
+
+// The storage element is essentially a generator that can be dispatched
+// to either produce power or consume power commensurate with rating and
+// amount of stored energy.
+//
+// The storage element can also produce or absorb vars within the kVA rating of the inverter.
+// That is, a StorageController object requests kvar and the storage element provides them if
+// it has any capacity left. The storage element can produce/absorb kvar while idling.
// The Storage element is assumed balanced over the no. of phases defined
-
interface
uses
Classes,
- StorageVars,
- StoreUserModel,
DSSClass,
PCClass,
PCElement,
ucmatrix,
- ucomplex,
+ UComplex, DSSUcomplex,
LoadShape,
Spectrum,
ArrayDef,
Dynamics;
+type
+{$SCOPEDENUMS ON}
+ TStorageProp = (
+ INVALID = 0,
+ phases = 1,
+ bus1 = 2,
+ kv = 3, // propKV
+ kW = 4, // propKW
+ pf = 5, // propPF
+ conn = 6, // propCONNECTION
+ kvar = 7, // propKVAR
+ kVA = 8, // propKVA
+ kWrated = 9, // propKWRATED
+ kWhrated = 10, // propKWHRATED
+ kWhstored = 11, // propKWHSTORED
+ pctstored = 12, // propPCTSTORED
+ pctreserve = 13, // propPCTRESERVE
+ State = 14, // propSTATE
+ pctDischarge = 15, // propPCTKWOUT
+ pctCharge = 16, // propPCTKWIN
+ pctEffCharge = 17, // propCHARGEEFF
+ pctEffDischarge = 18, // propDISCHARGEEFF
+ pctIdlingkW = 19, // propIDLEKW
+ pctIdlingkvar = 20, // propIDLEKVAR
+ pctR = 21, // propPCTR
+ pctX = 22, // propPCTX
+ model = 23, // propMODEL
+ Vminpu = 24, // propVMINPU
+ Vmaxpu = 25, // propVMAXPU
+ Balanced = 26, // propBalanced
+ LimitCurrent = 27, // propLimited
+ yearly = 28, // propYEARLY
+ daily = 29, // propDAILY
+ duty = 30, // propDUTY
+ DispMode = 31, // propDISPMODE
+ DischargeTrigger = 32, // propDISPOUTTRIG
+ ChargeTrigger = 33, // propDISPINTRIG
+ TimeChargeTrig = 34, // propCHARGETIME
+ cls = 35, // propCLASS
+ DynaDLL = 36, // propDynaDLL
+ DynaData = 37, // propDynaData
+ UserModel = 38, // propUSERMODEL
+ UserData = 39, // propUSERDATA
+ debugtrace = 40 // propDEBUGTRACE
+ );
+{$SCOPEDENUMS OFF}
+
const
NumStorageRegisters = 6; // Number of energy meter registers
NumStorageVariables = 7; // No state variables
@@ -65,33 +101,56 @@ interface
STORE_FOLLOW = 4;
type
+ // Struct that was passed to user-written DLLs (disabled in v0.12 for legacy models)
+ TStorageVars = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}packed{$ENDIF} record
+
+ kWrating: Double;
+ kWhRating: Double;
+ kWhStored: Double;
+ kWhReserve: Double;
+ ChargeEff: Double;
+ DisChargeEff: Double;
+ kVArating: Double; // --> kVArating
+ kVStorageBase: Double;
+ kvarRequested: Double;
+ RThev: Double;
+ XThev: Double;
+ // Dynamics variables
+ Vthev: Complex; {Thevenin equivalent voltage (complex) for dynamic model}
+ ZThev: Complex;
+ Vthevharm: Double; {Thevenin equivalent voltage mag and angle reference for Harmonic model}
+ Thetaharm: Double; {Thevenin equivalent voltage mag and angle reference for Harmonic model}
+ VthevMag: Double; {Thevenin equivalent voltage for dynamic model}
+ Theta: Double; {Power angle between voltage and current}
+ w_grid: Double; {Grid frequency}
+ TotalLosses: Double;
+ IdlingLosses: Double;
+
+ // 32-bit integers
+ NumPhases, {Number of phases}
+ NumConductors, {Total Number of conductors (wye-connected will have 4)}
+ Conn: Integer; // 0 = wye; 1 = Delta
+ end;
-
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TStorage = class(TPCClass)
- PRIVATE
-
- procedure InterpretConnection(const S: String);
- procedure SetNcondsForConnection;
PROTECTED
- procedure DefineProperties;
- function MakeLike(const OtherStorageObjName: String): Integer; OVERRIDE;
+ cBuffer: TCBuffer24; // Temp buffer for calcs 24-phase Storage element?
+
+ procedure DefineProperties; override;
PUBLIC
RegisterNames: array[1..NumStorageRegisters] of String;
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
procedure ResetRegistersAll;
procedure SampleAll;
procedure UpdateAll;
-
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TStorageObj = class(TPCElement)
PRIVATE
Yeq: Complex; // at nominal
@@ -102,16 +161,16 @@ TStorageObj = class(TPCElement)
PhaseCurrentLimit: Complex;
MaxDynPhaseCurrent: Double;
- DebugTrace: Boolean;
+ DebugTrace: LongBool;
FState: Integer;
FStateChanged: Boolean;
FirstSampleAfterReset: Boolean;
StorageSolutionCount: Integer;
- StorageFundamental: Double; {Thevinen equivalent voltage mag and angle reference for Harmonic model}
+ StorageFundamental: Double; // Thevinen equivalent voltage mag and angle reference for Harmonic model
StorageObjSwitchOpen: Boolean;
- ForceBalanced: Boolean;
- CurrentLimited: Boolean;
+ ForceBalanced: LongBool;
+ CurrentLimited: LongBool;
kVANotSet: Boolean;
kvar_out: Double;
@@ -141,9 +200,10 @@ TStorageObj = class(TPCElement)
Reg_Price: Integer;
ShapeFactor: Complex;
TraceFile: TFileStream;
- IsUserModel: Boolean;
- UserModel: TStoreUserModel; {User-Written Models}
- DynaModel: TStoreDynaModel;
+
+ UserModelNameStr, UserModelEditStr: String;
+ DynaModelNameStr, DynaModelEditStr: String;
+
kvarBase: Double; // Base vars per phase
VBase: Double; // Base volts suitable for computing currents
VBase105: Double;
@@ -157,7 +217,6 @@ TStorageObj = class(TPCElement)
procedure CalcDutyMult(Hr: Double);
procedure CalcStorageModelContribution;
procedure CalcInjCurrentArray;
- (*PROCEDURE CalcVterminal;*)
procedure CalcVTerminalPhase;
procedure CalcYearlyMult(Hr: Double);
procedure CalcYPrimMatrix(Ymatrix: TcMatrix);
@@ -166,8 +225,6 @@ TStorageObj = class(TPCElement)
procedure DoConstantZStorageObj;
procedure DoDynamicMode;
procedure DoHarmonicMode;
- procedure DoUserModel;
- procedure DoDynaModel;
procedure Integrate(Reg: Integer; const Deriv: Double; const Interval: Double);
procedure SetDragHandRegister(Reg: Integer; const Value: Double);
@@ -180,15 +237,10 @@ TStorageObj = class(TPCElement)
procedure CheckStateTriggerLevel(Level: Double);
procedure UpdateStorage; // Update Storage elements based on present kW and IntervalHrs variable
function NormalizeToTOD(h: Integer; sec: Double): Double;
-
- function InterpretState(const S: String): Integer;
-// FUNCTION StateToStr:String;
- function DecodeState: String;
-
function Get_PresentkW: Double;
function Get_Presentkvar: Double;
function Get_PresentkV: Double;
- procedure Set_PresentkV(const Value: Double);
+ procedure Set_PresentkV(const Value: Double); //TODO: remove?
procedure Set_Presentkvar(const Value: Double);
procedure Set_PresentkW(const Value: Double);
procedure Set_PowerFactor(const Value: Double);
@@ -202,17 +254,13 @@ TStorageObj = class(TPCElement)
procedure GetTerminalCurrents(Curr: pComplexArray); OVERRIDE;
PUBLIC
-
- Connection: Integer; {0 = line-neutral; 1=Delta}
- DailyShape: String; // Daily (24 HR) Storage element shape
- DailyShapeObj: TLoadShapeObj; // Daily Storage element Shape for this load
- DutyShape: String; // Duty cycle load shape for changes typically less than one hour
- DutyShapeObj: TLoadShapeObj; // Shape for this Storage element
+ Connection: Integer; // 0 = line-neutral; 1=Delta
+ DailyShapeObj: TLoadShapeObj; // Daily (24 HR) Storage element shape
+ DutyShapeObj: TLoadShapeObj; // Duty cycle load shape for changes typically less than one hour
+ YearlyShapeObj: TLoadShapeObj; // Shape for this Storage element
StorageClass: Integer;
VoltageModel: Integer; // Variation with voltage
PFNominal: Double;
- YearlyShape: String; // ='fixed' means no variation on all the time
- YearlyShapeObj: TLoadShapeObj; // Shape for this Storage element
StorageVars: TStorageVars;
@@ -226,6 +274,8 @@ TStorageObj = class(TPCElement)
constructor Create(ParClass: TDSSClass; const SourceName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure Set_ConductorClosed(Index: Integer; Value: Boolean); OVERRIDE;
procedure RecalcElementData; OVERRIDE;
@@ -251,11 +301,7 @@ TStorageObj = class(TPCElement)
// Support for Harmonics Mode
procedure InitHarmonics; OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
property PresentkW: Double READ Get_PresentkW WRITE Set_PresentkW;
property Presentkvar: Double READ Get_Presentkvar WRITE Set_Presentkvar;
@@ -272,12 +318,10 @@ TStorageObj = class(TPCElement)
property MinModelVoltagePU: Double READ VminPu;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
implementation
-
uses
- ParserDel,
+ BufStream,
Circuit,
Sysutils,
Command,
@@ -290,70 +334,31 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TStorageObj;
+ TProp = TStorageProp;
const
-
-{ = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
- To add a property,
- 1) add a property constant to this list
- 2) add a handler to the CASE statement in the Edit FUNCTION
- 3) add a statement(s) to InitPropertyValues FUNCTION to initialize the string value
- 4) add any special handlers to DumpProperties and GetPropertyValue, If needed
- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =}
-
- propKV = 3;
- propKW = 4;
- propPF = 5;
- propMODEL = 6;
- propYEARLY = 7;
- propDAILY = 8;
- propDUTY = 9;
- propDISPMODE = 10;
- propIDLEKVAR = 11;
- propCONNECTION = 12;
- propKVAR = 13;
- propPCTR = 14;
- propPCTX = 15;
- propIDLEKW = 16;
- propCLASS = 17;
- propDISPOUTTRIG = 18;
- propDISPINTRIG = 19;
- propCHARGEEFF = 20;
- propDISCHARGEEFF = 21;
- propPCTKWOUT = 22;
- propVMINPU = 23;
- propVMAXPU = 24;
- propSTATE = 25;
- propKVA = 26;
- propKWRATED = 27;
- propKWHRATED = 28;
- propKWHSTORED = 29;
- propPCTRESERVE = 30;
- propUSERMODEL = 31;
- propUSERDATA = 32;
- propDEBUGTRACE = 33;
- propPCTKWIN = 34;
- propPCTSTORED = 35;
- propCHARGETIME = 36;
- propDynaDLL = 37;
- propDynaData = 38;
- propBalanced = 39;
- propLimited = 40;
-
- NumPropsThisClass = 40; // Make this agree with the last property constant
-
-
+ NumPropsThisClass = Ord(High(TProp));
var
- cBuffer: array[1..24] of Complex; // Temp buffer for calcs 24-phase Storage element?
- CDOUBLEONE: Complex;
+ PropInfo: Pointer = NIL;
+ DispatchModeEnum, StateEnum: TDSSEnum;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TStorage.Create(dssContext: TDSSContext); // Creates superstructure for all Storage elements
+constructor TStorage.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'Storage';
- DSSClassType := DSSClassType + STORAGE_ELEMENT; // In both PCelement and Storage element list
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ StateEnum := TDSSEnum.Create('LegacyStorage: State', True, 1, 1,
+ ['Charging', 'Idling', 'Discharging'],
+ [-1, 0, 1]);
+ StateEnum.DefaultValue := 0;
+ DispatchModeEnum := TDSSEnum.Create('LegacyStorage: Dispatch Mode', True, 1, 1,
+ ['Default', 'Loadshape', 'Price', 'External', 'Follow'],
+ [0, 1, 2, 3, 4]);
+ DispatchModeEnum.DefaultValue := 0;
+ end;
- ActiveElement := 0;
+ inherited Create(dssContext, STORAGE_ELEMENT, 'Storage');
// Set Register names
RegisterNames[1] := 'kWh';
@@ -362,211 +367,152 @@ constructor TStorage.Create(dssContext: TDSSContext); // Creates superstructure
RegisterNames[4] := 'Max kVA';
RegisterNames[5] := 'Hours';
RegisterNames[6] := 'Price($)';
+end;
- DefineProperties;
+destructor TStorage.Destroy;
+begin
+ inherited Destroy;
+end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+procedure Setkvar(obj: TObj; Value: Double);
+begin
+ obj.Presentkvar := Value;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-destructor TStorage.Destroy;
+function Getkvar(obj: TObj): Double;
+begin
+ Result := obj.kvar_out;
+end;
+procedure SetPctStored(obj: TObj; Value: Double);
begin
- // ElementList and CommandList freed in inherited destroy
- inherited Destroy;
+ obj.StorageVars.kWhStored := Value * 0.01 * obj.StorageVars.kWhRating;
+end;
+function GetPctStored(obj: TObj): Double;
+begin
+ Result := obj.StorageVars.kWhStored / obj.StorageVars.kWhRating * 100.0;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TStorage.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays; {see DSSClass}
-
- // Define Property names
- {
- Using the AddProperty FUNCTION, you can list the properties here in the order you want
- them to appear when properties are accessed sequentially without tags. Syntax:
-
- AddProperty( , , );
-
- }
- AddProperty('phases', 1,
- 'Number of Phases, this Storage element. Power is evenly divided among phases.');
- AddProperty('bus1', 2,
- 'Bus to which the Storage element is connected. May include specific node specification.');
- AddProperty('kv', propKV,
- 'Nominal rated (1.0 per unit) voltage, kV, for Storage element. For 2- and 3-phase Storage elements, specify phase-phase kV. ' +
- 'Otherwise, specify actual kV across each branch of the Storage element. ' + CRLF + CRLF +
- 'If wye (star), specify phase-neutral kV. ' + CRLF + CRLF +
- 'If delta or phase-phase connected, specify phase-phase kV.'); // line-neutral voltage// base voltage
- AddProperty('kW', propKW,
- 'Get/set the present kW value. A positive value denotes power coming OUT of the element, ' +
- 'which is the opposite of a Load element. A negative value indicates the Storage element is in Charging state. ' +
- 'This value is modified internally depending on the dispatch mode. ');
- AddProperty('pf', propPF,
- 'Nominally, the power factor for discharging (acting as a generator). Default is 1.0. ' + CRLF + CRLF +
- 'Setting this property will also set the kvar property.' +
- 'Enter negative for leading powerfactor ' +
- '(when kW and kvar have opposite signs.)' + CRLF + CRLF +
- 'A positive power factor for a generator signifies that the Storage element produces vars ' +
- 'as is typical for a generator. ');
- AddProperty('conn', propCONNECTION,
- '={wye|LN|delta|LL}. Default is wye.');
- AddProperty('kvar', propKVAR,
- 'Get/set the present kvar value. Alternative to specifying the power factor. Side effect: ' +
- ' the power factor value is altered to agree based on present value of kW.');
- AddProperty('kVA', propKVA,
- 'kVA rating of power output. Defaults to rated kW. Used as the base for Dynamics mode and Harmonics mode values.');
- AddProperty('kWrated', propKWRATED,
- 'kW rating of power output. Base for Loadshapes when DispMode=Follow. Side effect: Sets KVA property.');
-
- AddProperty('kWhrated', propKWHRATED,
- 'Rated storage capacity in kWh. Default is 50.');
- AddProperty('kWhstored', propKWHSTORED,
- 'Present amount of energy stored, kWh. Default is same as kWh rated.');
- AddProperty('%stored', propPCTSTORED,
- 'Present amount of energy stored, % of rated kWh. Default is 100%.');
- AddProperty('%reserve', propPCTRESERVE,
- 'Percent of rated kWh storage capacity to be held in reserve for normal operation. Default = 20. ' + CRLF +
- 'This is treated as the minimum energy discharge level unless there is an emergency. For emergency operation ' +
- 'set this property lower. Cannot be less than zero.');
- AddProperty('State', propSTATE,
- '{IDLING | CHARGING | DISCHARGING} Get/Set present operational state. In DISCHARGING mode, the Storage element ' +
- 'acts as a generator and the kW property is positive. The element continues discharging at the scheduled output power level ' +
- 'until the storage reaches the reserve value. Then the state reverts to IDLING. ' +
- 'In the CHARGING state, the Storage element behaves like a Load and the kW property is negative. ' +
- 'The element continues to charge until the max storage kWh is reached and Then switches to IDLING state. ' +
- 'In IDLING state, the kW property shows zero. However, the resistive and reactive loss elements remain in the circuit ' +
- 'and the power flow report will show power being consumed.');
- AddProperty('%Discharge', propPCTKWOUT,
- 'Discharge rate (output power) in Percent of rated kW. Default = 100.');
- AddProperty('%Charge', propPCTKWIN,
- 'Charging rate (input power) in Percent of rated kW. Default = 100.');
- AddProperty('%EffCharge', propCHARGEEFF,
- 'Percent efficiency for CHARGING the storage element. Default = 90.');
- AddProperty('%EffDischarge', propDISCHARGEEFF,
- 'Percent efficiency for DISCHARGING the storage element. Default = 90.' +
- 'Idling losses are handled by %IdlingkW property and are in addition to the charging and discharging efficiency losses ' +
- 'in the power conversion process inside the unit.');
- AddProperty('%IdlingkW', propIDLEKW,
- 'Percent of rated kW consumed while idling. Default = 1.');
- AddProperty('%Idlingkvar', propIDLEKVAR,
- 'Percent of rated kW consumed as reactive power (kvar) while idling. Default = 0.');
- AddProperty('%R', propPCTR,
- 'Equivalent percent internal resistance, ohms. Default is 0. Placed in series with internal voltage source' +
- ' for harmonics and dynamics modes. Use a combination of %IdlekW and %EffCharge and %EffDischarge to account for ' +
- 'losses in power flow modes.');
- AddProperty('%X', propPCTX,
- 'Equivalent percent internal reactance, ohms. Default is 50%. Placed in series with internal voltage source' +
- ' for harmonics and dynamics modes. (Limits fault current to 2 pu.) ' +
- 'Use %Idlekvar and kvar properties to account for any reactive power during power flow solutions.');
- AddProperty('model', propMODEL,
- 'Integer code (default=1) for the model to use for powet output variation with voltage. ' +
- 'Valid values are:' + CRLF + CRLF +
- '1:Storage element injects a CONSTANT kW at specified power factor.' + CRLF +
- '2:Storage element is modeled as a CONSTANT ADMITTANCE.' + CRLF +
- '3:Compute load injection from User-written Model.');
-
- AddProperty('Vminpu', propVMINPU,
- 'Default = 0.90. Minimum per unit voltage for which the Model is assumed to apply. ' +
- 'Below this value, the load model reverts to a constant impedance model.');
- AddProperty('Vmaxpu', propVMAXPU,
- 'Default = 1.10. Maximum per unit voltage for which the Model is assumed to apply. ' +
- 'Above this value, the load model reverts to a constant impedance model.');
- AddProperty('Balanced', propBalanced, '{Yes | No*} Default is No. Force balanced current only for 3-phase Storage. Forces zero- and negative-sequence to zero. ');
- AddProperty('LimitCurrent', propLimited, 'Limits current magnitude to Vminpu value for both 1-phase and 3-phase Storage similar to Generator Model 7. For 3-phase, ' +
- 'limits the positive-sequence current but not the negative-sequence.');
- AddProperty('yearly', propYEARLY,
- 'Dispatch shape to use for yearly simulations. Must be previously defined ' +
- 'as a Loadshape object. If this is not specified, the Daily dispatch shape, if any, is repeated ' +
- 'during Yearly solution modes. In the default dispatch mode, ' +
- 'the Storage element uses this loadshape to trigger State changes.');
- AddProperty('daily', propDAILY,
- 'Dispatch shape to use for daily simulations. Must be previously defined ' +
- 'as a Loadshape object of 24 hrs, typically. In the default dispatch mode, ' +
- 'the Storage element uses this loadshape to trigger State changes.'); // daily dispatch (hourly)
- AddProperty('duty', propDUTY,
- 'Load shape to use for duty cycle dispatch simulations such as for solar ramp rate studies. ' +
- 'Must be previously defined as a Loadshape object. ' + CRLF + CRLF +
- 'Typically would have time intervals of 1-5 seconds. ' + CRLF + CRLF +
- 'Designate the number of points to solve using the Set Number=xxxx command. ' +
- 'If there are fewer points in the actual shape, the shape is assumed to repeat.'); // as for wind generation
- AddProperty('DispMode', propDISPMODE,
- '{DEFAULT | FOLLOW | EXTERNAL | LOADLEVEL | PRICE } Default = "DEFAULT". Dispatch mode. ' + CRLF + CRLF +
- 'In DEFAULT mode, Storage element state is triggered to discharge or charge at the specified rate by the ' +
- 'loadshape curve corresponding to the solution mode. ' + CRLF + CRLF +
- 'In FOLLOW mode the kW and kvar output of the STORAGE element follows the active loadshape multipliers ' +
- 'until storage is either exhausted or full. ' +
- 'The element discharges for positive values and charges for negative values. The loadshapes are based on the kW and kvar ' +
- 'values in the most recent definition of kW and PF or kW and kvar properties. ' + CRLF + CRLF +
- 'In EXTERNAL mode, Storage element state is controlled by an external Storage controller. ' +
- 'This mode is automatically set if this Storage element is included in the element list of a StorageController element. ' + CRLF + CRLF +
- 'For the other two dispatch modes, the Storage element state is controlled by either the global default Loadlevel value or the price level. ');
- AddProperty('DischargeTrigger', propDISPOUTTRIG,
- 'Dispatch trigger value for discharging the storage. ' + CRLF +
- 'If = 0.0 the Storage element state is changed by the State command or by a StorageController object. ' + CRLF +
- 'If <> 0 the Storage element state is set to DISCHARGING when this trigger level is EXCEEDED by either the specified ' +
- 'Loadshape curve value or the price signal or global Loadlevel value, depending on dispatch mode. See State property.');
- AddProperty('ChargeTrigger', propDISPINTRIG,
- 'Dispatch trigger value for charging the storage. ' + CRLF + CRLF +
- 'If = 0.0 the Storage element state is changed by the State command or StorageController object. ' + CRLF + CRLF +
- 'If <> 0 the Storage element state is set to CHARGING when this trigger level is GREATER than either the specified ' +
- 'Loadshape curve value or the price signal or global Loadlevel value, depending on dispatch mode. See State property.');
- AddProperty('TimeChargeTrig', propCHARGETIME,
- 'Time of day in fractional hours (0230 = 2.5) at which storage element will automatically go into charge state. ' +
- 'Default is 2.0. Enter a negative time value to disable this feature.');
-
- AddProperty('class', propCLASS,
- 'An arbitrary integer number representing the class of Storage element so that Storage values may ' +
- 'be segregated by class.'); // integer
-
- AddProperty('DynaDLL', propDynaDLL,
- 'Name of DLL containing user-written dynamics model, which computes the terminal currents for Dynamics-mode simulations, ' +
- 'overriding the default model. Set to "none" to negate previous setting. ' +
- 'This DLL has a simpler interface than the UserModel DLL and is only used for Dynamics mode.');
- AddProperty('DynaData', propDYNADATA,
- 'String (in quotes or parentheses if necessary) that gets passed to the user-written dynamics model Edit function for defining the data required for that model.');
- AddProperty('UserModel', propUSERMODEL,
- 'Name of DLL containing user-written model, which computes the terminal currents for both power flow and dynamics, ' +
- 'overriding the default model. Set to "none" to negate previous setting.');
- AddProperty('UserData', propUSERDATA,
- 'String (in quotes or parentheses) that gets passed to user-written model for defining the data required for that model.');
- AddProperty('debugtrace', propDEBUGTRACE,
- '{Yes | No } Default is no. Turn this on to capture the progress of the Storage model ' +
- 'for each iteration. Creates a separate file for each Storage element named "STORAGE_name.CSV".');
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enum properties
+ PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.conn)] := ptruint(@obj.Connection);
+ PropertyOffset2[ord(TProp.conn)] := PtrInt(DSS.ConnectionEnum);
+
+ PropertyType[ord(TProp.State)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.State)] := ptruint(@obj.FState);
+ PropertyOffset2[ord(TProp.State)] := PtrInt(StateEnum);
+
+ PropertyType[ord(TProp.DispMode)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.DispMode)] := ptruint(@obj.DispatchMode);
+ PropertyOffset2[ord(TProp.DispMode)] := PtrInt(DispatchModeEnum);
+
+ // strings
+ PropertyType[ord(TProp.UserModel)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.UserModel)] := ptruint(@obj.UserModelNameStr);
+ PropertyType[ord(TProp.UserData)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.UserData)] := ptruint(@obj.UserModelEditStr);
+
+ PropertyType[ord(TProp.DynaDLL)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.DynaDLL)] := ptruint(@obj.DynaModelNameStr);
+ PropertyType[ord(TProp.DynaData)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.DynaData)] := ptruint(@obj.DynaModelEditStr);
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+
+ // boolean properties
+ PropertyType[ord(TProp.Balanced)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.LimitCurrent)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.debugtrace)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.LimitCurrent)] := ptruint(@obj.CurrentLimited);
+ PropertyOffset[ord(TProp.Balanced)] := ptruint(@obj.ForceBalanced);
+ PropertyOffset[ord(TProp.debugtrace)] := ptruint(@obj.DebugTrace);
+
+ // integer properties
+ PropertyType[ord(TProp.cls)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.model)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.cls)] := ptruint(@obj.StorageClass);
+ PropertyOffset[ord(TProp.model)] := ptruint(@obj.VoltageModel);
+
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // object properties
+ PropertyType[ord(TProp.yearly)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.daily)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.duty)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.yearly)] := ptruint(@obj.YearlyShapeObj);
+ PropertyOffset[ord(TProp.daily)] := ptruint(@obj.DailyShapeObj);
+ PropertyOffset[ord(TProp.duty)] := ptruint(@obj.DutyShapeObj);
+ PropertyOffset2[ord(TProp.yearly)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.daily)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.duty)] := ptruint(DSS.LoadShapeClass);
+
+ // doubles
+ PropertyOffset[ord(TProp.pf)] := ptruint(@obj.PFNominal);
+ PropertyOffset[ord(TProp.pctIdlingkvar)] := ptruint(@obj.pctIdlekvar);
+ PropertyOffset[ord(TProp.pctR)] := ptruint(@obj.pctR);
+ PropertyOffset[ord(TProp.pctX)] := ptruint(@obj.pctX);
+ PropertyOffset[ord(TProp.pctIdlingkW)] := ptruint(@obj.pctIdlekW);
+ PropertyOffset[ord(TProp.DischargeTrigger)] := ptruint(@obj.DischargeTrigger);
+ PropertyOffset[ord(TProp.ChargeTrigger)] := ptruint(@obj.ChargeTrigger);
+ PropertyOffset[ord(TProp.pctEffCharge)] := ptruint(@obj.pctChargeEff);
+ PropertyOffset[ord(TProp.pctEffDischarge)] := ptruint(@obj.pctDischargeEff);
+ PropertyOffset[ord(TProp.pctDischarge)] := ptruint(@obj.pctkWout);
+ PropertyOffset[ord(TProp.Vminpu)] := ptruint(@obj.VMinPu);
+ PropertyOffset[ord(TProp.Vmaxpu)] := ptruint(@obj.VMaxPu);
+ PropertyOffset[ord(TProp.kVA)] := ptruint(@obj.StorageVars.kVArating);
+ PropertyOffset[ord(TProp.kWrated)] := ptruint(@obj.StorageVars.kWrating);
+ PropertyOffset[ord(TProp.kWhrated)] := ptruint(@obj.StorageVars.kWhrating);
+ PropertyOffset[ord(TProp.kWhstored)] := ptruint(@obj.StorageVars.kWhstored);
+ PropertyOffset[ord(TProp.pctreserve)] := ptruint(@obj.pctReserve);
+ PropertyOffset[ord(TProp.pctCharge)] := ptruint(@obj.pctkWIn);
+ PropertyOffset[ord(TProp.TimeChargeTrig)] := ptruint(@obj.ChargeTime);
+ PropertyOffset[ord(TProp.kv)] := ptruint(@obj.StorageVars.kVStorageBase);
+ PropertyOffset[ord(TProp.kW)] := ptruint(@obj.kW_Out);
+
+ PropertyType[ord(TProp.kvar)] := TPropertyType.DoubleProperty;
+ PropertyOffset[ord(TProp.kvar)] := 1;
+ PropertyWriteFunction[ord(TProp.kvar)] := @Setkvar;
+ PropertyReadFunction[ord(TProp.kvar)] := @Getkvar;
+ PropertyFlags[ord(TProp.kvar)] := [TPropertyFlag.WriteByFunction, TPropertyFlag.ReadByFunction];
+
+ PropertyType[ord(TProp.pctstored)] := TPropertyType.DoubleProperty;
+ PropertyOffset[ord(TProp.pctstored)] := 1;
+ PropertyWriteFunction[ord(TProp.pctstored)] := @SetPctStored;
+ PropertyReadFunction[ord(TProp.pctstored)] := @GetPctStored;
+ PropertyFlags[ord(TProp.pctstored)] := [TPropertyFlag.WriteByFunction, TPropertyFlag.ReadByFunction];
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- // Override default help string
- PropertyHelp[NumPropsThisClass + 1] := 'Name of harmonic voltage or current spectrum for this Storage element. ' +
- 'Current injection is assumed for inverter. ' +
- 'Default value is "default", which is defined when the DSS starts.';
-
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TStorage.NewObject(const ObjName: String): Integer;
+function TStorage.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new Storage element and add it to Storage class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TStorageObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TStorage.SetNcondsForConnection;
-
+procedure SetNcondsForConnection(Obj: TObj);
begin
- with DSS.ActiveStorageObj do
- begin
+ with Obj do
case Connection of
0:
NConds := Fnphases + 1;
@@ -578,10 +524,8 @@ procedure TStorage.SetNcondsForConnection;
NConds := Fnphases;
end;
end;
- end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TStorage.UpdateAll;
var
i: Integer;
@@ -592,385 +536,175 @@ procedure TStorage.UpdateAll;
UpdateStorage;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TStorage.InterpretConnection(const S: String);
-
-// Accepts
-// delta or LL (Case insensitive)
-// Y, wye, or LN
+procedure TStorageObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- TestS: String;
-
+ i: Integer;
begin
- with DSS.ActiveStorageObj do
- begin
- TestS := lowercase(S);
- case TestS[1] of
- 'y', 'w':
- Connection := 0; {Wye}
- 'd':
- Connection := 1; {Delta or line-Line}
- 'l':
- case Tests[2] of
- 'n':
- Connection := 0;
- 'l':
- Connection := 1;
- end;
+ case Idx of
+ ord(TProp.phases):
+ SetNCondsForConnection(self); // Force Reallocation of terminal info
+ ord(TProp.kW):
+ begin
+ FpctkWOut := kW_Out / StorageVars.kWRating * 100.0;
+ SyncUpPowerQuantities; // keep kvar nominal up to date with kW and PF
end;
-
- SetNCondsForConnection;
-
- {VBase is always L-N voltage unless 1-phase device or more than 3 phases}
-
- case Fnphases of
- 2, 3:
- VBase := StorageVars.kVStorageBase * InvSQRT3x1000; // L-N Volts
- else
- VBase := StorageVars.kVStorageBase * 1000.0; // Just use what is supplied
+ ord(TProp.pf):
+ SyncUpPowerQuantities; // keep kvar nominal up to date with kW and PF
+ ord(TProp.kWrated):
+ StorageVars.kVArating := StorageVars.kWrating;
+ ord(TProp.kWhrated):
+ begin
+ StorageVars.kWhStored := StorageVars.kWhRating; // Assume fully charged
+ kWhBeforeUpdate := StorageVars.kWhStored;
+ StorageVars.kWhReserve := StorageVars.kWhRating * pctReserve * 0.01;
end;
- VBase95 := Vminpu * VBase;
- VBase105 := Vmaxpu * VBase;
-
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
- end;
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function InterpretDispMode(const S: String): Integer;
-begin
- case lowercase(S)[1] of
- 'e':
- Result := STORE_EXTERNALMODE;
- 'f':
- Result := STORE_FOLLOW;
- 'l':
- Result := STORE_LOADMODE;
- 'p':
- Result := STORE_PRICEMODE;
- else
- Result := STORE_DEFAULT;
- end;
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function ReturnDispMode(const imode: Integer): String;
-begin
- case imode of
- STORE_EXTERNALMODE:
- Result := 'External';
- STORE_FOLLOW:
- Result := 'Follow';
- STORE_LOADMODE:
- Result := 'Loadshape';
- STORE_PRICEMODE:
- Result := 'Price';
- else
- Result := 'default';
- end;
-end;
-
-
-//- - - - - - - - - - - - - - -MAIN EDIT FUNCTION - - - - - - - - - - - - - - -
-
-function TStorage.Edit: Integer;
+ ord(TProp.pctreserve):
+ StorageVars.kWhReserve := StorageVars.kWhRating * pctReserve * 0.01;
-var
- i, iCase,
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
-begin
+ ord(TProp.conn):
+ begin
+ SetNCondsForConnection(self);
- // continue parsing with contents of Parser
- DSS.ActiveStorageObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveStorageObj;
+ // VBase is always L-N voltage unless 1-phase device or more than 3 phases
- Result := 0;
+ case Fnphases of
+ 2, 3:
+ VBase := StorageVars.kVStorageBase * InvSQRT3x1000; // L-N Volts
+ else
+ VBase := StorageVars.kVStorageBase * 1000.0; // Just use what is supplied
+ end;
- with DSS.ActiveStorageObj do
- begin
+ VBase95 := Vminpu * VBase;
+ VBase105 := Vmaxpu * VBase;
- ParamPointer := 0;
- ParamName := Parser.NextParam; // Parse next property off the command line
- Param := Parser.StrValue; // Put the string value of the property value in local memory for faster access
- while Length(Param) > 0 do
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
+ end;
+ ord(TProp.kv):
begin
-
- if (Length(ParamName) = 0) then
- Inc(ParamPointer) // If it is not a named property, assume the next property
+ case FNphases of
+ 2, 3:
+ VBase := StorageVars.kVStorageBase * InvSQRT3x1000;
else
- ParamPointer := CommandList.GetCommand(ParamName); // Look up the name in the list for this class
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[PropertyIdxMap[ParamPointer]] := Param // Update the string value of the property
+ VBase := StorageVars.kVStorageBase * 1000.0;
+ end;
+ VBase95 := Vminpu * VBase;
+ VBase105 := Vmaxpu * VBase;
+ end;
+ ord(TProp.debugtrace):
+ if DebugTrace then
+ begin // Init trace file
+ FreeAndNil(TraceFile);
+ TraceFile := TBufferedFileStream.Create(DSS.OutputDirectory + 'STOR_' + Name + '.csv', fmCreate);
+ FSWrite(TraceFile, 't, Iteration, LoadMultiplier, Mode, LoadModel, StorageModel, Qnominalperphase, Pnominalperphase, CurrentType');
+ for i := 1 to nphases do
+ FSWrite(Tracefile, ', |Iinj' + IntToStr(i) + '|');
+ for i := 1 to nphases do
+ FSWrite(Tracefile, ', |Iterm' + IntToStr(i) + '|');
+ for i := 1 to nphases do
+ FSWrite(Tracefile, ', |Vterm' + IntToStr(i) + '|');
+ for i := 1 to NumVariables do
+ FSWrite(Tracefile, ', ' + VariableName(i));
+
+ FSWrite(TraceFile, ',Vthev, Theta');
+ FSWriteln(TraceFile);
+ FSFlush(Tracefile);
+ end
else
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Storage "' + Name + '"', 560);
-
- if ParamPointer > 0 then
begin
- iCase := PropertyIdxMap[ParamPointer];
- case iCASE of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 561);
- 1:
- NPhases := Parser.Intvalue; // num phases
- 2:
- SetBus(1, param);
- propKV:
- PresentkV := Parser.DblValue;
- propKW:
- PresentkW := Parser.DblValue;
- propPF:
- PFNominal := Parser.DblValue;
- propMODEL:
- VoltageModel := Parser.IntValue;
- propYEARLY:
- YearlyShape := Param;
- propDAILY:
- DailyShape := Param;
- propDUTY:
- DutyShape := Param;
- propDISPMODE:
- DispatchMode := InterpretDispMode(Param);
- propIDLEKVAR:
- pctIdlekvar := Parser.DblValue;
- propCONNECTION:
- InterpretConnection(Param);
- propKVAR:
- Presentkvar := Parser.DblValue;
- propPCTR:
- pctR := Parser.DblValue;
- propPCTX:
- pctX := Parser.DblValue;
- propIDLEKW:
- pctIdlekW := Parser.DblValue;
- propCLASS:
- StorageClass := Parser.IntValue;
- propDISPOUTTRIG:
- DischargeTrigger := Parser.DblValue;
- propDISPINTRIG:
- ChargeTrigger := Parser.DblValue;
- propCHARGEEFF:
- pctChargeEff := Parser.DblValue;
- propDISCHARGEEFF:
- pctDischargeEff := Parser.DblValue;
- propPCTKWOUT:
- pctkWout := Parser.DblValue;
- propVMINPU:
- VMinPu := Parser.DblValue;
- propVMAXPU:
- VMaxPu := Parser.DblValue;
- propSTATE:
- FState := InterpretState(Param); //****
- propKVA:
- StorageVars.kVArating := Parser.DblValue;
- propKWRATED:
- StorageVars.kWrating := Parser.DblValue;
- propKWHRATED:
- StorageVars.kWhrating := Parser.DblValue;
- propKWHSTORED:
- StorageVars.kWhstored := Parser.DblValue;
- propPCTRESERVE:
- pctReserve := Parser.DblValue;
- propUSERMODEL:
- UserModel.Name := Parser.StrValue; // Connect to user written models
- propUSERDATA:
- if UserModel.Exists then
- UserModel.Edit := Parser.StrValue; // Send edit string to user model
- propDEBUGTRACE:
- DebugTrace := InterpretYesNo(Param);
- propPCTKWIN:
- pctkWIn := Parser.DblValue;
- propPCTSTORED:
- StorageVars.kWhStored := Parser.DblValue * 0.01 * StorageVars.kWhRating;
- propCHARGETIME:
- ChargeTime := Parser.DblValue;
- propDynaDLL:
- DynaModel.Name := Parser.StrValue;
- propDynaData:
- if DynaModel.Exists then
- DynaModel.Edit := Parser.StrValue;
- propBalanced:
- ForceBalanced := InterpretYesNo(Param);
- propLimited:
- CurrentLimited := InterpretYesNo(Param);
-
-
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveStorageObj, ParamPointer - NumPropsThisClass)
- end;
-
- case iCase of
- 1:
- SetNcondsForConnection; // Force Reallocation of terminal info
- propKW, propPF:
- begin
- SyncUpPowerQuantities; // keep kvar nominal up to date with kW and PF
-
- end;
-
- {Set loadshape objects; returns nil If not valid}
- propYEARLY:
- YearlyShapeObj := DSS.LoadShapeClass.Find(YearlyShape);
- propDAILY:
- DailyShapeObj := DSS.LoadShapeClass.Find(DailyShape);
- propDUTY:
- DutyShapeObj := DSS.LoadShapeClass.Find(DutyShape);
- propKWRATED:
- StorageVars.kVArating := StorageVars.kWrating;
- propKWHRATED:
- begin
- StorageVars.kWhStored := StorageVars.kWhRating; // Assume fully charged
- kWhBeforeUpdate := StorageVars.kWhStored;
- StorageVars.kWhReserve := StorageVars.kWhRating * pctReserve * 0.01;
- end;
-
- propPCTRESERVE:
- StorageVars.kWhReserve := StorageVars.kWhRating * pctReserve * 0.01;
-
- propDEBUGTRACE:
- if DebugTrace then
- begin // Init trace file
- FreeAndNil(TraceFile);
- TraceFile := TFileStream.Create(DSS.OutputDirectory + 'STOR_' + Name + '.CSV', fmCreate);
- FSWrite(TraceFile, 't, Iteration, LoadMultiplier, Mode, LoadModel, StorageModel, Qnominalperphase, Pnominalperphase, CurrentType');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Iinj' + IntToStr(i) + '|');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Iterm' + IntToStr(i) + '|');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Vterm' + IntToStr(i) + '|');
- for i := 1 to NumVariables do
- FSWrite(Tracefile, ', ' + VariableName(i));
-
- FSWrite(TraceFile, ',Vthev, Theta');
- FSWriteln(TraceFile);
- FSFlush(Tracefile);
- end
- else
- begin
- FreeAndNil(TraceFile);
- end;
-
- propKVA:
- kVANotSet := FALSE;
- propUSERMODEL:
- IsUserModel := UserModel.Exists;
- propDynaDLL:
- IsUserModel := DynaModel.Exists;
- end;
+ FreeAndNil(TraceFile);
end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
+ ord(TProp.UserModel):
+ DoSimpleMsg('%s model designated to use user-written model, but user-written model are not available for legacy models anymore (removed in DSS C-API v0.12).', [FullName], 567);
+ ord(TProp.DynaDLL):
+ DoSimpleMsg('%s model designated to use user-written model, but user-written model are not available for legacy models anymore (removed in DSS C-API v0.12).', [FullName], 567);
+
+ ord(TProp.kVA):
+ kVANotSet := FALSE;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
+function TStorage.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
+ begin
RecalcElementData;
YPrimInvalid := TRUE;
+ Exclude(Flags, Flg.EditionActive);
end;
-
+ Result := True;
end;
-//----------------------------------------------------------------------------
-function TStorage.MakeLike(const OtherStorageObjName: String): Integer;
-
-// Copy over essential properties from other object
-
+procedure TStorageObj.MakeLike(OtherPtr: Pointer);
var
- OtherStorageObj: TStorageObj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- {See If we can find this line name in the present collection}
- OtherStorageObj := Find(OtherStorageObjName);
- if (OtherStorageObj <> NIL) then
- with DSS.ActiveStorageObj do
- begin
- if (Fnphases <> OtherStorageObj.Fnphases) then
- begin
- Nphases := OtherStorageObj.Fnphases;
- NConds := Fnphases; // Forces reallocation of terminal stuff
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
- end;
+ inherited MakeLike(OtherPtr);
- StorageVars.kVStorageBase := OtherStorageObj.StorageVars.kVStorageBase;
- Vbase := OtherStorageObj.Vbase;
- Vminpu := OtherStorageObj.Vminpu;
- Vmaxpu := OtherStorageObj.Vmaxpu;
- Vbase95 := OtherStorageObj.Vbase95;
- Vbase105 := OtherStorageObj.Vbase105;
- kW_out := OtherStorageObj.kW_out;
- kvar_out := OtherStorageObj.kvar_out;
- Pnominalperphase := OtherStorageObj.Pnominalperphase;
- PFNominal := OtherStorageObj.PFNominal;
- Qnominalperphase := OtherStorageObj.Qnominalperphase;
- Connection := OtherStorageObj.Connection;
- YearlyShape := OtherStorageObj.YearlyShape;
- YearlyShapeObj := OtherStorageObj.YearlyShapeObj;
- DailyShape := OtherStorageObj.DailyShape;
- DailyShapeObj := OtherStorageObj.DailyShapeObj;
- DutyShape := OtherStorageObj.DutyShape;
- DutyShapeObj := OtherStorageObj.DutyShapeObj;
- DispatchMode := OtherStorageObj.DispatchMode;
- StorageClass := OtherStorageObj.StorageClass;
- VoltageModel := OtherStorageObj.VoltageModel;
-
- Fstate := OtherStorageObj.Fstate;
- FstateChanged := OtherStorageObj.FstateChanged;
- kVANotSet := OtherStorageObj.kVANotSet;
-
- StorageVars.kVArating := OtherStorageObj.StorageVars.kVArating;
- StorageVars.kWRating := OtherStorageObj.StorageVars.kWRating;
- StorageVars.kWhRating := OtherStorageObj.StorageVars.kWhRating;
- StorageVars.kWhStored := OtherStorageObj.StorageVars.kWhStored;
- StorageVars.kWhReserve := OtherStorageObj.StorageVars.kWhReserve;
- kWhBeforeUpdate := OtherStorageObj.kWhBeforeUpdate;
- pctReserve := OtherStorageObj.pctReserve;
- DischargeTrigger := OtherStorageObj.DischargeTrigger;
- ChargeTrigger := OtherStorageObj.ChargeTrigger;
- pctChargeEff := OtherStorageObj.pctChargeEff;
- pctDischargeEff := OtherStorageObj.pctDischargeEff;
- pctkWout := OtherStorageObj.pctkWout;
- pctkWin := OtherStorageObj.pctkWin;
- pctIdlekW := OtherStorageObj.pctIdlekW;
- pctIdlekvar := OtherStorageObj.pctIdlekvar;
- ChargeTime := OtherStorageObj.ChargeTime;
-
- pctR := OtherStorageObj.pctR;
- pctX := OtherStorageObj.pctX;
-
- RandomMult := OtherStorageObj.RandomMult;
-
- UserModel.Name := OtherStorageObj.UserModel.Name; // Connect to user written models
- DynaModel.Name := OtherStorageObj.DynaModel.Name;
- IsUserModel := OtherStorageObj.IsUserModel;
- ForceBalanced := OtherStorageObj.ForceBalanced;
- CurrentLimited := OtherStorageObj.CurrentLimited;
-
- ClassMakeLike(OtherStorageObj);
-
- for i := 1 to ParentClass.NumProperties do
- FPropertyValue^[i] := OtherStorageObj.FPropertyValue^[i];
-
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Storage MakeLike: "' + OtherStorageObjName + '" Not Found.', 562);
+ Other := TObj(OtherPtr);
+ if (Fnphases <> Other.Fnphases) then
+ begin
+ FNphases := Other.Fnphases;
+ NConds := Fnphases; // Forces reallocation of terminal stuff
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
+ end;
+ StorageVars.kVStorageBase := Other.StorageVars.kVStorageBase;
+ Vbase := Other.Vbase;
+ Vminpu := Other.Vminpu;
+ Vmaxpu := Other.Vmaxpu;
+ Vbase95 := Other.Vbase95;
+ Vbase105 := Other.Vbase105;
+ kW_out := Other.kW_out;
+ kvar_out := Other.kvar_out;
+ Pnominalperphase := Other.Pnominalperphase;
+ PFNominal := Other.PFNominal;
+ Qnominalperphase := Other.Qnominalperphase;
+ Connection := Other.Connection;
+ YearlyShapeObj := Other.YearlyShapeObj;
+ DailyShapeObj := Other.DailyShapeObj;
+ DutyShapeObj := Other.DutyShapeObj;
+ DispatchMode := Other.DispatchMode;
+ StorageClass := Other.StorageClass;
+ VoltageModel := Other.VoltageModel;
+
+ Fstate := Other.Fstate;
+ FstateChanged := Other.FstateChanged;
+ kVANotSet := Other.kVANotSet;
+
+ StorageVars.kVArating := Other.StorageVars.kVArating;
+ StorageVars.kWRating := Other.StorageVars.kWRating;
+ StorageVars.kWhRating := Other.StorageVars.kWhRating;
+ StorageVars.kWhStored := Other.StorageVars.kWhStored;
+ StorageVars.kWhReserve := Other.StorageVars.kWhReserve;
+ kWhBeforeUpdate := Other.kWhBeforeUpdate;
+ pctReserve := Other.pctReserve;
+ DischargeTrigger := Other.DischargeTrigger;
+ ChargeTrigger := Other.ChargeTrigger;
+ pctChargeEff := Other.pctChargeEff;
+ pctDischargeEff := Other.pctDischargeEff;
+ pctkWout := Other.pctkWout;
+ pctkWin := Other.pctkWin;
+ pctIdlekW := Other.pctIdlekW;
+ pctIdlekvar := Other.pctIdlekvar;
+ ChargeTime := Other.ChargeTime;
+
+ pctR := Other.pctR;
+ pctX := Other.pctX;
+
+ RandomMult := Other.RandomMult;
+
+ ForceBalanced := Other.ForceBalanced;
+ CurrentLimited := Other.CurrentLimited;
end;
-{--------------------------------------------------------------------------}
procedure TStorage.ResetRegistersAll; // Force all EnergyMeters in the circuit to reset
-
var
idx: Integer;
-
begin
idx := First;
while idx > 0 do
@@ -980,9 +714,7 @@ procedure TStorage.ResetRegistersAll; // Force all EnergyMeters in the circuit
end;
end;
-{--------------------------------------------------------------------------}
procedure TStorage.SampleAll; // Force all Storage elements in the circuit to take a sample
-
var
i: Integer;
begin
@@ -992,28 +724,23 @@ procedure TStorage.SampleAll; // Force all Storage elements in the circuit to t
TakeSample;
end;
-//----------------------------------------------------------------------------
constructor TStorageObj.Create(ParClass: TDSSClass; const SourceName: String);
begin
-
inherited create(ParClass);
- Name := LowerCase(SourceName);
+ Name := AnsiLowerCase(SourceName);
DSSObjType := ParClass.DSSClassType; // + STORAGE_ELEMENT; // In both PCelement and Storageelement list
TraceFile := nil;
- Nphases := 3;
+ FNphases := 3;
Fnconds := 4; // defaults to wye
Yorder := 0; // To trigger an initial allocation
Nterms := 1; // forces allocations
- YearlyShape := '';
- YearlyShapeObj := NIL; // If YearlyShapeobj = nil Then the load alway stays nominal * global multipliers
- DailyShape := '';
- DailyShapeObj := NIL; // If DaillyShapeobj = nil Then the load alway stays nominal * global multipliers
- DutyShape := '';
- DutyShapeObj := NIL; // If DutyShapeobj = nil Then the load alway stays nominal * global multipliers
+ YearlyShapeObj := NIL;
+ DailyShapeObj := NIL;
+ DutyShapeObj := NIL;
Connection := 0; // Wye (star)
- VoltageModel := 1; {Typical fixed kW negative load}
+ VoltageModel := 1; // Typical fixed kW negative load
StorageClass := 1;
StorageSolutionCount := -1; // For keep track of the present solution in Injcurrent calcs
@@ -1029,7 +756,7 @@ constructor TStorageObj.Create(ParClass: TDSSClass; const SourceName: String);
Yorder := Fnterms * Fnconds;
RandomMult := 1.0;
- {Output rating stuff}
+ // Output rating stuff
kW_out := 25.0;
kvar_out := 0.0;
kvarBase := kvar_out; // initialize
@@ -1048,7 +775,6 @@ constructor TStorageObj.Create(ParClass: TDSSClass; const SourceName: String);
FStateChanged := TRUE; // Force building of YPrim
pctReserve := 20.0; // per cent of kWhRating
pctR := 0.0;
- ;
pctX := 50.0;
pctIdlekW := 1.0;
pctIdlekvar := 0.0;
@@ -1065,13 +791,14 @@ constructor TStorageObj.Create(ParClass: TDSSClass; const SourceName: String);
kVANotSet := TRUE; // Flag to set the default value for kVA
- {Make the StorageVars struct as public}
+ // Make the StorageVars struct as public
PublicDataStruct := @StorageVars;
PublicDataSize := SizeOf(TStorageVars);
- IsUserModel := FALSE;
- UserModel := TStoreUserModel.Create(DSS);
- DynaModel := TStoreDynaModel.Create(DSS);
+ UserModelNameStr := '';
+ UserModelEditStr := '';
+ DynaModelNameStr := '';
+ DynaModelEditStr := '';
Reg_kWh := 1;
Reg_kvarh := 2;
@@ -1082,197 +809,23 @@ constructor TStorageObj.Create(ParClass: TDSSClass; const SourceName: String);
DebugTrace := FALSE;
StorageObjSwitchOpen := FALSE;
- Spectrum := ''; // override base class
- SpectrumObj := NIL;
+ SpectrumObj := NIL; // override base class
ForceBalanced := FALSE;
CurrentLimited := FALSE;
- InitPropertyValues(0);
RecalcElementData;
-
-end;
-
-
-//----------------------------------------------------------------------------
-function TStorageObj.DecodeState: String;
-begin
- case Fstate of
- STORE_CHARGING:
- Result := 'CHARGING';
- STORE_DISCHARGING:
- Result := 'DISCHARGING';
- else
- Result := 'IDLING';
- end;
-end;
-
-//----------------------------------------------------------------------------
-procedure TStorageObj.InitPropertyValues(ArrayOffset: Integer);
-
-// Define default values for the properties
-
-begin
-
- PropertyValue[1] := '3'; //'phases';
- PropertyValue[2] := Getbus(1); //'bus1';
-
- PropertyValue[propKV] := Format('%-g', [StorageVars.kVStorageBase]);
- PropertyValue[propKW] := Format('%-g', [kW_out]);
- PropertyValue[propPF] := Format('%-g', [PFNominal]);
- PropertyValue[propMODEL] := '1';
- PropertyValue[propYEARLY] := '';
- PropertyValue[propDAILY] := '';
- PropertyValue[propDUTY] := '';
- PropertyValue[propDISPMODE] := 'Default';
- PropertyValue[propIDLEKVAR] := '0';
- PropertyValue[propCONNECTION] := 'wye';
- PropertyValue[propKVAR] := Format('%-g', [Presentkvar]);
-
- PropertyValue[propPCTR] := Format('%-g', [pctR]);
- PropertyValue[propPCTX] := Format('%-g', [pctX]);
-
- PropertyValue[propIDLEKW] := '1'; // PERCENT
- PropertyValue[propCLASS] := '1'; //'class'
- PropertyValue[propDISPOUTTRIG] := '0'; // 0 MEANS NO TRIGGER LEVEL
- PropertyValue[propDISPINTRIG] := '0';
- PropertyValue[propCHARGEEFF] := '90';
- PropertyValue[propDISCHARGEEFF] := '90';
- PropertyValue[propPCTKWOUT] := '100';
- PropertyValue[propPCTKWIN] := '100';
-
- PropertyValue[propVMINPU] := '0.90';
- PropertyValue[propVMAXPU] := '1.10';
- PropertyValue[propSTATE] := 'IDLING';
-
- with StorageVars do
- begin
- PropertyValue[propKVA] := Format('%-g', [StorageVars.kVARating]);
- PropertyValue[propKWRATED] := Format('%-g', [kWRating]);
- PropertyValue[propKWHRATED] := Format('%-g', [kWhRating]);
- PropertyValue[propKWHSTORED] := Format('%-g', [kWhStored]);
- PropertyValue[propPCTSTORED] := Format('%-g', [kWhStored / kWhRating * 100.0]);
- end;
-
- PropertyValue[propPCTRESERVE] := Format('%-g', [pctReserve]);
- PropertyValue[propCHARGETIME] := Format('%-g', [ChargeTime]);
-
- PropertyValue[propUSERMODEL] := ''; // Usermodel
- PropertyValue[propUSERDATA] := ''; // Userdata
- PropertyValue[propDYNADLL] := ''; //
- PropertyValue[propDYNADATA] := ''; //
- PropertyValue[propDEBUGTRACE] := 'NO';
- PropertyValue[propBalanced] := 'NO';
- PropertyValue[propLimited] := 'NO';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-
-//----------------------------------------------------------------------------
-function TStorageObj.GetPropertyValue(Index: Integer): String;
-
-
-begin
-
- Result := '';
- with StorageVars do
- case Index of
- propKV:
- Result := Format('%.6g', [StorageVars.kVStorageBase]);
- propKW:
- Result := Format('%.6g', [kW_out]);
- propPF:
- Result := Format('%.6g', [PFNominal]);
- propMODEL:
- Result := Format('%d', [VoltageModel]);
- propYEARLY:
- Result := YearlyShape;
- propDAILY:
- Result := DailyShape;
- propDUTY:
- Result := DutyShape;
- propDISPMODE:
- Result := ReturnDispMode(DispatchMode);
- propIDLEKVAR:
- Result := Format('%.6g', [pctIdlekvar]);
- {propCONNECTION :;}
- propKVAR:
- Result := Format('%.6g', [kvar_out]);
- propPCTR:
- Result := Format('%.6g', [pctR]);
- propPCTX:
- Result := Format('%.6g', [pctX]);
- propIDLEKW:
- Result := Format('%.6g', [pctIdlekW]);
- {propCLASS = 17;}
- propDISPOUTTRIG:
- Result := Format('%.6g', [DischargeTrigger]);
- propDISPINTRIG:
- Result := Format('%.6g', [ChargeTrigger]);
- propCHARGEEFF:
- Result := Format('%.6g', [pctChargeEff]);
- propDISCHARGEEFF:
- Result := Format('%.6g', [pctDischargeEff]);
- propPCTKWOUT:
- Result := Format('%.6g', [pctkWout]);
- propVMINPU:
- Result := Format('%.6g', [VMinPu]);
- propVMAXPU:
- Result := Format('%.6g', [VMaxPu]);
- propSTATE:
- Result := DecodeState;
- {StorageVars}
- propKVA:
- Result := Format('%.6g', [kVArating]);
- propKWRATED:
- Result := Format('%.6g', [kWrating]);
- propKWHRATED:
- Result := Format('%.6g', [kWhrating]);
- propKWHSTORED:
- Result := Format('%.6g', [kWHStored]);
- propPCTRESERVE:
- Result := Format('%.6g', [pctReserve]);
- propUSERMODEL:
- Result := UserModel.Name;
- propUSERDATA:
- Result := '(' + inherited GetPropertyValue(index) + ')';
- propDynaDLL:
- Result := DynaModel.Name;
- propdynaDATA:
- Result := '(' + inherited GetPropertyValue(index) + ')';
- {propDEBUGTRACE = 33;}
- propPCTKWIN:
- Result := Format('%.6g', [pctkWin]);
- propPCTSTORED:
- Result := Format('%.6g', [kWhStored / kWhRating * 100.0]);
- propCHARGETIME:
- Result := Format('%.6g', [Chargetime]);
- propBalanced:
- Result := StrYorN(ForceBalanced);
- propLimited:
- Result := StrYorN(CurrentLimited);
-
- else // take the generic handler
- Result := inherited GetPropertyValue(index);
- end;
end;
-//----------------------------------------------------------------------------
destructor TStorageObj.Destroy;
begin
YPrimOpenCond.Free;
- UserModel.Free;
- DynaModel.Free;
FreeAndNil(TraceFile);
inherited Destroy;
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.Randomize(Opt: Integer);
begin
-
case Opt of
0:
RandomMult := 1.0;
@@ -1283,12 +836,9 @@ procedure TStorageObj.Randomize(Opt: Integer);
LOGNORMAL:
RandomMult := QuasiLognormal(YearlyShapeObj.Mean);
end;
-
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.CalcDailyMult(Hr: Double);
-
begin
if (DailyShapeObj <> NIL) then
begin
@@ -1300,10 +850,7 @@ procedure TStorageObj.CalcDailyMult(Hr: Double);
CheckStateTriggerLevel(ShapeFactor.re); // last recourse
end;
-
-//----------------------------------------------------------------------------
procedure TStorageObj.CalcDutyMult(Hr: Double);
-
begin
if DutyShapeObj <> NIL then
begin
@@ -1314,9 +861,7 @@ procedure TStorageObj.CalcDutyMult(Hr: Double);
CalcDailyMult(Hr); // Default to Daily Mult If no duty curve specified
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.CalcYearlyMult(Hr: Double);
-
begin
if YearlyShapeObj <> NIL then
begin
@@ -1327,7 +872,6 @@ procedure TStorageObj.CalcYearlyMult(Hr: Double);
CalcDailyMult(Hr); // Defaults to Daily curve
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.SetKWandKvarOut;
var
OldState: Integer;
@@ -1379,7 +923,7 @@ procedure TStorageObj.SetKWandKvarOut;
end;
- {If idling output is only losses}
+ // If idling output is only losses
if Fstate = STORE_IDLING then
begin
@@ -1389,15 +933,10 @@ procedure TStorageObj.SetKWandKvarOut;
if OldState <> Fstate then
FstateChanged := TRUE;
-
end;
-
-//----------------------------------------------------------------------------
procedure TStorageObj.SetNominalStorageOuput;
-
begin
-
ShapeFactor := CDOUBLEONE; // init here; changed by curve routine
// Check to make sure the Storage element is ON
with ActiveCircuit, ActiveCircuit.Solution do
@@ -1417,17 +956,15 @@ procedure TStorageObj.SetNominalStorageOuput;
with Solution do
case Mode of
- TSolveMode.SNAPSHOT: ; {Just solve for the present kW, kvar} // Don't check for state change
+ TSolveMode.SNAPSHOT: ; // Just solve for the present kW, kvar // Don't check for state change
TSolveMode.DAILYMODE:
CalcDailyMult(DynaVars.dblHour); // Daily dispatch curve
TSolveMode.YEARLYMODE:
CalcYearlyMult(DynaVars.dblHour);
- (*
- MONTECARLO1,
- MONTEFAULT,
- FAULTSTUDY,
- DYNAMICMODE: ; // {do nothing for these modes}
- *)
+ // MONTECARLO1,
+ // MONTEFAULT,
+ // FAULTSTUDY,
+ // DYNAMICMODE: ; // // do nothing for these modes
TSolveMode.GENERALTIME:
begin
// This mode allows use of one class of load shape
@@ -1442,7 +979,7 @@ procedure TStorageObj.SetNominalStorageOuput;
ShapeFactor := CDOUBLEONE // default to 1 + j1 if not known
end;
end;
- // Assume Daily curve, If any, for the following
+ // Assume Daily curve, If any, for the following
TSolveMode.MONTECARLO2,
TSolveMode.MONTECARLO3,
TSolveMode.LOADDURATION1,
@@ -1453,18 +990,14 @@ procedure TStorageObj.SetNominalStorageOuput;
TSolveMode.DUTYCYCLE:
CalcDutyMult(DynaVars.dblHour);
- {AUTOADDFLAG: ; }
+ // AUTOADDFLAG: ;
end;
-
end;
-
SetKWandKvarOut; // Based on State and amount of energy left in storage
- {
- Pnominalperphase is net at the terminal. When discharging, the storage supplies the idling losses.
- When charging, the idling losses are subtracting from the amount entering the storage element.
- }
+ // Pnominalperphase is net at the terminal. When discharging, the storage supplies the idling losses.
+ // When charging, the idling losses are subtracting from the amount entering the storage element.
Pnominalperphase := 1000.0 * kW_out / Fnphases;
@@ -1474,66 +1007,58 @@ procedure TStorageObj.SetNominalStorageOuput;
Qnominalperphase := StorageVars.kvarRequested / Fnphases * 1000.0
else
Qnominalperphase := 0.0;
- Yeq := CDivReal(Cmplx(Pnominalperphase, -Qnominalperphase), Sqr(Vbase)); // Vbase must be L-N for 3-phase
+ Yeq := Cmplx(Pnominalperphase, -Qnominalperphase) / Sqr(Vbase); // Vbase must be L-N for 3-phase
Yeq95 := Yeq;
Yeq105 := Yeq;
end
else
begin
-
Qnominalperphase := 1000.0 * kvar_out / Fnphases;
case VoltageModel of
- //**** Fix this when user model gets connected in
+ //**** Fix this when user model gets connected in
3: // Yeq := Cinv(cmplx(0.0, -StoreVARs.Xd)) ; // Gets negated in CalcYPrim
else
- {
- Yeq no longer used for anything other than this calculation of Yeq95, Yeq105 and
- constant Z power flow model
- }
- Yeq := CDivReal(Cmplx(Pnominalperphase, -Qnominalperphase), Sqr(Vbase)); // Vbase must be L-N for 3-phase
+ // Yeq no longer used for anything other than this calculation of Yeq95, Yeq105 and
+ // constant Z power flow model
+ Yeq := Cmplx(Pnominalperphase, -Qnominalperphase) / Sqr(Vbase); // Vbase must be L-N for 3-phase
if (Vminpu <> 0.0) then
- Yeq95 := CDivReal(Yeq, sqr(Vminpu)) // at 95% voltage
+ Yeq95 := Yeq / sqr(Vminpu) // at 95% voltage
else
Yeq95 := Yeq; // Always a constant Z model
if (Vmaxpu <> 0.0) then
- Yeq105 := CDivReal(Yeq, Sqr(Vmaxpu)) // at 105% voltage
+ Yeq105 := Yeq / Sqr(Vmaxpu) // at 105% voltage
else
Yeq105 := Yeq;
end;
- { Like Model 7 generator, max current is based on amount of current to get out requested power at min voltage
- }
+ // Like Model 7 generator, max current is based on amount of current to get out requested power at min voltage
with StorageVars do
begin
- PhaseCurrentLimit := Cdivreal(Cmplx(Pnominalperphase, Qnominalperphase), VBase95);
+ PhaseCurrentLimit := Cmplx(Pnominalperphase, Qnominalperphase) / VBase95;
MaxDynPhaseCurrent := Cabs(PhaseCurrentLimit);
end;
end;
- { When we leave here, all the Yeq's are in L-N values}
+ // When we leave here, all the Yeq's are in L-N values
- end; {If NOT (IsDynamicModel or IsHarmonicModel)}
- end; {With ActiveCircuit}
+ end; // If NOT (IsDynamicModel or IsHarmonicModel)
+ end; // With ActiveCircuit
- // If Storage element state changes, force re-calc of Y matrix
+ // If Storage element state changes, force re-calc of Y matrix
if FStateChanged then
begin
YPrimInvalid := TRUE;
FStateChanged := FALSE; // reset the flag
end;
-
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.RecalcElementData;
-
begin
-
VBase95 := VMinPu * VBase;
VBase105 := VMaxPu * VBase;
- // removed 5/8/17 kvarBase := kvar_out ; // remember this for Follow Mode
+ // removed 5/8/17 kvarBase := kvar_out ; // remember this for Follow Mode
// values in ohms for thevenin equivalents
StorageVars.RThev := pctR * 0.01 * SQR(PresentkV) / StorageVars.kVARating * 1000.0;
@@ -1543,75 +1068,44 @@ procedure TStorageObj.RecalcElementData;
StorageVars.ChargeEff := pctChargeEff * 0.01;
StorageVars.DisChargeEff := pctDisChargeEff * 0.01;
- YeqIdling := CmulReal(Cmplx(pctIdlekW, pctIdlekvar), (StorageVars.kWrating * 10.0 / SQR(vbase) / FNPhases)); // 10.0 = 1000/100 = kW->W/pct
+ YeqIdling := Cmplx(pctIdlekW, pctIdlekvar) * (StorageVars.kWrating * 10.0 / SQR(vbase) / FNPhases); // 10.0 = 1000/100 = kW->W/pct
YeqDischarge := Cmplx((StorageVars.kWrating * 1000.0 / SQR(vbase) / FNPhases), 0.0);
SetNominalStorageOuput;
- {Now check for errors. If any of these came out nil and the string was not nil, give warning}
- if YearlyShapeObj = NIL then
- if Length(YearlyShape) > 0 then
- DoSimpleMsg('WARNING! Yearly load shape: "' + YearlyShape + '" Not Found.', 563);
- if DailyShapeObj = NIL then
- if Length(DailyShape) > 0 then
- DoSimpleMsg('WARNING! Daily load shape: "' + DailyShape + '" Not Found.', 564);
- if DutyShapeObj = NIL then
- if Length(DutyShape) > 0 then
- DoSimpleMsg('WARNING! Duty load shape: "' + DutyShape + '" Not Found.', 565);
-
- if Length(Spectrum) > 0 then
- begin
- SpectrumObj := DSS.SpectrumClass.Find(Spectrum);
- if SpectrumObj = NIL then
- DoSimpleMsg('ERROR! Spectrum "' + Spectrum + '" Not Found.', 566);
- end
- else
- SpectrumObj := NIL;
-
// Initialize to Zero - defaults to PQ Storage element
// Solution object will reset after circuit modifications
Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
-
- {Update any user-written models}
- if Usermodel.Exists then
- UserModel.FUpdateModel; // Checks for existence and Selects
- if Dynamodel.Exists then
- Dynamodel.FUpdateModel; // Checks for existence and Selects
-
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
-
var
Y, Yij: Complex;
i, j: Integer;
FreqMultiplier: Double;
-
begin
-
FYprimFreq := ActiveCircuit.Solution.Frequency;
FreqMultiplier := FYprimFreq / BaseFrequency;
with ActiveCircuit.solution do
- if {IsDynamicModel or} IsHarmonicModel then
+ if IsHarmonicModel then // IsDynamicModel or
begin
- {Yeq is computed from %R and %X -- inverse of Rthev + j Xthev}
+ // Yeq is computed from %R and %X -- inverse of Rthev + j Xthev
case Fstate of
STORE_CHARGING:
- Y := Cadd(YeqDischarge, YeqIdling);
+ Y := YeqDischarge + YeqIdling;
STORE_IDLING:
Y := YeqIdling;
STORE_DISCHARGING:
- Y := Cadd(cnegate(YeqDischarge), YeqIdling);
+ Y := -YeqDischarge + YeqIdling;
// old way Y := Yeq // L-N value computed in initialization routines
end;
if Connection = 1 then
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
+ Y := Y / 3.0; // Convert to delta impedance
Y.im := Y.im / FreqMultiplier;
- Yij := Cnegate(Y);
+ Yij := -Y;
for i := 1 to Fnphases do
begin
case Connection of
@@ -1622,7 +1116,7 @@ procedure TStorageObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
Ymatrix.SetElemsym(i, Fnconds, Yij);
end;
1:
- begin {Delta connection}
+ begin // Delta connection
Ymatrix.SetElement(i, i, Y);
Ymatrix.AddElement(i, i, Y); // put it in again
for j := 1 to i - 1 do
@@ -1631,23 +1125,19 @@ procedure TStorageObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
end;
end;
end
-
else
begin // Regular power flow Storage element model
-
- {Yeq is always expected as the equivalent line-neutral admittance}
-
-
+ // Yeq is always expected as the equivalent line-neutral admittance
case Fstate of
STORE_CHARGING:
- Y := Cadd(YeqDischarge, YeqIdling);
+ Y := YeqDischarge + YeqIdling;
STORE_IDLING:
Y := YeqIdling;
STORE_DISCHARGING:
- Y := Cadd(cnegate(YeqDischarge), YeqIdling);
+ Y := -YeqDischarge + YeqIdling;
end;
- // ****** Need to modify the base admittance for real harmonics calcs
+ // ****** Need to modify the base admittance for real harmonics calcs
Y.im := Y.im / FreqMultiplier;
case Connection of
@@ -1655,7 +1145,7 @@ procedure TStorageObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
0:
with YMatrix do
begin // WYE
- Yij := Cnegate(Y);
+ Yij := -Y;
for i := 1 to Fnphases do
begin
SetElement(i, i, Y);
@@ -1667,8 +1157,8 @@ procedure TStorageObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
1:
with YMatrix do
begin // Delta or L-L
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
- Yij := Cnegate(Y);
+ Y := Y / 3.0; // Convert to delta impedance
+ Yij := -Y;
for i := 1 to Fnphases do
begin
j := i + 1;
@@ -1681,19 +1171,15 @@ procedure TStorageObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
end;
end;
- end; {ELSE IF Solution.mode}
-
+ end; // ELSE IF Solution.mode
end;
-//----------------------------------------------------------------------------
function TStorageObj.NormalizeToTOD(h: Integer; sec: Double): Double;
// Normalize time to a floating point number representing time of day If Hour > 24
// time should be 0 to 24.
var
HourOfDay: Integer;
-
begin
-
if h > 23 then
HourOfDay := (h - (h div 24) * 24)
else
@@ -1706,13 +1192,10 @@ function TStorageObj.NormalizeToTOD(h: Integer; sec: Double): Double;
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.CheckStateTriggerLevel(Level: Double);
-{This is where we set the state of the Storage element}
-
+// This is where we set the state of the Storage element
var
OldState: Integer;
-
begin
FStateChanged := FALSE;
@@ -1721,8 +1204,7 @@ procedure TStorageObj.CheckStateTriggerLevel(Level: Double);
with StorageVars do
if DispatchMode = STORE_FOLLOW then
begin
-
- // set charge and discharge modes based on sign of loadshape
+ // set charge and discharge modes based on sign of loadshape
if (Level > 0.0) and (kWhStored > kWhReserve) then
StorageState := STORE_DISCHARGING
else
@@ -1738,7 +1220,7 @@ procedure TStorageObj.CheckStateTriggerLevel(Level: Double);
if (ChargeTrigger = 0.0) and (DischargeTrigger = 0.0) then
Exit;
- // First see If we want to turn off Charging or Discharging State
+ // First see If we want to turn off Charging or Discharging State
case Fstate of
STORE_CHARGING:
if (ChargeTrigger <> 0.0) then
@@ -1750,7 +1232,7 @@ procedure TStorageObj.CheckStateTriggerLevel(Level: Double);
Fstate := STORE_IDLING;
end;
- // Now check to see If we want to turn on the opposite state
+ // Now check to see If we want to turn on the opposite state
case Fstate of
STORE_IDLING:
begin
@@ -1779,17 +1261,13 @@ procedure TStorageObj.CheckStateTriggerLevel(Level: Double);
end;
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.CalcYPrim;
-
var
i: Integer;
-
begin
-
- // Build only shunt Yprim
- // Build a dummy Yprim Series so that CalcV Does not fail
- if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Shunt = NIL) OR (Yprim_Series = NIL) {YPrimInvalid} then
+ // Build only shunt Yprim
+ // Build a dummy Yprim Series so that CalcV Does not fail
+ if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Shunt = NIL) OR (Yprim_Series = NIL) then // YPrimInvalid
begin
if YPrim_Shunt <> NIL then
YPrim_Shunt.Free;
@@ -1813,50 +1291,43 @@ procedure TStorageObj.CalcYPrim;
// Set YPrim_Series based on diagonals of YPrim_shunt so that CalcVoltages Doesn't fail
for i := 1 to Yorder do
- Yprim_Series.SetElement(i, i, CmulReal(Yprim_Shunt.Getelement(i, i), 1.0e-10));
+ Yprim_Series.SetElement(i, i, Yprim_Shunt.Getelement(i, i) * 1.0e-10);
YPrim.CopyFrom(YPrim_Shunt);
// Account for Open Conductors
inherited CalcYPrim;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorageObj.StickCurrInTerminalArray(TermArray: pComplexArray; const Curr: Complex; i: Integer);
- {Add the current into the proper location according to connection}
-
- {Reverse of similar routine in load (Cnegates are switched)}
+// Add the current into the proper location according to connection
+// Reverse of similar routine in load (Cnegates are switched)
var
j: Integer;
-
begin
case Connection of
0:
begin //Wye
- Caccum(TermArray^[i], Curr);
- Caccum(TermArray^[Fnconds], Cnegate(Curr)); // Neutral
+ TermArray^[i] += Curr;
+ TermArray^[Fnconds] -= Curr; // Neutral
end;
1:
begin //DELTA
- Caccum(TermArray^[i], Curr);
+ TermArray^[i] += Curr;
j := i + 1;
if j > Fnconds then
j := 1;
- Caccum(TermArray^[j], Cnegate(Curr));
+ TermArray^[j] -= Curr;
end;
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorageObj.WriteTraceRecord(const s: String);
-
var
i: Integer;
sout: String;
begin
-
try
if (not DSS.InShowResults) then
begin
@@ -1864,8 +1335,8 @@ procedure TStorageObj.WriteTraceRecord(const s: String);
[ActiveCircuit.Solution.DynaVars.dblHour,
ActiveCircuit.Solution.Iteration,
ActiveCircuit.LoadMultiplier]),
- GetSolutionModeID(DSS), ', ',
- GetLoadModel(DSS), ', ',
+ DSS.SolveModeEnum.OrdinalToString(ord(DSS.ActiveCircuit.Solution.mode)), ', ',
+ DSS.DefaultLoadModelEnum.OrdinalToString(DSS.ActiveCircuit.Solution.LoadModel), ', ',
VoltageModel: 0, ', ',
(Qnominalperphase * 3.0 / 1.0e6): 8: 2, ', ',
(Pnominalperphase * 3.0 / 1.0e6): 8: 2, ', ',
@@ -1900,11 +1371,9 @@ procedure TStorageObj.WriteTraceRecord(const s: String);
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TStorageObj.DoConstantPQStorageObj;
-
-{Compute total terminal current for Constant PQ}
+procedure TStorageObj.DoConstantPQStorageObj;
+// Compute total terminal current for Constant PQ
var
i: Integer;
Curr,
@@ -1928,7 +1397,7 @@ procedure TStorageObj.DoConstantPQStorageObj;
Curr := InjCurrent^[i];
StickCurrInTerminalArray(ITerminal, Curr, i); // Put YPrim contribution into Terminal array taking into account connection
IterminalUpdated := TRUE;
- StickCurrInTerminalArray(InjCurrent, Cnegate(Curr), i); // Compensation current is zero since terminal current is same as Yprim contribution
+ StickCurrInTerminalArray(InjCurrent, -Curr, i); // Compensation current is zero since terminal current is same as Yprim contribution
end;
end;
else // For Charging and Discharging
@@ -1945,27 +1414,26 @@ procedure TStorageObj.DoConstantPQStorageObj;
for i := 1 to Fnphases do
begin
-
case Connection of
0:
- begin {Wye}
+ begin // Wye
VLN := Vterminal^[i];
VMagLN := Cabs(VLN);
if VMagLN <= VBase95 then
- Curr := Cmul(Yeq95, VLN) // Below 95% use an impedance model
+ Curr := Yeq95 * VLN // Below 95% use an impedance model
else
if VMagLN > VBase105 then
- Curr := Cmul(Yeq105, VLN) // above 105% use an impedance model
+ Curr := Yeq105 * VLN // above 105% use an impedance model
else
- Curr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLN)); // Between 95% -105%, constant PQ
+ Curr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLN); // Between 95% -105%, constant PQ
if CurrentLimited then
if Cabs(Curr) > MaxDynPhaseCurrent then
- Curr := Conjg(Cdiv(PhaseCurrentLimit, CDivReal(VLN, VMagLN)));
+ Curr := cong(PhaseCurrentLimit / (VLN / VMagLN));
end;
1:
- begin {Delta}
+ begin // Delta
VLL := Vterminal^[i];
VMagLL := Cabs(VLL);
if Fnphases > 1 then
@@ -1973,47 +1441,41 @@ procedure TStorageObj.DoConstantPQStorageObj;
else
VMagLN := VmagLL; // L-N magnitude
if VMagLN <= VBase95 then
- Curr := Cmul(CdivReal(Yeq95, 3.0), VLL) // Below 95% use an impedance model
+ Curr := (Yeq95 / 3.0) * VLL // Below 95% use an impedance model
else
if VMagLN > VBase105 then
- Curr := Cmul(CdivReal(Yeq105, 3.0), VLL) // above 105% use an impedance model
+ Curr := (Yeq105 / 3.0) * VLL // above 105% use an impedance model
else
- Curr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLL)); // Between 95% -105%, constant PQ
+ Curr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLL); // Between 95% -105%, constant PQ
if CurrentLimited then
if Cabs(Curr) * SQRT3 > MaxDynPhaseCurrent then
- Curr := Conjg(Cdiv(PhaseCurrentLimit, CDivReal(VLL, VMagLN))); // Note VmagLN has sqrt3 factor in it
+ Curr := cong(PhaseCurrentLimit / (VLL / VMagLN)); // Note VmagLN has sqrt3 factor in it
end;
end;
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
end;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorageObj.DoConstantZStorageObj;
-
-{constant Z model}
+// constant Z model
var
i: Integer;
Curr,
Yeq2: Complex;
V012: array[0..2] of Complex; // Sequence voltages
-
begin
-
-// Assume Yeq is kept up to date
-
+ // Assume Yeq is kept up to date
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the load
ZeroITerminal;
if Connection = 0 then
Yeq2 := Yeq
else
- Yeq2 := CdivReal(Yeq, 3.0);
+ Yeq2 := Yeq / 3.0;
if ForceBalanced and (Fnphases = 3) then
begin // convert to pos-seq only
@@ -2025,54 +1487,19 @@ procedure TStorageObj.DoConstantZStorageObj;
for i := 1 to Fnphases do
begin
-
- Curr := Cmul(Yeq2, Vterminal^[i]); // Yeq is always line to neutral
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ Curr := Yeq2 * Vterminal^[i]; // Yeq is always line to neutral
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
-
-end;
-
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TStorageObj.DoUserModel;
-{Compute total terminal Current from User-written model}
-var
- i: Integer;
-
-begin
-
- CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
-
- if UserModel.Exists then // Check automatically selects the usermodel If true
- begin
- UserModel.FCalc(Vterminal, Iterminal);
- IterminalUpdated := TRUE;
- with ActiveCircuit.Solution do
- begin // Negate currents from user model for power flow Storage element model
- for i := 1 to FnConds do
- Caccum(InjCurrent^[i], Cnegate(Iterminal^[i]));
- end;
- end
- else
- begin
- DoSimpleMsg('Storage.' + name + ' model designated to use user-written model, but user-written model is not defined.', 567);
- end;
-
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorageObj.DoDynamicMode;
+// Compute Total Current and add into InjTemp
-{Compute Total Current and add into InjTemp}
-{
- For now, just assume the storage element Thevenin voltage is constant
- for the duration of the dynamic simulation.
-}
-{****}
+// For now, just assume the storage element Thevenin voltage is constant
+// for the duration of the dynamic simulation.
var
i: Integer;
V012,
@@ -2086,154 +1513,103 @@ procedure TStorageObj.DoDynamicMode;
end;
begin
+ // Test using DESS model
+ // Compute Vterminal
+ CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
+ ZeroITerminal;
-{****} // Test using DESS model
- // Compute Vterminal
+ // Simple Thevenin equivalent
+ // compute terminal current (Iterminal) and take out the Yprim contribution
- if DynaModel.Exists then
- DoDynaModel // do user-written model
+ with StorageVars do
+ case Fnphases of
+ 1:
+ begin
+ CalcVthev_Dyn; // Update for latest phase angle
+ ITerminal^[1] := (VTerminal^[1] - Vthev - VTerminal^[2]) / Zthev;
+ if CurrentLimited then
+ if Cabs(Iterminal^[1]) > MaxDynPhaseCurrent then // Limit the current but keep phase angle
+ ITerminal^[1] := ptocomplex(topolar(MaxDynPhaseCurrent, cang(Iterminal^[1])));
+ ITerminal^[2] := -ITerminal^[1];
+ end;
+ 3:
+ begin
+ Phase2SymComp(Vterminal, pComplexArray(@V012));
- else
- begin
+ // Positive Sequence Contribution to Iterminal
+ CalcVthev_Dyn; // Update for latest phase angle
- CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
- ZeroITerminal;
+ // Positive Sequence Contribution to Iterminal
+ I012[1] := (V012[1] - Vthev) / Zthev;
- // Simple Thevenin equivalent
- // compute terminal current (Iterminal) and take out the Yprim contribution
+ if CurrentLimited and (Cabs(I012[1]) > MaxDynPhaseCurrent) then // Limit the pos seq current but keep phase angle
+ I012[1] := ptocomplex(topolar(MaxDynPhaseCurrent, cang(I012[1])));
- with StorageVars do
- case Fnphases of
- 1:
- begin
- CalcVthev_Dyn; // Update for latest phase angle
- ITerminal^[1] := CDiv(CSub(Csub(VTerminal^[1], Vthev), VTerminal^[2]), Zthev);
- if CurrentLimited then
- if Cabs(Iterminal^[1]) > MaxDynPhaseCurrent then // Limit the current but keep phase angle
- ITerminal^[1] := ptocomplex(topolar(MaxDynPhaseCurrent, cang(Iterminal^[1])));
- ITerminal^[2] := Cnegate(ITerminal^[1]);
- end;
- 3:
+ if ForceBalanced then
begin
- Phase2SymComp(Vterminal, pComplexArray(@V012));
-
- // Positive Sequence Contribution to Iterminal
- CalcVthev_Dyn; // Update for latest phase angle
-
- // Positive Sequence Contribution to Iterminal
- I012[1] := CDiv(Csub(V012[1], Vthev), Zthev);
-
- if CurrentLimited and (Cabs(I012[1]) > MaxDynPhaseCurrent) then // Limit the pos seq current but keep phase angle
- I012[1] := ptocomplex(topolar(MaxDynPhaseCurrent, cang(I012[1])));
-
- if ForceBalanced then
- begin
- I012[2] := CZERO;
- end
- else
- I012[2] := Cdiv(V012[2], Zthev); // for inverter
+ I012[2] := CZERO;
+ end
+ else
+ I012[2] := V012[2] / Zthev; // for inverter
- I012[0] := CZERO;
+ I012[0] := CZERO;
- SymComp2Phase(ITerminal, pComplexArray(@I012)); // Convert back to phase components
+ SymComp2Phase(ITerminal, pComplexArray(@I012)); // Convert back to phase components
- end;
- else
- DoSimpleMsg(Format('Dynamics mode is implemented only for 1- or 3-phase Storage Element. Storage.%s has %d phases.', [name, Fnphases]), 5671);
- DSS.SolutionAbort := TRUE;
end;
+ else
+ DoSimpleMsg('Dynamics mode is implemented only for 1- or 3-phase Storage Element. %s has %d phases.', [FullName, Fnphases], 5671);
+ DSS.SolutionAbort := TRUE;
+ end;
- {Add it into inj current array}
- for i := 1 to FnConds do
- Caccum(InjCurrent^[i], Cnegate(Iterminal^[i]));
-
- end;
-
-end;
-
-
-procedure TStorageObj.DoDynaModel;
-var
- DESSCurr: array[1..6] of Complex; // Temporary biffer
- i: Integer;
-
-begin
-// do user written dynamics model
-
- with ActiveCircuit.Solution do
- begin // Just pass node voltages to ground and let dynamic model take care of it
- for i := 1 to FNconds do
- VTerminal^[i] := NodeV^[NodeRef^[i]];
- StorageVars.w_grid := TwoPi * Frequency;
- end;
-
- DynaModel.FCalc(Vterminal, pComplexArray(@DESSCurr));
-
- CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
- ZeroITerminal;
-
- for i := 1 to Fnphases do
- begin
- StickCurrInTerminalArray(ITerminal, Cnegate(DESSCurr[i]), i); // Put into Terminal array taking into account connection
- IterminalUpdated := TRUE;
- StickCurrInTerminalArray(InjCurrent, DESSCurr[i], i); // Put into Terminal array taking into account connection
- end;
-
+ // Add it into inj current array
+ for i := 1 to FnConds do
+ InjCurrent^[i] -= Iterminal^[i];
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorageObj.DoHarmonicMode;
+// Compute Injection Current Only when in harmonics mode
-{Compute Injection Current Only when in harmonics mode}
-
-{Assumes spectrum is a voltage source behind subtransient reactance and YPrim has been built}
-{Vd is the fundamental frequency voltage behind Xd" for phase 1}
-
+// Assumes spectrum is a voltage source behind subtransient reactance and YPrim has been built
+// Vd is the fundamental frequency voltage behind Xd" for phase 1
var
i: Integer;
E: Complex;
StorageHarmonic: Double;
-
+ pBuffer: PCBuffer24;
begin
-
+ pBuffer := @TStorage(ParentClass).cBuffer;
ComputeVterminal;
with ActiveCircuit.Solution do
begin
StorageHarmonic := Frequency / StorageFundamental;
if SpectrumObj <> NIL then
- E := CmulReal(SpectrumObj.GetMult(StorageHarmonic), StorageVars.VThevHarm) // Get base harmonic magnitude
+ E := SpectrumObj.GetMult(StorageHarmonic) * StorageVars.VThevHarm // Get base harmonic magnitude
else
E := CZERO;
RotatePhasorRad(E, StorageHarmonic, StorageVars.ThetaHarm); // Time shift by fundamental frequency phase shift
for i := 1 to Fnphases do
begin
- cBuffer[i] := E;
+ pBuffer[i] := E;
if i < Fnphases then
RotatePhasorDeg(E, StorageHarmonic, -120.0); // Assume 3-phase Storage element
end;
end;
- {Handle Wye Connection}
+ // Handle Wye Connection
if Connection = 0 then
- cbuffer[Fnconds] := Vterminal^[Fnconds]; // assume no neutral injection voltage
-
- {Inj currents = Yprim (E) }
- YPrim.MVMult(InjCurrent, pComplexArray(@cBuffer));
+ pBuffer[Fnconds] := Vterminal^[Fnconds]; // assume no neutral injection voltage
+ // Inj currents = Yprim (E)
+ YPrim.MVMult(InjCurrent, pComplexArray(pBuffer));
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorageObj.CalcVTerminalPhase;
-
var
i, j: Integer;
-
begin
-
-{ Establish phase voltages and stick in Vterminal}
+ // Establish phase voltages and stick in Vterminal
case Connection of
0:
@@ -2258,15 +1634,11 @@ procedure TStorageObj.CalcVTerminalPhase;
end;
StorageSolutionCount := ActiveCircuit.Solution.SolutionCount;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorageObj.CalcStorageModelContribution;
-
// Calculates Storage element current and adds it properly into the injcurrent array
// routines may also compute ITerminal (ITerminalUpdated flag)
-
begin
IterminalUpdated := FALSE;
with ActiveCircuit, ActiveCircuit.Solution do
@@ -2278,25 +1650,20 @@ procedure TStorageObj.CalcStorageModelContribution;
DoHarmonicMode
else
begin
- // compute currents and put into InjTemp array;
+ // compute currents and put into InjTemp array;
case VoltageModel of
1:
DoConstantPQStorageObj;
2:
DoConstantZStorageObj;
- 3:
- DoUserModel;
else
DoConstantPQStorageObj; // for now, until we implement the other models.
end;
- end; {ELSE}
- end; {WITH}
-
- {When this is Done, ITerminal is up to date}
-
+ end;
+ end;
+ // When this is Done, ITerminal is up to date
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorageObj.CalcInjCurrentArray;
// Difference between currents in YPrim and total current
begin
@@ -2307,11 +1674,8 @@ procedure TStorageObj.CalcInjCurrentArray;
CalcStorageModelContribution;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorageObj.GetTerminalCurrents(Curr: pComplexArray);
-
// Compute total Currents
-
begin
with ActiveCircuit.Solution do
begin
@@ -2325,12 +1689,9 @@ procedure TStorageObj.GetTerminalCurrents(Curr: pComplexArray);
if (DebugTrace) then
WriteTraceRecord('TotalCurrent');
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
function TStorageObj.InjCurrents: Integer;
-
begin
with ActiveCircuit.Solution do
begin
@@ -2348,12 +1709,9 @@ function TStorageObj.InjCurrents: Integer;
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TStorageObj.ResetRegisters;
-
var
i: Integer;
-
begin
for i := 1 to NumStorageRegisters do
Registers[i] := 0.0;
@@ -2362,38 +1720,31 @@ procedure TStorageObj.ResetRegisters;
FirstSampleAfterReset := TRUE; // initialize for trapezoidal integration
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TStorageObj.Integrate(Reg: Integer; const Deriv: Double; const Interval: Double);
-
begin
if ActiveCircuit.TrapezoidalIntegration then
begin
- {Trapezoidal Rule Integration}
+ // Trapezoidal Rule Integration
if not FirstSampleAfterReset then
Registers[Reg] := Registers[Reg] + 0.5 * Interval * (Deriv + Derivatives[Reg]);
end
- else {Plain Euler integration}
+ else // Plain Euler integration
Registers[Reg] := Registers[Reg] + Interval * Deriv;
Derivatives[Reg] := Deriv;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TStorageObj.TakeSample;
// Update Energy from metered zone
-
var
S: Complex;
Smag: Double;
HourValue: Double;
-
begin
-
-// Compute energy in Storage element branch
+ // Compute energy in Storage element branch
if Enabled then
begin
-
- // Only tabulate discharge hours
+ // Only tabulate discharge hours
if FSTate = STORE_DISCHARGING then
begin
S := cmplx(Get_PresentkW, Get_Presentkvar);
@@ -2408,13 +1759,13 @@ procedure TStorageObj.TakeSample;
end;
if (FState = STORE_DISCHARGING) or ActiveCircuit.TrapezoidalIntegration then
- {Make sure we always integrate for Trapezoidal case
- Don't need to for Gen Off and normal integration}
+ // Make sure we always integrate for Trapezoidal case
+ // Don't need to for Gen Off and normal integration
with ActiveCircuit.Solution do
begin
if ActiveCircuit.PositiveSequence then
begin
- S := CmulReal(S, 3.0);
+ S := S * 3;
Smag := 3.0 * Smag;
end;
Integrate(Reg_kWh, S.re, IntervalHrs); // Accumulate the power
@@ -2428,27 +1779,19 @@ procedure TStorageObj.TakeSample;
end;
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.UpdateStorage;
-{Update Storage levels}
+// Update Storage levels
begin
-
with StorageVars do
begin
-
kWhBeforeUpdate := kWhStored; // keep this for reporting change in storage as a variable
- {Assume User model will take care of updating storage in dynamics mode}
- if ActiveCircuit.solution.IsDynamicModel and IsUserModel then
- Exit;
-
-
with ActiveCircuit.Solution do
case FState of
STORE_DISCHARGING:
begin
- {Deplete storage by amount of Idling Power to achieve Present kW output}
+ // Deplete storage by amount of Idling Power to achieve Present kW output
kWhStored := kWhStored - (PresentkW + kWIdlingLosses) * IntervalHrs / DischargeEff;
if kWhStored < kWhReserve then
begin
@@ -2460,7 +1803,7 @@ procedure TStorageObj.UpdateStorage;
STORE_CHARGING:
begin
- {kWIdlingLosses is always positive while PresentkW is negative for Charging}
+ // kWIdlingLosses is always positive while PresentkW is negative for Charging
kWhStored := kWhStored - (PresentkW + kWIdlingLosses) * IntervalHrs * ChargeEff;
if kWhStored > kWhRating then
begin
@@ -2477,16 +1820,13 @@ procedure TStorageObj.UpdateStorage;
// a recalc of the Yprim for the next time step. Else it will stay the same.
if FstateChanged then
YPrimInvalid := TRUE;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
function TStorageObj.Get_PresentkW: Double;
begin
Result := kW_Out; //Pnominalperphase * 0.001 * Fnphases;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
function TStorageObj.Get_kWTotalLosses: Double;
begin
Result := 0.0;
@@ -2507,9 +1847,9 @@ function TStorageObj.Get_kWIdlingLosses: Double;
ComputeVterminal;
Result := 0.0;
- // Compute sum of SQR(V) at this device -- sum of VV*
+ // Compute sum of SQR(V) at this device -- sum of VV*
for i := 1 to FNphases do
- Result := Result + Cmul(Vterminal^[i], Conjg(VTerminal^[i])).re;
+ Result := Result + (Vterminal^[i] * cong(VTerminal^[i])).re;
Result := Result * YeqIdling.re * 0.001; // to kW
@@ -2525,49 +1865,17 @@ function TStorageObj.Get_Presentkvar: Double;
Result := kvar_out; // Qnominalperphase * 0.001 * Fnphases;
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TStorageObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i, idx: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- idx := PropertyIdxMap[i];
- case idx of
- propUSERDATA:
- FSWriteln(F, '~ ' + PropertyName^[i] + '=(' + PropertyValue[idx] + ')');
- propDynaData:
- FSWriteln(F, '~ ' + PropertyName^[i] + '=(' + PropertyValue[idx] + ')');
- else
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[idx]);
- end;
- end;
-
- FSWriteln(F);
-end;
-
-
-//----------------------------------------------------------------------------
procedure TStorageObj.InitHarmonics;
-
// This routine makes a thevenin equivalent behis the reactance spec'd in %R and %X
-
var
E, Va: complex;
-
begin
YPrimInvalid := TRUE; // Force rebuild of YPrims
StorageFundamental := ActiveCircuit.Solution.Frequency; // Whatever the frequency is when we enter here.
Yeq := Cinv(Cmplx(StorageVars.RThev, StorageVars.XThev)); // used for current calcs Always L-N
- {Compute reference Thevinen voltage from phase 1 current}
+ // Compute reference Thevinen voltage from phase 1 current
if FState = STORE_DISCHARGING then
begin
@@ -2576,16 +1884,16 @@ procedure TStorageObj.InitHarmonics;
with ActiveCircuit.solution do
case Connection of
0:
- begin {wye - neutral is explicit}
- Va := Csub(NodeV^[NodeRef^[1]], NodeV^[NodeRef^[Fnconds]]);
+ begin // wye - neutral is explicit
+ Va := NodeV^[NodeRef^[1]] - NodeV^[NodeRef^[Fnconds]];
end;
1:
- begin {delta -- assume neutral is at zero}
+ begin // delta -- assume neutral is at zero
Va := NodeV^[NodeRef^[1]];
end;
end;
- E := Csub(Va, Cmul(Iterminal^[1], cmplx(StorageVars.Rthev, StorageVars.Xthev)));
+ E := Va - Iterminal^[1] * cmplx(StorageVars.Rthev, StorageVars.Xthev);
StorageVars.Vthevharm := Cabs(E); // establish base mag and angle
StorageVars.ThetaHarm := Cang(E);
end
@@ -2596,10 +1904,7 @@ procedure TStorageObj.InitHarmonics;
end;
end;
-
-//----------------------------------------------------------------------------
procedure TStorageObj.InitStateVars;
-
// for going into dynamics mode
var
// VNeut: Complex;
@@ -2608,10 +1913,7 @@ procedure TStorageObj.InitStateVars;
V012,
I012: array[0..2] of Complex;
Vabc: array[1..3] of Complex;
-
-
begin
-
YPrimInvalid := TRUE; // Force rebuild of YPrims
with StorageVars do
@@ -2620,149 +1922,88 @@ procedure TStorageObj.InitStateVars;
Yeq := Cinv(ZThev); // used to init state vars
end;
-
- if DynaModel.Exists then // Checks existence and selects
- begin
- ComputeIterminal;
- ComputeVterminal;
- with StorageVars do
+ // Compute nominal Positive sequence voltage behind equivalent filter impedance
+ if FState = STORE_DISCHARGING then
+ with ActiveCircuit.Solution do
begin
- NumPhases := Fnphases;
- NumConductors := Fnconds;
- w_grid := twopi * ActiveCircuit.Solution.Frequency;
- end;
- DynaModel.FInit(Vterminal, Iterminal);
- end
-
- else
- begin
+ ComputeIterminal;
- {Compute nominal Positive sequence voltage behind equivalent filter impedance}
-
- if FState = STORE_DISCHARGING then
- with ActiveCircuit.Solution do
+ if FnPhases = 3 then
begin
- ComputeIterminal;
-
- if FnPhases = 3 then
- begin
- Phase2SymComp(ITerminal, pComplexArray(@I012));
+ Phase2SymComp(ITerminal, pComplexArray(@I012));
// Voltage behind Xdp (transient reactance), volts
- // case Connection of
- // 0:
- // Vneut := NodeV^[NodeRef^[Fnconds]]
- // else
- // Vneut := CZERO;
- // end;
-
- for i := 1 to FNphases do
- Vabc[i] := NodeV^[NodeRef^[i]]; // Wye Voltage
-
- Phase2SymComp(pComplexArray(@Vabc), pComplexArray(@V012));
- with StorageVars do
- begin
- Vthev := Csub(V012[1], Cmul(I012[1], ZThev)); // Pos sequence
- VThevPolar := cToPolar(VThev);
- VThevMag := VThevPolar.mag;
- Theta := VThevPolar.ang; // Initial phase angle
- end;
- end
- else
- begin // Single-phase Element
- for i := 1 to Fnconds do
- Vabc[i] := NodeV^[NodeRef^[i]];
- with StorageVars do
- begin
- Vthev := Csub(VDiff(NodeRef^[1], NodeRef^[2]), Cmul(ITerminal^[1], ZThev)); // Pos sequence
- VThevPolar := cToPolar(VThev);
- VThevMag := VThevPolar.mag;
- Theta := VThevPolar.ang; // Initial phase angle
- end;
+ // case Connection of
+ // 0:
+ // Vneut := NodeV^[NodeRef^[Fnconds]]
+ // else
+ // Vneut := CZERO;
+ // end;
+ for i := 1 to FNphases do
+ Vabc[i] := NodeV^[NodeRef^[i]]; // Wye Voltage
+
+ Phase2SymComp(pComplexArray(@Vabc), pComplexArray(@V012));
+ with StorageVars do
+ begin
+ Vthev := V012[1] - I012[1] * ZThev; // Pos sequence
+ VThevPolar := cToPolar(VThev);
+ VThevMag := VThevPolar.mag;
+ Theta := VThevPolar.ang; // Initial phase angle
+ end;
+ end
+ else
+ begin // Single-phase Element
+ for i := 1 to Fnconds do
+ Vabc[i] := NodeV^[NodeRef^[i]];
+ with StorageVars do
+ begin
+ Vthev := VDiff(NodeRef^[1], NodeRef^[2]) - ITerminal^[1] * ZThev; // Pos sequence
+ VThevPolar := cToPolar(VThev);
+ VThevMag := VThevPolar.mag;
+ Theta := VThevPolar.ang; // Initial phase angle
end;
- end;
- end;
+ end;
+ end;
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.IntegrateStates;
-
// dynamics mode integration routine
-
-//var
-// TracePower: Complex;
-
begin
// Compute Derivatives and Then integrate
ComputeIterminal;
- if Dynamodel.Exists then // Checks for existence and Selects
-
- DynaModel.Integrate
-
- else
- with ActiveCircuit.Solution, StorageVars do
- begin
-
- with StorageVars do
- if (Dynavars.IterationFlag = 0) then
- begin {First iteration of new time step}
- end;
-
- // Compute shaft dynamics
- // TracePower := TerminalPowerIn(Vterminal, Iterminal, FnPhases);
- // Write Dynamics Trace Record
- if DebugTrace then
- begin
- FSWrite(TraceFile, Format('t=%-.5g ', [Dynavars.t]));
- FSWrite(TraceFile, Format(' Flag=%d ', [Dynavars.Iterationflag]));
- FSWriteln(TraceFile);
- FSFlush(TraceFile);
+ with ActiveCircuit.Solution, StorageVars do
+ begin
+ with StorageVars do
+ if (Dynavars.IterationFlag = 0) then
+ begin // First iteration of new time step
end;
+ // Compute shaft dynamics
+ // TracePower := TerminalPowerIn(Vterminal, Iterminal, FnPhases);
+ // Write Dynamics Trace Record
+ if DebugTrace then
+ begin
+ FSWrite(TraceFile, Format('t=%-.5g ', [Dynavars.t]));
+ FSWrite(TraceFile, Format(' Flag=%d ', [Dynavars.Iterationflag]));
+ FSWriteln(TraceFile);
+ FSFlush(TraceFile);
end;
-end;
-
-//----------------------------------------------------------------------------
-function TStorageObj.InterpretState(const S: String): Integer;
-begin
- case LowerCase(S)[1] of
- 'c':
- Result := STORE_CHARGING;
- 'd':
- Result := STORE_DISCHARGING;
- else
- Result := STORE_IDLING;
end;
end;
-{ apparently for debugging only
-//----------------------------------------------------------------------------
-Function TStorageObj.StateToStr:String;
-Begin
- CASE FState of
- STORE_CHARGING: Result := 'Charging';
- STORE_IDLING: Result := 'Idling';
- STORE_DISCHARGING: Result := 'Discharging';
- END;
-End;
-}
-
-//----------------------------------------------------------------------------
function TStorageObj.Get_Variable(i: Integer): Double;
-{Return variables one at a time}
-
+// Return variables one at a time
var
N, k: Integer;
-
begin
Result := -9999.99; // error return value; no state fars
if i < 1 then
Exit;
-// for now, report kWhstored and mode
+ // for now, report kWhstored and mode
with StorageVars do
case i of
1:
@@ -2780,38 +2021,14 @@ function TStorageObj.Get_Variable(i: Integer): Double;
else
Result := Power[1].re * 0.001; // kW_out; // pctkWin;
5:
- Result := kWTotalLosses; {Present kW charge or discharge loss incl idle losses}
+ Result := kWTotalLosses; // Present kW charge or discharge loss incl idle losses
6:
- Result := kWIdlingLosses; {Present Idling Loss}
+ Result := kWIdlingLosses; // Present Idling Loss
7:
Result := kWhStored - kWhBeforeUpdate;
- else
- begin
- if UserModel.Exists then // Checks for existence and Selects
- begin
- N := UserModel.FNumVars;
- k := (i - NumStorageVariables);
- if k <= N then
- begin
- Result := UserModel.FGetVariable(k);
- Exit;
- end;
- end;
- if DynaModel.Exists then // Checks for existence and Selects
- begin
- N := DynaModel.FNumVars;
- k := (i - NumStorageVariables);
- if k <= N then
- begin
- Result := DynaModel.FGetVariable(k);
- Exit;
- end;
- end;
- end;
end;
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.Set_Variable(i: Integer; Value: Double);
var
N, k: Integer;
@@ -2830,69 +2047,23 @@ procedure TStorageObj.Set_Variable(i: Integer; Value: Double);
pctkWout := Value;
4:
pctkWin := Value;
- 5..7: ; {Do Nothing; read only}
- else
- begin
- if UserModel.Exists then // Checks for existence and Selects
- begin
- N := UserModel.FNumVars;
- k := (i - NumStorageVariables);
- if k <= N then
- begin
- UserModel.FSetVariable(k, Value);
- Exit;
- end;
- end;
- if DynaModel.Exists then // Checks for existence and Selects
- begin
- N := DynaModel.FNumVars;
- k := (i - NumStorageVariables);
- if k <= N then
- begin
- DynaModel.FSetVariable(k, Value);
- Exit;
- end;
- end;
+ 5..7: ; // Do Nothing; read only
end;
- end;
-
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.GetAllVariables(States: pDoubleArray);
-
var
- i{, N}: Integer;
+ i: Integer;
begin
for i := 1 to NumStorageVariables do
States^[i] := Variable[i];
-
- if UserModel.Exists then
- begin // Checks for existence and Selects
- {N := UserModel.FNumVars;}
- UserModel.FGetAllVars(pDoubleArray(@States^[NumStorageVariables + 1]));
- end;
- if DynaModel.Exists then
- begin // Checks for existence and Selects
- {N := UserModel.FNumVars;}
- DynaModel.FGetAllVars(pDoubleArray(@States^[NumStorageVariables + 1]));
- end;
-
end;
-//----------------------------------------------------------------------------
function TStorageObj.NumVariables: Integer;
begin
Result := NumStorageVariables;
-
- // Exists does a check and then does a Select
- if UserModel.Exists then
- Result := Result + UserModel.FNumVars;
- if DynaModel.Exists then
- Result := Result + DynaModel.FNumVars;
end;
-//----------------------------------------------------------------------------
function TStorageObj.VariableName(i: Integer): String;
const
@@ -2922,85 +2093,57 @@ function TStorageObj.VariableName(i: Integer): String;
Result := 'Idling';
7:
Result := 'kWh Chng';
- else
- begin
- if UserModel.Exists then // Checks for existence and Selects
- begin
- pName := PAnsiChar(@Buff);
- n := UserModel.FNumVars;
- i2 := i - NumStorageVariables;
- if i2 <= n then
- begin
- UserModel.FGetVarName(i2, pName, BuffSize);
- Result := String(pName);
- Exit;
- end;
- end;
- if DynaModel.Exists then // Checks for existence and Selects
- begin
- pName := PAnsiChar(@Buff);
- n := DynaModel.FNumVars;
- i2 := i - NumStorageVariables; // Relative index
- if i2 <= n then
- begin
- DynaModel.FGetVarName(i2, pName, BuffSize);
- Result := String(pName);
- Exit;
- end;
- end;
- end;
end;
-
end;
-//----------------------------------------------------------------------------
-procedure TStorageObj.MakePosSequence;
-
+procedure TStorageObj.MakePosSequence();
var
- S: String;
- V: Double;
-
+ newkW, newPF, V: Double;
+ oldPhases, changes: Integer;
begin
-
- S := 'Phases=1 conn=wye';
-
- // Make sure voltage is line-neutral
+ // Make sure voltage is line-neutral
if (Fnphases > 1) or (connection <> 0) then
V := StorageVars.kVStorageBase / SQRT3
else
V := StorageVars.kVStorageBase;
- S := S + Format(' kV=%-.5g', [V]);
-
- if Fnphases > 1 then
+ oldPhases := FnPhases;
+ changes := 3;
+ if oldPhases > 1 then
begin
- S := S + Format(' kWrating=%-.5g PF=%-.5g', [StorageVars.kWrating / Fnphases, PFNominal]);
+ newkW := StorageVars.kWrating / Fnphases;
+ newPF := PFNominal;
+ changes := changes + 2;
end;
-
- Parser.CmdString := S;
- Edit;
+ SetInteger(ord(TProp.Phases), 1);
+ SetInteger(ord(TProp.conn), 0);
+ SetDouble(ord(TProp.kV), V);
+ if oldPhases > 1 then
+ begin
+ SetDouble(ord(TProp.kWrated), newkW);
+ SetDouble(ord(TProp.PF), newPF);
+ end;
+ EndEdit(changes);
inherited; // write out other properties
end;
-procedure TStorageObj.Set_ConductorClosed(Index: Integer;
- Value: Boolean);
+procedure TStorageObj.Set_ConductorClosed(Index: Integer; Value: Boolean);
begin
inherited;
- // Just turn storage element on or off;
+ // Just turn storage element on or off;
if Value then
StorageObjSwitchOpen := FALSE
else
StorageObjSwitchOpen := TRUE;
-
end;
procedure TStorageObj.Set_pctkvarOut(const Value: Double);
begin
FpctkvarOut := Value;
- // Force recompute of target PF and requested kVAr
+ // Force recompute of target PF and requested kVAr
Presentkvar := StorageVars.kWRating * sqrt(1.0 / SQR(PFNominal) - 1.0) * FpctkvarOut / 100.0;
end;
@@ -3010,14 +2153,12 @@ procedure TStorageObj.Set_pctkWOut(const Value: Double);
kW_Out := FpctkWOut * StorageVars.kWRating / 100.0;
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.Set_PowerFactor(const Value: Double);
begin
PFNominal := Value;
SyncUpPowerQuantities;
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.Set_PresentkV(const Value: Double);
begin
StorageVars.kVStorageBase := Value;
@@ -3029,7 +2170,6 @@ procedure TStorageObj.Set_PresentkV(const Value: Double);
end;
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.Set_Presentkvar(const Value: Double);
// set the kvar to requested value within rating of inverter
var
@@ -3037,7 +2177,7 @@ procedure TStorageObj.Set_Presentkvar(const Value: Double);
begin
kvar_out := Value;
StorageVars.kvarRequested := Value;
- {Requested kVA output}
+ // Requested kVA output
kVA_Gen := Sqrt(Sqr(kW_out) + Sqr(kvar_out));
with StorageVars do
if kVA_Gen > kVArating then
@@ -3050,7 +2190,6 @@ procedure TStorageObj.Set_Presentkvar(const Value: Double);
PFNominal := -PFNominal;
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.Set_PresentkW(const Value: Double);
begin
FpctkWOut := Value / StorageVars.kWRating * 100.0;
@@ -3094,10 +2233,8 @@ procedure TStorageObj.Set_StorageState(const Value: Integer);
//---DEBUG--- WriteDLLDebugFile(Format('t=%.8g, ---State Set To %s', [ActiveCircuit.Solution.dblHour, StateToStr ]));
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.SyncUpPowerQuantities;
begin
-
if kVANotSet then
StorageVars.kVARating := StorageVars.kWrating;
kvar_out := 0.0;
@@ -3113,17 +2250,13 @@ procedure TStorageObj.SyncUpPowerQuantities;
kvarbase := kvar_out; // remember for follow mode; synch up here
end;
-//----------------------------------------------------------------------------
procedure TStorageObj.SetDragHandRegister(Reg: Integer; const Value: Double);
begin
if Value > Registers[reg] then
Registers[Reg] := Value;
end;
-//----------------------------------------------------------------------------
-
-initialization
-
- CDOUBLEONE := CMPLX(1.0, 1.0);
-
+finalization
+ DispatchModeEnum.Free;
+ StateEnum.Free;
end.
diff --git a/src/PCElements/Storage2.pas b/src/PCElements/Storage2.pas
index 445765724..ba3ee9627 100644
--- a/src/PCElements/Storage2.pas
+++ b/src/PCElements/Storage2.pas
@@ -7,41 +7,32 @@
----------------------------------------------------------
}
-{ Change Log
-
- 10/04/2009 Created from Generator Model
-
-
- To Do:
- Make connection to User model
- Yprim for various modes
- Define state vars and dynamics mode behavior
- Complete Harmonics mode algorithm (generator mode is implemented)
-}
-{
- The Storage element is essentially a generator that can be dispatched
- to either produce power or consume power commensurate with rating and
- amount of stored energy.
-
- The Storage element can also produce or absorb vars within the kVA rating of the inverter.
- That is, a StorageController object requests kvar and the Storage element provides them if
- it has any capacity left. The Storage element can produce/absorb kvar while idling.
-}
+// To Do:
+// Make connection to User model
+// Yprim for various modes
+// Define state vars and dynamics mode behavior
+// Complete Harmonics mode algorithm (generator mode is implemented)
+
+// The Storage element is essentially a generator that can be dispatched
+// to either produce power or consume power commensurate with rating and
+// amount of stored energy.
+//
+// The Storage element can also produce or absorb vars within the kVA rating of the inverter.
+// That is, a StorageController object requests kvar and the Storage element provides them if
+// it has any capacity left. The Storage element can produce/absorb kvar while idling.
// The Storage element is assumed balanced over the no. of phases defined
-
interface
uses
Classes,
- Storage2Vars,
StoreUserModel,
DSSClass,
PCClass,
PCElement,
ucmatrix,
- ucomplex,
+ UComplex, DSSUcomplex,
LoadShape,
Spectrum,
ArrayDef,
@@ -67,25 +58,131 @@ interface
STORE_FOLLOW = 4;
type
+{$SCOPEDENUMS ON}
+ TStorage2Prop = (
+ INVALID = 0,
+ phases = 1,
+ bus1 = 2,
+ kv = 3, // propKV
+ conn = 4, // propCONNECTION
+ kW = 5, // propKW
+ kvar = 6, // propKVAR
+ pf = 7, // propPF
+ kVA = 8, // propKVA
+ pctCutin = 9, // propCutin
+ pctCutout = 10, // propCutout
+ EffCurve = 11, // propInvEffCurve
+ VarFollowInverter = 12, // propVarFollowInverter
+ kvarMax = 13, // propkvarLimit
+ kvarMaxAbs = 14, // propkvarLimitneg
+ WattPriority = 15, // propPpriority
+ PFPriority = 16, // propPFPriority
+ pctPminNoVars = 17, // propPminNoVars
+ pctPminkvarMax = 18, // propPminkvarLimit
+ kWrated = 19, // propKWRATED
+ pctkWrated = 20, // proppctkWrated
+ kWhrated = 21, // propKWHRATED
+ kWhstored = 22, // propKWHSTORED
+ pctstored = 23, // propPCTSTORED
+ pctreserve = 24, // propPCTRESERVE
+ State = 25, // propSTATE
+ pctDischarge = 26, // propPCTKWOUT
+ pctCharge = 27, // propPCTKWIN
+ pctEffCharge = 28, // propCHARGEEFF
+ pctEffDischarge = 29, // propDISCHARGEEFF
+ pctIdlingkW = 30, // propIDLEKW
+
+ // pctIdlingkvar = 31, // propIDLEKVAR --- was deprecated, removed
+
+ pctR, // propPCTR
+ pctX, // propPCTX
+ model, // propMODEL
+ Vminpu, // propVMINPU
+ Vmaxpu, // propVMAXPU
+ Balanced, // propBalanced
+ LimitCurrent, // propLimited
+ yearly, // propYEARLY
+ daily, // propDAILY
+ duty, // propDUTY
+ DispMode, // propDISPMODE
+ DischargeTrigger, // propDISPOUTTRIG
+ ChargeTrigger, // propDISPINTRIG
+ TimeChargeTrig, // propCHARGETIME
+ cls, // propCLASS
+ DynaDLL, // propDynaDLL
+ DynaData, // propDynaData
+ UserModel, // propUSERMODEL
+ UserData, // propUSERDATA
+ debugtrace = 50 // propDEBUGTRACE
+ );
+{$SCOPEDENUMS OFF}
+
+ // Struct to pass basic data to user-written DLLs
+ TStorage2Vars = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}packed{$ENDIF} record
+
+ kWrating: Double;
+ kWhRating: Double;
+ kWhStored: Double;
+ kWhReserve: Double;
+
+ ChargeEff: Double;
+ DisChargeEff: Double;
+ kVStorageBase: Double;
+ RThev: Double;
+ XThev: Double;
+
+ // Inverter Related Properties
+ FkVArating: Double;
+ Fkvarlimit: Double;
+ Fkvarlimitneg: Double;
+ P_Priority: LongBool;
+ PF_Priority: LongBool;
+ pctkWrated: Double;
+ EffFactor: Double;
+
+
+ // Interaction with InvControl
+ Vreg: Double;
+ Vavg: Double;
+ VVOperation: Double;
+ VWOperation: Double;
+ DRCOperation: Double;
+ VVDRCOperation: Double;
+ WPOperation: Double;
+ WVOperation: Double;
+// kW_out_desired :Double;
+
+
+ // Dynamics variables
+ Vthev: Complex; {Thevenin equivalent voltage (complex) for dynamic model}
+ ZThev: Complex;
+ Vthevharm: Double; {Thevenin equivalent voltage mag and angle reference for Harmonic model}
+ Thetaharm: Double; {Thevenin equivalent voltage mag and angle reference for Harmonic model}
+ VthevMag: Double; {Thevenin equivalent voltage for dynamic model}
+ Theta: Double; {Power angle between voltage and current}
+ w_grid: Double; {Grid frequency}
+ TotalLosses: Double;
+ IdlingLosses: Double;
+
+ {32-bit integers}
+ NumPhases, {Number of phases}
+ NumConductors, {Total Number of conductors (wye-connected will have 4)}
+ Conn: Integer; // 0 = wye; 1 = Delta
+ end;
-
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TStorage2 = class(TPCClass)
- PRIVATE
-
- procedure InterpretConnection(const S: String);
- procedure SetNcondsForConnection;
PROTECTED
- procedure DefineProperties;
- function MakeLike(const OtherStorage2ObjName: String): Integer; OVERRIDE;
+ cBuffer: TCBuffer24; // Temp buffer for calcs 24-phase Storage element?
+
+ procedure DefineProperties; override;
PUBLIC
RegisterNames: array[1..NumStorage2Registers] of String;
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit(): Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
procedure ResetRegistersAll;
procedure SampleAll();
@@ -93,7 +190,6 @@ TStorage2 = class(TPCClass)
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TStorage2Obj = class(TPCElement)
PRIVATE
Yeq: Complex; // at nominal
@@ -104,40 +200,29 @@ TStorage2Obj = class(TPCElement)
PhaseCurrentLimit: Complex;
MaxDynPhaseCurrent: Double;
- DebugTrace: Boolean;
+ DebugTrace: LongBool;
FState: Integer;
FStateChanged: Boolean;
FirstSampleAfterReset: Boolean;
StorageSolutionCount: Integer;
- StorageFundamental: Double; {Thevenin equivalent voltage mag and angle reference for Harmonic model}
+ StorageFundamental: Double; // Thevenin equivalent voltage mag and angle reference for Harmonic model
Storage2ObjSwitchOpen: Boolean;
- ForceBalanced: Boolean;
- CurrentLimited: Boolean;
+ ForceBalanced: LongBool;
+ CurrentLimited: LongBool;
-// LoadSpecType :Integer; // 0=kW, PF; 1= kw, kvar;
kvar_out: Double;
kW_out: Double;
- FkvarRequested: Double;
- FkWRequested: Double;
- FvarMode: Integer;
FDCkW: Double;
- Fpf_wp_nominal: Double;
+ pf_wp_nominal: Double;
// Variables for Inverter functionalities
FpctCutIn: Double;
FpctCutOut: Double;
- FVarFollowInverter: Boolean;
CutInkW: Double;
CutOutkW: Double;
- FCutOutkWAC: Double; // CutInkW reflected to the AC side of the inverter
- FCutInkWAC: Double; // CutOutkW reflected to the AC side of the inverter
-
- FStateDesired: Integer; // Stores desired state (before any change due to kWh limits or %CutIn/%CutOut
-
- FInverterON: Boolean;
FpctPminNoVars: Double;
FpctPminkvarLimit: Double;
PminNoVars: Double;
@@ -167,24 +252,17 @@ TStorage2Obj = class(TPCElement)
Tracefile: TFileStream;
IsUserModel: Boolean;
- UserModel: TStoreUserModel; {User-Written Models}
+ UserModel: TStoreUserModel; // User-Written Models
DynaModel: TStoreDynaModel;
+ UserModelNameStr, UserModelEditStr: String;
+ DynaModelNameStr, DynaModelEditStr: String;
+
// VBase :Double; // Base volts suitable for computing currents made public
VBase105: Double;
VBase95: Double;
- Vmaxpu: Double;
- Vminpu: Double;
YPrimOpenCond: TCmatrix;
- // Variables for InvControl's Volt-Watt function
- FVWMode: Boolean; //boolean indicating if under volt-watt control mode from InvControl (not ExpControl)
- FVVMode: Boolean; //boolean indicating if under volt-var mode from InvControl
- FDRCMode: Boolean; //boolean indicating if under DRC mode from InvControl
- FWPMode: Boolean; //boolean indicating if under watt-pf mode from InvControl
- FWVMode: Boolean; //boolean indicating if under watt-var mode from InvControl
-
-
procedure CalcDailyMult(Hr: Double);
procedure CalcDutyMult(Hr: Double);
procedure CalcYearlyMult(Hr: Double);
@@ -196,7 +274,7 @@ TStorage2Obj = class(TPCElement)
procedure ComputeDCkW; // For Storage Update
procedure CalcStorageModelContribution();
procedure CalcInjCurrentArray();
- (*PROCEDURE CalcVterminal;*)
+ // PROCEDURE CalcVterminal;
procedure CalcVTerminalPhase();
procedure CalcYPrimMatrix(Ymatrix: TcMatrix);
@@ -218,29 +296,16 @@ TStorage2Obj = class(TPCElement)
procedure UpdateStorage(); // Update Storage elements based on present kW and IntervalHrs variable
function NormalizeToTOD(h: Integer; sec: Double): Double;
- function InterpretState(const S: String): Integer;
-// FUNCTION StateToStr:String;
- function DecodeState: String;
-
function Get_PresentkW: Double;
function Get_Presentkvar: Double;
function Get_PresentkV: Double;
- function Get_kvarRequested: Double;
- function Get_kWRequested: Double;
procedure Set_kW(const Value: Double);
function Get_kW: Double;
- procedure Set_PresentkV(const Value: Double);
procedure Set_PowerFactor(const Value: Double);
- procedure Set_kWRequested(const Value: Double);
- procedure Set_kvarRequested(const Value: Double);
- procedure Set_pf_wp_nominal(const Value: Double);
-
procedure Set_StorageState(const Value: Integer);
- procedure Set_pctkWOut(const Value: Double);
- procedure Set_pctkWIn(const Value: Double);
function Get_DCkW: Double;
function Get_kWTotalLosses: Double;
@@ -249,58 +314,40 @@ TStorage2Obj = class(TPCElement)
function Get_kWChDchLosses: Double;
procedure Update_EfficiencyFactor;
- procedure Set_StateDesired(i: Integer);
function Get_kWDesired: Double;
// Procedures and functions for inverter functionalities
procedure Set_kVARating(const Value: Double);
- procedure Set_pctkWrated(const Value: Double);
- function Get_Varmode: Integer;
- procedure Set_Varmode(const Value: Integer);
- function Get_VWmode: Boolean;
-
- procedure Set_VVmode(const Value: Boolean);
- function Get_VVmode: Boolean;
-
- procedure Set_DRCmode(const Value: Boolean);
- function Get_DRCmode: Boolean;
-
- procedure Set_VWmode(const Value: Boolean);
procedure kWOut_Calc;
- function Get_WPmode: Boolean;
- procedure Set_WPmode(const Value: Boolean);
-
- function Get_WVmode: Boolean;
- procedure Set_WVmode(const Value: Boolean);
-
- function Get_CutOutkWAC: Double;
- function Get_CutInkWAC: Double;
-
PROTECTED
procedure GetTerminalCurrents(Curr: pComplexArray); OVERRIDE;
PUBLIC
+ StateDesired: Integer; // Stores desired state (before any change due to kWh limits or %CutIn/%CutOut
StorageVars: TStorage2Vars;
+ AVRMode: Boolean; //boolean indicating whether under AVR mode from ExpControl (or InvControl, but that does not seem to be implemented yet)
+
VBase: Double; // Base volts suitable for computing currents
+ Vmaxpu: Double;
+ Vminpu: Double;
- Connection: Integer; {0 = line-neutral; 1=Delta}
- DailyShape: String; // Daily (24 HR) Storage element shape
+ Connection: Integer; // 0 = line-neutral; 1=Delta
DailyShapeObj: TLoadShapeObj; // Daily Storage element Shape for this load
- DutyShape: String; // Duty cycle load shape for changes typically less than one hour
DutyShapeObj: TLoadShapeObj; // Shape for this Storage element
- YearlyShape: String; // ='fixed' means no variation on all the time
YearlyShapeObj: TLoadShapeObj; // Shape for this Storage element
- FpctkWout: Double; // percent of kW rated output currently dispatched
- FpctkWin: Double;
+ pctkWOut: Double; // percent of kW rated output currently dispatched
+ pctkWIn: Double;
pctReserve: Double;
DispatchMode: Integer;
pctIdlekW: Double;
+ kvarRequested: Double;
+ kWRequested: Double;
kWOutIdling: Double;
@@ -314,8 +361,10 @@ TStorage2Obj = class(TPCElement)
CurrentkvarLimit: Double;
CurrentkvarLimitNeg: Double;
+ CutOutkWAC: Double; // CutInkW reflected to the AC side of the inverter
+ CutInkWAC: Double; // CutOutkW reflected to the AC side of the inverter
+
// Inverter efficiency curve
- InverterCurve: String;
InverterCurveObj: TXYCurveObj;
FVWStateRequested: Boolean; // TEST Flag indicating if VW function has requested a specific state in last control iteration
@@ -326,8 +375,21 @@ TStorage2Obj = class(TPCElement)
Registers, Derivatives: array[1..NumStorage2Registers] of Double;
+ // Variables for InvControl's Volt-Watt function
+ VWMode: Boolean; //boolean indicating if under volt-watt control mode from InvControl (not ExpControl)
+ VVMode: Boolean; //boolean indicating if under volt-var mode from InvControl
+ DRCMode: Boolean; //boolean indicating if under DRC mode from InvControl
+ WPMode: Boolean; //boolean indicating if under watt-pf mode from InvControl
+ WVMode: Boolean; //boolean indicating if under watt-var mode from InvControl
+
+ InverterON: Boolean;
+ varMode: Integer;
+ VarFollowInverter: LongBool;
+
constructor Create(ParClass: TDSSClass; const SourceName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure Set_ConductorClosed(Index: Integer; Value: Boolean); OVERRIDE;
procedure RecalcElementData(); OVERRIDE;
@@ -340,11 +402,6 @@ TStorage2Obj = class(TPCElement)
procedure Set_Variable(i: Integer; Value: Double); OVERRIDE;
function VariableName(i: Integer): String; OVERRIDE;
- function Get_InverterON: Boolean;
- procedure Set_InverterON(const Value: Boolean);
- function Get_VarFollowInverter: Boolean;
- procedure Set_VarFollowInverter(const Value: Boolean);
-
procedure Set_Maxkvar(const Value: Double);
procedure Set_Maxkvarneg(const Value: Double);
@@ -363,41 +420,20 @@ TStorage2Obj = class(TPCElement)
procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
-
-
property kW: Double READ Get_kW WRITE Set_kW;
property kWDesired: Double READ Get_kWDesired;
- property StateDesired: Integer WRITE Set_StateDesired;
- property kWRequested: Double READ Get_kWRequested WRITE Set_kWRequested;
- property kvarRequested: Double READ Get_kvarRequested WRITE Set_kvarRequested;
property PresentkW: Double READ Get_PresentkW; // Present kW at inverter output
property Presentkvar: Double READ Get_Presentkvar; // Present kvar at inverter output
- property PresentkV: Double READ Get_PresentkV WRITE Set_PresentkV;
+ property PresentkV: Double READ Get_PresentkV;
property PowerFactor: Double READ PFNominal WRITE Set_PowerFactor;
property kVARating: Double READ StorageVars.FkVARating WRITE Set_kVARating;
- property pctkWrated: Double READ StorageVars.FpctkWrated WRITE Set_pctkWrated;
- property Varmode: Integer READ Get_Varmode WRITE Set_Varmode; // 0=constant PF; 1=kvar specified
- property VWmode: Boolean READ Get_VWmode WRITE Set_VWmode;
- property VVmode: Boolean READ Get_VVmode WRITE Set_VVmode;
- property WPmode: Boolean READ Get_WPmode WRITE Set_WPmode;
- property WVmode: Boolean READ Get_WVmode WRITE Set_WVmode;
- property DRCmode: Boolean READ Get_DRCmode WRITE Set_DRCmode;
- property InverterON: Boolean READ Get_InverterON WRITE Set_InverterON;
- property CutOutkWAC: Double READ Get_CutOutkWAC;
- property CutInkWAC: Double READ Get_CutInkWAC;
-
- property VarFollowInverter: Boolean READ Get_VarFollowInverter WRITE Set_VarFollowInverter;
+
property kvarLimit: Double READ StorageVars.Fkvarlimit WRITE Set_Maxkvar;
property kvarLimitneg: Double READ StorageVars.Fkvarlimitneg WRITE Set_Maxkvarneg;
property StorageState: Integer READ FState WRITE Set_StorageState;
- property PctkWOut: Double READ FpctkWOut WRITE Set_pctkWOut;
- property PctkWIn: Double READ FpctkWIn WRITE Set_pctkWIn;
property kWTotalLosses: Double READ Get_kWTotalLosses;
property kWIdlingLosses: Double READ Get_kWIdlingLosses;
@@ -406,15 +442,13 @@ TStorage2Obj = class(TPCElement)
property DCkW: Double READ Get_DCkW;
property MinModelVoltagePU: Double READ VminPu;
- property pf_wp_nominal: Double WRITE Set_pf_wp_nominal;
+ function UsingCIMDynamics(): Boolean;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
implementation
-
uses
- ParserDel,
+ BufStream,
Circuit,
Sysutils,
Command,
@@ -426,84 +460,31 @@ implementation
DSSHelper,
DSSObjectHelper;
+type
+ TObj = TStorage2Obj;
+ TProp = TStorage2Prop;
const
-
-{ = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
- To add a property,
- 1) add a property constant to this list
- 2) add a handler to the CASE statement in the Edit FUNCTION
- 3) add a statement(s) to InitPropertyValues FUNCTION to initialize the string value
- 4) add any special handlers to DumpProperties and GetPropertyValue, If needed
- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =}
-
- propKV = 3;
- propKW = 4;
- propPF = 5;
- propMODEL = 6;
- propYEARLY = 7;
- propDAILY = 8;
- propDUTY = 9;
- propDISPMODE = 10;
- propCONNECTION = 11;
- propKVAR = 12;
- propPCTR = 13;
- propPCTX = 14;
- propIDLEKW = 15;
- propCLASS = 16;
- propDISPOUTTRIG = 17;
- propDISPINTRIG = 18;
- propCHARGEEFF = 19;
- propDISCHARGEEFF = 20;
- propPCTKWOUT = 21;
- propVMINPU = 22;
- propVMAXPU = 23;
- propSTATE = 24;
- propKVA = 25;
- propKWRATED = 26;
- propKWHRATED = 27;
- propKWHSTORED = 28;
- propPCTRESERVE = 29;
- propUSERMODEL = 30;
- propUSERDATA = 31;
- propDEBUGTRACE = 32;
- propPCTKWIN = 33;
- propPCTSTORED = 34;
- propCHARGETIME = 35;
- propDynaDLL = 36;
- propDynaData = 37;
- propBalanced = 38;
- propLimited = 39;
-
- propInvEffCurve = 40;
- propCutin = 41;
- propCutout = 42;
- proppctkWrated = 43;
- propVarFollowInverter = 44;
- propkvarLimit = 45;
- propPpriority = 46;
- propPFPriority = 47;
- propPminNoVars = 48;
- propPminkvarLimit = 49;
-
- propkvarLimitneg = 50;
-
- propIDLEKVAR = 51;
-
- NumPropsThisClass = 51; // Make this agree with the last property constant
-
+ NumPropsThisClass = Ord(High(TProp));
var
+ PropInfo: Pointer = NIL;
+ StateEnum, DispatchModeEnum: TDSSEnum;
- cBuffer: array[1..24] of Complex; // Temp buffer for calcs 24-phase Storage element?
- CDOUBLEONE: Complex;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TStorage2.Create(dssContext: TDSSContext); // Creates superstructure for all Storage2 elements
+constructor TStorage2.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'Storage';
- DSSClassType := DSSClassType + Storage_ELEMENT; // In both PCelement and Storage element list
-
- ActiveElement := 0;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ StateEnum := TDSSEnum.Create('Storage: State', True, 1, 1,
+ ['Charging', 'Idling', 'Discharging'],
+ [-1, 0, 1]);
+ StateEnum.DefaultValue := 0;
+ DispatchModeEnum := TDSSEnum.Create('Storage: Dispatch Mode', True, 1, 1,
+ ['Default', 'LoadLevel', 'Price', 'External', 'Follow'],
+ [0, 1, 2, 3, 4]);
+ DispatchModeEnum.DefaultValue := 0;
+ end;
+
+ inherited Create(dssContext, Storage_ELEMENT, 'Storage');
// Set Register names
RegisterNames[1] := 'kWh';
@@ -512,241 +493,176 @@ constructor TStorage2.Create(dssContext: TDSSContext); // Creates superstructur
RegisterNames[4] := 'Max kVA';
RegisterNames[5] := 'Hours';
RegisterNames[6] := 'Price($)';
+end;
- DefineProperties;
+destructor TStorage2.Destroy;
+begin
+ inherited Destroy;
+end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+procedure SetkW(obj: TObj; Value: Double);
+begin
+ obj.kW := Value;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-destructor TStorage2.Destroy;
+function Getkvar(obj: TObj): Double;
+begin
+ Result := obj.kvar_out;
+end;
+procedure SetPctStored(obj: TObj; Value: Double);
begin
- // ElementList and CommandList freed in inherited destroy
- inherited Destroy;
+ obj.StorageVars.kWhStored := Value * 0.01 * obj.StorageVars.kWhRating;
+end;
+function GetPctStored(obj: TObj): Double;
+begin
+ Result := obj.StorageVars.kWhStored / obj.StorageVars.kWhRating * 100.0;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TStorage2.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays; {see DSSClass}
-
- // Define Property names
- {
- Using the AddProperty FUNCTION, you can list the properties here in the order you want
- them to appear when properties are accessed sequentially without tags. Syntax:
-
- AddProperty( , , );
-
- }
- AddProperty('phases', 1,
- 'Number of Phases, this Storage element. Power is evenly divided among phases.');
- AddProperty('bus1', 2,
- 'Bus to which the Storage element is connected. May include specific node specification.');
- AddProperty('kv', propKV,
- 'Nominal rated (1.0 per unit) voltage, kV, for Storage element. For 2- and 3-phase Storage elements, specify phase-phase kV. ' +
- 'Otherwise, specify actual kV across each branch of the Storage element. ' + CRLF + CRLF +
- 'If wye (star), specify phase-neutral kV. ' + CRLF + CRLF +
- 'If delta or phase-phase connected, specify phase-phase kV.'); // line-neutral voltage// base voltage
- AddProperty('conn', propCONNECTION,
- '={wye|LN|delta|LL}. Default is wye.');
- AddProperty('kW', propKW,
- 'Get/set the requested kW value. Final kW is subjected to the inverter ratings. A positive value denotes power coming OUT of the element, ' +
- 'which is the opposite of a Load element. A negative value indicates the Storage element is in Charging state. ' +
- 'This value is modified internally depending on the dispatch mode.');
- AddProperty('kvar', propKVAR,
- 'Get/set the requested kvar value. Final kvar is subjected to the inverter ratings. Sets inverter to operate in constant kvar mode.');
- AddProperty('pf', propPF,
- 'Get/set the requested PF value. Final PF is subjected to the inverter ratings. Sets inverter to operate in constant PF mode. Nominally, ' +
- 'the power factor for discharging (acting as a generator). Default is 1.0. ' + CRLF + CRLF +
- 'Enter negative for leading power factor ' +
- '(when kW and kvar have opposite signs.)' + CRLF + CRLF +
- 'A positive power factor signifies kw and kvar at the same direction.');
- AddProperty('kVA', propKVA,
- 'Indicates the inverter nameplate capability (in kVA). ' +
- 'Used as the base for Dynamics mode and Harmonics mode values.');
- AddProperty('%Cutin', propCutin,
- 'Cut-in power as a percentage of inverter kVA rating. It is the minimum DC power necessary to turn the inverter ON when it is OFF. ' +
- 'Must be greater than or equal to %CutOut. Defaults to 2 for PVSystems and 0 for Storage elements which means that the inverter state ' +
- 'will be always ON for this element.');
- AddProperty('%Cutout', propCutout,
- 'Cut-out power as a percentage of inverter kVA rating. It is the minimum DC power necessary to keep the inverter ON. ' +
- 'Must be less than or equal to %CutIn. Defaults to 0, which means that, once ON, the inverter state ' +
- 'will be always ON for this element.');
-
- AddProperty('EffCurve', propInvEffCurve,
- 'An XYCurve object, previously defined, that describes the PER UNIT efficiency vs PER UNIT of rated kVA for the inverter. ' +
- 'Power at the AC side of the inverter is discounted by the multiplier obtained from this curve.');
-
- AddProperty('VarFollowInverter', propVarFollowInverter,
- 'Boolean variable (Yes|No) or (True|False). Defaults to False, which indicates that the reactive power generation/absorption does not respect the inverter status.' +
- 'When set to True, the reactive power generation/absorption will cease when the inverter status is off, due to DC kW dropping below %CutOut. The reactive power ' +
- 'generation/absorption will begin again when the DC kW is above %CutIn. When set to False, the Storage will generate/absorb reactive power regardless of the status of the inverter.');
- AddProperty('kvarMax', propkvarLimit,
- 'Indicates the maximum reactive power GENERATION (un-signed numerical variable in kvar) for the inverter. Defaults to kVA rating of the inverter.');
-
- AddProperty('kvarMaxAbs', propkvarLimitneg,
- 'Indicates the maximum reactive power ABSORPTION (un-signed numerical variable in kvar) for the inverter. Defaults to kvarMax.');
-
- AddProperty('WattPriority', propPPriority,
- '{Yes/No*/True/False} Set inverter to watt priority instead of the default var priority.');
-
- AddProperty('PFPriority', propPFPriority,
- 'If set to true, priority is given to power factor and WattPriority is neglected. It works only if operating in either constant PF ' +
- 'or constant kvar modes. Defaults to False.');
-
- AddProperty('%PminNoVars', propPminNoVars,
- 'Minimum active power as percentage of kWrated under which there is no vars production/absorption. Defaults to 0 (disabled).');
-
- AddProperty('%PminkvarMax', propPminkvarLimit,
- 'Minimum active power as percentage of kWrated that allows the inverter to produce/absorb reactive power up to its maximum ' +
- 'reactive power, which can be either kvarMax or kvarMaxAbs, depending on the current operation quadrant. Defaults to 0 (disabled).');
-
- AddProperty('kWrated', propKWRATED,
- 'kW rating of power output. Base for Loadshapes when DispMode=Follow. Sets kVA property if it has not been specified yet. ' +
- 'Defaults to 25.');
- AddProperty('%kWrated', proppctkWrated,
- 'Upper limit on active power as a percentage of kWrated. Defaults to 100 (disabled).');
-
- AddProperty('kWhrated', propKWHRATED,
- 'Rated Storage capacity in kWh. Default is 50.');
- AddProperty('kWhstored', propKWHSTORED,
- 'Present amount of energy stored, kWh. Default is same as kWhrated.');
- AddProperty('%stored', propPCTSTORED,
- 'Present amount of energy stored, % of rated kWh. Default is 100.');
- AddProperty('%reserve', propPCTRESERVE,
- 'Percentage of rated kWh Storage capacity to be held in reserve for normal operation. Default = 20. ' + CRLF +
- 'This is treated as the minimum energy discharge level unless there is an emergency. For emergency operation ' +
- 'set this property lower. Cannot be less than zero.');
- AddProperty('State', propSTATE,
- '{IDLING | CHARGING | DISCHARGING} Get/Set present operational state. In DISCHARGING mode, the Storage element ' +
- 'acts as a generator and the kW property is positive. The element continues discharging at the scheduled output power level ' +
- 'until the Storage reaches the reserve value. Then the state reverts to IDLING. ' +
- 'In the CHARGING state, the Storage element behaves like a Load and the kW property is negative. ' +
- 'The element continues to charge until the max Storage kWh is reached and then switches to IDLING state. ' +
- 'In IDLING state, the element draws the idling losses plus the associated inverter losses.');
- AddProperty('%Discharge', propPCTKWOUT,
- 'Discharge rate (output power) in percentage of rated kW. Default = 100.');
- AddProperty('%Charge', propPCTKWIN,
- 'Charging rate (input power) in percentage of rated kW. Default = 100.');
- AddProperty('%EffCharge', propCHARGEEFF,
- 'Percentage efficiency for CHARGING the Storage element. Default = 90.');
- AddProperty('%EffDischarge', propDISCHARGEEFF,
- 'Percentage efficiency for DISCHARGING the Storage element. Default = 90.');
- AddProperty('%IdlingkW', propIDLEKW,
- 'Percentage of rated kW consumed by idling losses. Default = 1.');
- AddProperty('%Idlingkvar', propIDLEKVAR,
- 'Deprecated.');
- AddProperty('%R', propPCTR,
- 'Equivalent percentage internal resistance, ohms. Default is 0. Placed in series with internal voltage source' +
- ' for harmonics and dynamics modes. Use a combination of %IdlingkW, %EffCharge and %EffDischarge to account for ' +
- 'losses in power flow modes.');
- AddProperty('%X', propPCTX,
- 'Equivalent percentage internal reactance, ohms. Default is 50%. Placed in series with internal voltage source' +
- ' for harmonics and dynamics modes. (Limits fault current to 2 pu.');
- AddProperty('model', propMODEL,
- 'Integer code (default=1) for the model to be used for power output variation with voltage. ' +
- 'Valid values are:' + CRLF + CRLF +
- '1:Storage element injects/absorbs a CONSTANT power.' + CRLF +
- '2:Storage element is modeled as a CONSTANT IMPEDANCE.' + CRLF +
- '3:Compute load injection from User-written Model.');
-
- AddProperty('Vminpu', propVMINPU,
- 'Default = 0.90. Minimum per unit voltage for which the Model is assumed to apply. ' +
- 'Below this value, the load model reverts to a constant impedance model.');
- AddProperty('Vmaxpu', propVMAXPU,
- 'Default = 1.10. Maximum per unit voltage for which the Model is assumed to apply. ' +
- 'Above this value, the load model reverts to a constant impedance model.');
- AddProperty('Balanced', propBalanced, '{Yes | No*} Default is No. Force balanced current only for 3-phase Storage. Forces zero- and negative-sequence to zero. ');
- AddProperty('LimitCurrent', propLimited, 'Limits current magnitude to Vminpu value for both 1-phase and 3-phase Storage similar to Generator Model 7. For 3-phase, ' +
- 'limits the positive-sequence current but not the negative-sequence.');
- AddProperty('yearly', propYEARLY,
- 'Dispatch shape to use for yearly simulations. Must be previously defined ' +
- 'as a Loadshape object. If this is not specified, the Daily dispatch shape, if any, is repeated ' +
- 'during Yearly solution modes. In the default dispatch mode, ' +
- 'the Storage element uses this loadshape to trigger State changes.');
- AddProperty('daily', propDAILY,
- 'Dispatch shape to use for daily simulations. Must be previously defined ' +
- 'as a Loadshape object of 24 hrs, typically. In the default dispatch mode, ' +
- 'the Storage element uses this loadshape to trigger State changes.'); // daily dispatch (hourly)
- AddProperty('duty', propDUTY,
- 'Load shape to use for duty cycle dispatch simulations such as for solar ramp rate studies. ' +
- 'Must be previously defined as a Loadshape object. ' + CRLF + CRLF +
- 'Typically would have time intervals of 1-5 seconds. ' + CRLF + CRLF +
- 'Designate the number of points to solve using the Set Number=xxxx command. ' +
- 'If there are fewer points in the actual shape, the shape is assumed to repeat.'); // as for wind generation
- AddProperty('DispMode', propDISPMODE,
- '{DEFAULT | FOLLOW | EXTERNAL | LOADLEVEL | PRICE } Default = "DEFAULT". Dispatch mode. ' + CRLF + CRLF +
- 'In DEFAULT mode, Storage element state is triggered to discharge or charge at the specified rate by the ' +
- 'loadshape curve corresponding to the solution mode. ' + CRLF + CRLF +
- 'In FOLLOW mode the kW output of the Storage element follows the active loadshape multiplier ' +
- 'until Storage is either exhausted or full. ' +
- 'The element discharges for positive values and charges for negative values. The loadshape is based on rated kW. ' + CRLF + CRLF +
- 'In EXTERNAL mode, Storage element state is controlled by an external Storagecontroller. ' +
- 'This mode is automatically set if this Storage element is included in the element list of a StorageController element. ' + CRLF + CRLF +
- 'For the other two dispatch modes, the Storage element state is controlled by either the global default Loadlevel value or the price level. ');
- AddProperty('DischargeTrigger', propDISPOUTTRIG,
- 'Dispatch trigger value for discharging the Storage. ' + CRLF +
- 'If = 0.0 the Storage element state is changed by the State command or by a StorageController object. ' + CRLF +
- 'If <> 0 the Storage element state is set to DISCHARGING when this trigger level is EXCEEDED by either the specified ' +
- 'Loadshape curve value or the price signal or global Loadlevel value, depending on dispatch mode. See State property.');
- AddProperty('ChargeTrigger', propDISPINTRIG,
- 'Dispatch trigger value for charging the Storage. ' + CRLF + CRLF +
- 'If = 0.0 the Storage element state is changed by the State command or StorageController object. ' + CRLF + CRLF +
- 'If <> 0 the Storage element state is set to CHARGING when this trigger level is GREATER than either the specified ' +
- 'Loadshape curve value or the price signal or global Loadlevel value, depending on dispatch mode. See State property.');
- AddProperty('TimeChargeTrig', propCHARGETIME,
- 'Time of day in fractional hours (0230 = 2.5) at which Storage element will automatically go into charge state. ' +
- 'Default is 2.0. Enter a negative time value to disable this feature.');
- AddProperty('class', propCLASS,
- 'An arbitrary integer number representing the class of Storage element so that Storage values may ' +
- 'be segregated by class.'); // integer
- AddProperty('DynaDLL', propDynaDLL,
- 'Name of DLL containing user-written dynamics model, which computes the terminal currents for Dynamics-mode simulations, ' +
- 'overriding the default model. Set to "none" to negate previous setting. ' +
- 'This DLL has a simpler interface than the UserModel DLL and is only used for Dynamics mode.');
- AddProperty('DynaData', propDYNADATA,
- 'String (in quotes or parentheses if necessary) that gets passed to the user-written dynamics model Edit function for defining the data required for that model.');
- AddProperty('UserModel', propUSERMODEL,
- 'Name of DLL containing user-written model, which computes the terminal currents for both power flow and dynamics, ' +
- 'overriding the default model. Set to "none" to negate previous setting.');
- AddProperty('UserData', propUSERDATA,
- 'String (in quotes or parentheses) that gets passed to user-written model for defining the data required for that model.');
- AddProperty('debugtrace', propDEBUGTRACE,
- '{Yes | No } Default is no. Turn this on to capture the progress of the Storage model ' +
- 'for each iteration. Creates a separate file for each Storage element named "Storage_name.CSV".');
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // strings
+ PropertyType[ord(TProp.UserModel)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.UserModel)] := ptruint(@obj.UserModelNameStr);
+ PropertyType[ord(TProp.UserData)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.UserData)] := ptruint(@obj.UserModelEditStr);
+
+ PropertyType[ord(TProp.DynaDLL)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.DynaDLL)] := ptruint(@obj.DynaModelNameStr);
+ PropertyType[ord(TProp.DynaData)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.DynaData)] := ptruint(@obj.DynaModelEditStr);
+
+ // enum properties
+ PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.conn)] := ptruint(@obj.Connection);
+ PropertyOffset2[ord(TProp.conn)] := PtrInt(DSS.ConnectionEnum);
+
+ // enum properties
+ PropertyType[ord(TProp.State)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.State)] := ptruint(@obj.FState);
+ PropertyOffset2[ord(TProp.State)] := PtrInt(StateEnum);
+
+ PropertyType[ord(TProp.DispMode)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.DispMode)] := ptruint(@obj.DispatchMode);
+ PropertyOffset2[ord(TProp.DispMode)] := PtrInt(DispatchModeEnum);
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+
+ // boolean properties
+ PropertyType[ord(TProp.debugtrace)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.Balanced)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.LimitCurrent)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.VarFollowInverter)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.WattPriority)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.PFPriority)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.debugtrace)] := ptruint(@obj.DebugTrace);
+ PropertyOffset[ord(TProp.Balanced)] := ptruint(@obj.ForceBalanced);
+ PropertyOffset[ord(TProp.LimitCurrent)] := ptruint(@obj.CurrentLimited);
+ PropertyOffset[ord(TProp.VarFollowInverter)] := ptruint(@obj.VarFollowInverter);
+ PropertyOffset[ord(TProp.WattPriority)] := ptruint(@obj.StorageVars.P_priority);
+ PropertyOffset[ord(TProp.PFPriority)] := ptruint(@obj.StorageVars.PF_priority);
+
+ // integer properties
+ PropertyOffset[ord(TProp.cls)] := ptruint(@obj.StorageClass);
+ PropertyOffset[ord(TProp.model)] := ptruint(@obj.VoltageModel);
+ PropertyType[ord(TProp.cls)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.model)] := TPropertyType.IntegerProperty;
+
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // object properties
+ PropertyType[ord(TProp.yearly)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.daily)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.duty)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.yearly)] := ptruint(@obj.YearlyShapeObj);
+ PropertyOffset[ord(TProp.daily)] := ptruint(@obj.DailyShapeObj);
+ PropertyOffset[ord(TProp.duty)] := ptruint(@obj.DutyShapeObj);
+ PropertyOffset2[ord(TProp.yearly)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.daily)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.duty)] := ptruint(DSS.LoadShapeClass);
+
+ PropertyType[ord(TProp.EffCurve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.EffCurve)] := ptruint(@obj.InverterCurveObj);
+ PropertyOffset2[ord(TProp.EffCurve)] := ptruint(DSS.XYCurveClass);
+
+ PropertyScale[ord(TProp.pctkWrated)] := 0.01;
+ PropertyOffset[ord(TProp.pctkWrated)] := ptruint(@obj.StorageVars.pctkWrated);
+
+ // adv doubles
+ PropertyOffset[ord(TProp.kvarMax)] := ptruint(@obj.StorageVars.Fkvarlimit);
+ PropertyOffset[ord(TProp.kvarMaxAbs)] := ptruint(@obj.StorageVars.Fkvarlimitneg);
+ PropertyFlags[ord(TProp.kvarMax)] := [TPropertyFlag.Transform_Abs];
+ PropertyFlags[ord(TProp.kvarMaxAbs)] := [TPropertyFlag.Transform_Abs];
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.pctR)] := ptruint(@obj.pctR);
+ PropertyOffset[ord(TProp.pctX)] := ptruint(@obj.pctX);
+ PropertyOffset[ord(TProp.pctIdlingkW)] := ptruint(@obj.pctIdlekW);
+ PropertyOffset[ord(TProp.DischargeTrigger)] := ptruint(@obj.DischargeTrigger);
+ PropertyOffset[ord(TProp.ChargeTrigger)] := ptruint(@obj.ChargeTrigger);
+ PropertyOffset[ord(TProp.pctEffCharge)] := ptruint(@obj.pctChargeEff);
+ PropertyOffset[ord(TProp.pctEffDischarge)] := ptruint(@obj.pctDischargeEff);
+ PropertyOffset[ord(TProp.pctDischarge)] := ptruint(@obj.pctkWout);
+ PropertyOffset[ord(TProp.pctCharge)] := ptruint(@obj.pctkWIn);
+ PropertyOffset[ord(TProp.pctCutin)] := ptruint(@obj.FpctCutIn);
+ PropertyOffset[ord(TProp.pctCutout)] := ptruint(@obj.FpctCutOut);
+ PropertyOffset[ord(TProp.Vminpu)] := ptruint(@obj.VMinPu);
+ PropertyOffset[ord(TProp.Vmaxpu)] := ptruint(@obj.VMaxPu);
+ PropertyOffset[ord(TProp.kWrated)] := ptruint(@obj.StorageVars.kWrating);
+ PropertyOffset[ord(TProp.kWhrated)] := ptruint(@obj.StorageVars.kWhrating);
+ PropertyOffset[ord(TProp.kWhstored)] := ptruint(@obj.StorageVars.kWhstored);
+ PropertyOffset[ord(TProp.pctreserve)] := ptruint(@obj.pctReserve);
+ PropertyOffset[ord(TProp.pctPminNoVars)] := ptruint(@obj.FpctPminNoVars);
+ PropertyOffset[ord(TProp.pctPminkvarMax)] := ptruint(@obj.FpctPminkvarLimit);
+ PropertyOffset[ord(TProp.TimeChargeTrig)] := ptruint(@obj.ChargeTime);
+ PropertyOffset[ord(TProp.pf)] := ptruint(@obj.PFnominal);
+ PropertyOffset[ord(TProp.kVA)] := ptruint(@obj.StorageVars.FkVArating);
+ PropertyOffset[ord(TProp.kV)] := ptruint(@obj.StorageVars.kVStorageBase);
+
+ PropertyOffset[ord(TProp.kvar)] := ptruint(@obj.kvarRequested);
+ PropertyReadFunction[ord(TProp.kvar)] := @Getkvar;
+ PropertyFlags[ord(TProp.kvar)] := [TPropertyFlag.ReadByFunction];
+
+ PropertyType[ord(TProp.pctstored)] := TPropertyType.DoubleProperty;
+ PropertyOffset[ord(TProp.pctstored)] := 1;
+ PropertyWriteFunction[ord(TProp.pctstored)] := @SetPctStored;
+ PropertyReadFunction[ord(TProp.pctstored)] := @GetPctStored;
+ PropertyFlags[ord(TProp.pctstored)] := [TPropertyFlag.WriteByFunction, TPropertyFlag.ReadByFunction];
+
+ PropertyType[ord(TProp.kW)] := TPropertyType.DoubleProperty;
+ PropertyOffset[ord(TProp.kW)] := ptruint(@obj.kW_out);
+ PropertyWriteFunction[ord(TProp.kW)] := @SetkW;
+ PropertyFlags[ord(TProp.kW)] := [TPropertyFlag.WriteByFunction];
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- // Override default help string
- PropertyHelp[NumPropsThisClass + 1] := 'Name of harmonic voltage or current spectrum for this Storage element. ' +
- 'Current injection is assumed for inverter. ' +
- 'Default value is "default", which is defined when the DSS starts.';
-
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TStorage2.NewObject(const ObjName: String): Integer;
+function TStorage2.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new Storage element and add it to Storage class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TStorage2Obj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TStorage2.SetNcondsForConnection;
-
+procedure SetNcondsForConnection(Obj: TObj);
begin
- with DSS.ActiveStorage2Obj do
+ with Obj do
begin
case Connection of
0:
@@ -762,7 +678,6 @@ procedure TStorage2.SetNcondsForConnection;
end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TStorage2.UpdateAll();
var
i: Integer;
@@ -773,461 +688,230 @@ procedure TStorage2.UpdateAll();
UpdateStorage();
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TStorage2.InterpretConnection(const S: String);
-
-// Accepts
-// delta or LL (Case insensitive)
-// Y, wye, or LN
+procedure TStorage2Obj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- TestS: String;
-
+ i: Integer;
begin
- with DSS.ActiveStorage2Obj do
- begin
- TestS := lowercase(S);
- case TestS[1] of
- 'y', 'w':
- Connection := 0; {Wye}
- 'd':
- Connection := 1; {Delta or line-Line}
- 'l':
- case Tests[2] of
- 'n':
- Connection := 0;
- 'l':
- Connection := 1;
- end;
- end;
-
- SetNCondsForConnection;
+ case Idx of
+ ord(TProp.conn):
+ begin
+ SetNCondsForConnection(self);
- {VBase is always L-N voltage unless 1-phase device or more than 3 phases}
+ // VBase is always L-N voltage unless 1-phase device or more than 3 phases
- case Fnphases of
- 2, 3:
- VBase := StorageVars.kVStorageBase * InvSQRT3x1000; // L-N Volts
- else
- VBase := StorageVars.kVStorageBase * 1000.0; // Just use what is supplied
- end;
+ case Fnphases of
+ 2, 3:
+ VBase := StorageVars.kVStorageBase * InvSQRT3x1000; // L-N Volts
+ else
+ VBase := StorageVars.kVStorageBase * 1000.0; // Just use what is supplied
+ end;
- VBase95 := Vminpu * VBase;
- VBase105 := Vmaxpu * VBase;
+ VBase95 := Vminpu * VBase;
+ VBase105 := Vmaxpu * VBase;
- Yorder := Fnconds * Fnterms;
- YprimInvalid := TRUE;
- end;
-end;
+ Yorder := Fnconds * Fnterms;
+ YprimInvalid := TRUE;
+ end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function InterpretDispMode(const S: String): Integer;
-begin
- case lowercase(S)[1] of
- 'e':
- Result := STORE_EXTERNALMODE;
- 'f':
- Result := STORE_FOLLOW;
- 'l':
- Result := STORE_LOADMODE;
- 'p':
- Result := STORE_PRICEMODE;
- else
- Result := STORE_DEFAULT;
- end;
-end;
+ ord(TProp.kv):
+ case FNphases of
+ 2, 3:
+ VBase := StorageVars.kVStorageBase * InvSQRT3x1000;
+ else
+ VBase := StorageVars.kVStorageBase * 1000.0;
+ end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function ReturnDispMode(const imode: Integer): String;
-begin
- case imode of
- STORE_EXTERNALMODE:
- Result := 'External';
- STORE_FOLLOW:
- Result := 'Follow';
- STORE_LOADMODE:
- Result := 'Loadshape';
- STORE_PRICEMODE:
- Result := 'Price';
- else
- Result := 'default';
- end;
-end;
+ ord(TProp.kVA):
+ with StorageVars do
+ begin
+ kVASet := TRUE;
+ if not kvarLimitSet then
+ StorageVars.Fkvarlimit := FkVArating;
+ if not kvarLimitSet and not kvarLimitNegSet then
+ StorageVars.Fkvarlimitneg := FkVArating;
+ end;
+ ord(TProp.pf):
+ varMode := VARMODEPF;
-//- - - - - - - - - - - - - - -MAIN EDIT FUNCTION - - - - - - - - - - - - - - -
-function TStorage2.Edit(): Integer;
+ ord(TProp.kvar):
+ varMode := VARMODEKVAR;
-var
- i, iCase,
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
+ ord(TProp.kvarMax):
+ begin
+ kvarLimitSet := TRUE;
+ if not kvarLimitNegSet then
+ StorageVars.Fkvarlimitneg := Abs(StorageVars.Fkvarlimit);
+ end;
+ ord(TProp.kvarMaxAbs):
+ kvarLimitNegSet := TRUE;
-begin
+ ord(TProp.phases):
+ SetNCondsForConnection(self); // Force Reallocation of terminal info
- // continue parsing with contents of Parser
- DSS.ActiveStorage2Obj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveStorage2Obj;
+ ord(TProp.kWrated):
+ if not kVASet then
+ StorageVars.FkVArating := StorageVars.kWrating;
- Result := 0;
+ ord(TProp.kWhrated):
+ begin
+ StorageVars.kWhStored := StorageVars.kWhRating; // Assume fully charged
+ kWhBeforeUpdate := StorageVars.kWhStored;
+ StorageVars.kWhReserve := StorageVars.kWhRating * pctReserve * 0.01;
+ end;
- with DSS.ActiveStorage2Obj do
- begin
+ ord(TProp.pctreserve):
+ StorageVars.kWhReserve := StorageVars.kWhRating * pctReserve * 0.01;
- ParamPointer := 0;
- ParamName := Parser.NextParam; // Parse next property off the command line
- Param := Parser.StrValue; // Put the string value of the property value in local memory for faster access
- while Length(Param) > 0 do
+ ord(TProp.UserModel):
begin
+ UserModel.Name := UserModelNameStr;
+ IsUserModel := UserModel.Exists;
+ end;
+ ord(TProp.UserData):
+ if UserModel.Exists then
+ UserModel.Edit := UserModelEditStr;
+ ord(TProp.DynaDLL):
+ begin
+ DynaModel.Name := DynaModelNameStr;
+ IsUserModel := DynaModel.Exists;
+ end;
+ ord(TProp.DynaData):
+ if DynaModel.Exists then
+ DynaModel.Edit := DynaModelEditStr;
- if (Length(ParamName) = 0) then
- Inc(ParamPointer) // If it is not a named property, assume the next property
- else
- ParamPointer := CommandList.GetCommand(ParamName); // Look up the name in the list for this class
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[PropertyIdxMap[ParamPointer]] := Param // Update the string value of the property
+ ord(TProp.debugtrace):
+ if DebugTrace then
+ begin // Init trace file
+ FreeAndNil(TraceFile);
+ TraceFile := TBufferedFileStream.Create(DSS.OutputDirectory + 'STOR_' + Name + '.csv', fmCreate);
+ FSWrite(TraceFile, 't, Iteration, LoadMultiplier, Mode, LoadModel, StorageModel, Qnominalperphase, Pnominalperphase, CurrentType');
+ for i := 1 to nphases do
+ FSWrite(Tracefile, ', |Iinj' + IntToStr(i) + '|');
+ for i := 1 to nphases do
+ FSWrite(Tracefile, ', |Iterm' + IntToStr(i) + '|');
+ for i := 1 to nphases do
+ FSWrite(Tracefile, ', |Vterm' + IntToStr(i) + '|');
+ for i := 1 to NumVariables do
+ FSWrite(Tracefile, ', ' + VariableName(i));
+
+ FSWrite(TraceFile, ',Vthev, Theta');
+ FSWriteln(TraceFile);
+ FSFlush(Tracefile);
+ end
else
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Storage "' + Name + '"', 560);
-
- if ParamPointer > 0 then
begin
- iCase := PropertyIdxMap[ParamPointer];
- case iCASE of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 561);
- 1:
- NPhases := Parser.Intvalue;
- 2:
- SetBus(1, param);
- propKV:
- PresentkV := Parser.DblValue;
- propKW:
- kW := Parser.DblValue;
- propPF:
- begin
- varMode := VARMODEPF;
- PFnominal := Parser.DblValue;
- end;
- propMODEL:
- VoltageModel := Parser.IntValue;
- propYEARLY:
- YearlyShape := Param;
- propDAILY:
- DailyShape := Param;
- propDUTY:
- DutyShape := Param;
- propDISPMODE:
- DispatchMode := InterpretDispMode(Param);
- propCONNECTION:
- InterpretConnection(Param);
- propKVAR:
- begin
- varMode := VARMODEKVAR;
- kvarRequested := Parser.DblValue;
- end;
- propPCTR:
- pctR := Parser.DblValue;
- propPCTX:
- pctX := Parser.DblValue;
- propIDLEKW:
- pctIdlekW := Parser.DblValue;
- propIDLEKVAR: ; // Do nothing. Deprecated property.
- propCLASS:
- StorageClass := Parser.IntValue;
- propInvEffCurve:
- InverterCurve := Param;
- propDISPOUTTRIG:
- DischargeTrigger := Parser.DblValue;
- propDISPINTRIG:
- ChargeTrigger := Parser.DblValue;
- propCHARGEEFF:
- pctChargeEff := Parser.DblValue;
- propDISCHARGEEFF:
- pctDischargeEff := Parser.DblValue;
- propPCTKWOUT:
- pctkWout := Parser.DblValue;
- propCutin:
- FpctCutIn := Parser.DblValue;
- propCutout:
- FpctCutOut := Parser.DblValue;
- propVMINPU:
- VMinPu := Parser.DblValue;
- propVMAXPU:
- VMaxPu := Parser.DblValue;
- propSTATE:
- FState := InterpretState(Param); //****
- propKVA:
- with StorageVars do
- begin
- FkVArating := Parser.DblValue;
- kVASet := TRUE;
- if not kvarLimitSet then
- StorageVars.Fkvarlimit := FkVArating;
- if not kvarLimitSet and not kvarLimitNegSet then
- StorageVars.Fkvarlimitneg := FkVArating;
- end;
- propKWRATED:
- StorageVars.kWrating := Parser.DblValue;
- propKWHRATED:
- StorageVars.kWhrating := Parser.DblValue;
- propKWHSTORED:
- StorageVars.kWhstored := Parser.DblValue;
- propPCTRESERVE:
- pctReserve := Parser.DblValue;
- propUSERMODEL:
- UserModel.Name := Parser.StrValue; // Connect to user written models
- propUSERDATA:
- UserModel.Edit := Parser.StrValue; // Send edit string to user model
- propDEBUGTRACE:
- DebugTrace := InterpretYesNo(Param);
- propPCTKWIN:
- pctkWIn := Parser.DblValue;
- propPCTSTORED:
- StorageVars.kWhStored := Parser.DblValue * 0.01 * StorageVars.kWhRating;
- propCHARGETIME:
- ChargeTime := Parser.DblValue;
- propDynaDLL:
- DynaModel.Name := Parser.StrValue;
- propDynaData:
- DynaModel.Edit := Parser.StrValue;
- proppctkWrated:
- StorageVars.FpctkWrated := Parser.DblValue / 100.0; // convert to pu
- propBalanced:
- ForceBalanced := InterpretYesNo(Param);
- propLimited:
- CurrentLimited := InterpretYesNo(Param);
- propVarFollowInverter:
- FVarFollowInverter := InterpretYesNo(Param);
- propkvarLimit:
- begin
- StorageVars.Fkvarlimit := Abs(Parser.DblValue);
- kvarLimitSet := TRUE;
- if not kvarLimitNegSet then
- StorageVars.Fkvarlimitneg := Abs(StorageVars.Fkvarlimit);
-
- end;
- propPPriority:
- StorageVars.P_priority := InterpretYesNo(Param); // watt priority flag
- propPFPriority:
- StorageVars.PF_priority := InterpretYesNo(Param);
-
- propPminNoVars:
- FpctPminNoVars := Parser.DblValue;
- propPminkvarLimit:
- FpctPminkvarLimit := Parser.DblValue;
-
- propkvarLimitneg:
- begin
- StorageVars.Fkvarlimitneg := Abs(Parser.DblValue);
- kvarLimitNegSet := TRUE;
- end;
-
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveStorage2Obj, ParamPointer - NumPropsThisClass)
- end;
-
- case iCase of
- 1:
- SetNcondsForConnection; // Force Reallocation of terminal info
- // (PR) Make sure if we will need it
- { removed
- propKW,propPF: Begin
- SyncUpPowerQuantities; // keep kvar nominal up to date with kW and PF
-
- End; }
-
- {Set loadshape objects; returns nil If not valid}
- propYEARLY:
- YearlyShapeObj := DSS.LoadShapeClass.Find(YearlyShape);
- propDAILY:
- DailyShapeObj := DSS.LoadShapeClass.Find(DailyShape);
- propDUTY:
- DutyShapeObj := DSS.LoadShapeClass.Find(DutyShape);
-
- propKWRATED:
- if not kVASet then
- StorageVars.FkVArating := StorageVars.kWrating;
- propKWHRATED:
- begin
- StorageVars.kWhStored := StorageVars.kWhRating; // Assume fully charged
- kWhBeforeUpdate := StorageVars.kWhStored;
- StorageVars.kWhReserve := StorageVars.kWhRating * pctReserve * 0.01;
- end;
-
- propPCTRESERVE:
- StorageVars.kWhReserve := StorageVars.kWhRating * pctReserve * 0.01;
-
- propInvEffCurve:
- InverterCurveObj := DSS.XYCurveClass.Find(InverterCurve);
-
- propDEBUGTRACE:
- if DebugTrace then
- begin // Init trace file
- FreeAndNil(TraceFile);
- TraceFile := TFileStream.Create(DSS.OutputDirectory + 'STOR_' + Name + '.CSV', fmCreate);
- FSWrite(TraceFile, 't, Iteration, LoadMultiplier, Mode, LoadModel, StorageModel, Qnominalperphase, Pnominalperphase, CurrentType');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Iinj' + IntToStr(i) + '|');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Iterm' + IntToStr(i) + '|');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Vterm' + IntToStr(i) + '|');
- for i := 1 to NumVariables do
- FSWrite(Tracefile, ', ' + VariableName(i));
-
- FSWrite(TraceFile, ',Vthev, Theta');
- FSWriteln(TraceFile);
- FSFlush(Tracefile);
- end
- else
- begin
- FreeAndNil(TraceFile);
- end;
-
- propUSERMODEL:
- IsUserModel := UserModel.Exists;
- propDynaDLL:
- IsUserModel := DynaModel.Exists;
-
-// propPFPriority: For i := 1 to ControlElementList.Count Do
-// Begin
-//
-// if TControlElem(ControlElementList.Get(i)).ClassName = 'InvControl' Then
-// // Except for VW mode, all other modes (including combined ones) can operate with PF priority
-// if (TInvControlObj(ControlElementList.Get(i)).Mode <> 'VOLTWATT') Then
-// StorageVars.PF_Priority := FALSE; // For all other modes
-//
-// End;
-
- end;
+ FreeAndNil(TraceFile);
end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
- RecalcElementData();
- YprimInvalid := TRUE;
end;
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-//----------------------------------------------------------------------------
-function TStorage2.MakeLike(const OtherStorage2ObjName: String): Integer;
-
-// Copy over essential properties from other object
+function TStorage2.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
+ begin
+ RecalcElementData;
+ YPrimInvalid := TRUE;
+ Exclude(Flags, Flg.EditionActive);
+ end;
+ Result := True;
+end;
+procedure TStorage2Obj.MakeLike(OtherPtr: Pointer);
var
- OtherStorageObj: TStorage2Obj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- {See If we can find this line name in the present collection}
- OtherStorageObj := Find(OtherStorage2ObjName);
- if (OtherStorageObj <> NIL) then
- with DSS.ActiveStorage2Obj do
- begin
- if (Fnphases <> OtherStorageObj.Fnphases) then
- begin
- Nphases := OtherStorageObj.Fnphases;
- NConds := Fnphases; // Forces reallocation of terminal stuff
- Yorder := Fnconds * Fnterms;
- YprimInvalid := TRUE;
- end;
+ inherited MakeLike(OtherPtr);
- StorageVars.kVStorageBase := OtherStorageObj.StorageVars.kVStorageBase;
- Vbase := OtherStorageObj.Vbase;
- Vminpu := OtherStorageObj.Vminpu;
- Vmaxpu := OtherStorageObj.Vmaxpu;
- Vbase95 := OtherStorageObj.Vbase95;
- Vbase105 := OtherStorageObj.Vbase105;
- kW_out := OtherStorageObj.kW_out;
- kvar_out := OtherStorageObj.kvar_out;
- Pnominalperphase := OtherStorageObj.Pnominalperphase;
- PFNominal := OtherStorageObj.PFNominal;
- Qnominalperphase := OtherStorageObj.Qnominalperphase;
- Connection := OtherStorageObj.Connection;
- YearlyShape := OtherStorageObj.YearlyShape;
- YearlyShapeObj := OtherStorageObj.YearlyShapeObj;
- DailyShape := OtherStorageObj.DailyShape;
- DailyShapeObj := OtherStorageObj.DailyShapeObj;
- DutyShape := OtherStorageObj.DutyShape;
- DutyShapeObj := OtherStorageObj.DutyShapeObj;
- DispatchMode := OtherStorageObj.DispatchMode;
- InverterCurve := OtherStorageObj.InverterCurve;
- InverterCurveObj := OtherStorageObj.InverterCurveObj;
- StorageClass := OtherStorageObj.StorageClass;
- VoltageModel := OtherStorageObj.VoltageModel;
-
- Fstate := OtherStorageObj.Fstate;
- FstateChanged := OtherStorageObj.FstateChanged;
- kvarLimitSet := OtherStorageObj.kvarLimitSet;
- kvarLimitNegSet := OtherStorageObj.kvarLimitNegSet;
-
- FpctCutin := OtherStorageObj.FpctCutin;
- FpctCutout := OtherStorageObj.FpctCutout;
- FVarFollowInverter := OtherStorageObj.FVarFollowInverter;
- StorageVars.Fkvarlimit := OtherStorageObj.StorageVars.Fkvarlimit;
- StorageVars.Fkvarlimitneg := OtherStorageObj.StorageVars.Fkvarlimitneg;
- StorageVars.FkVArating := OtherStorageObj.StorageVars.FkVArating;
-
- FpctPminNoVars := OtherStorageObj.FpctPminNoVars;
- FpctPminkvarLimit := OtherStorageObj.FpctPminkvarLimit;
-
- kWOutIdling := OtherStorageObj.kWOutIdling;
-
- StorageVars.kWRating := OtherStorageObj.StorageVars.kWRating;
- StorageVars.kWhRating := OtherStorageObj.StorageVars.kWhRating;
- StorageVars.kWhStored := OtherStorageObj.StorageVars.kWhStored;
- StorageVars.kWhReserve := OtherStorageObj.StorageVars.kWhReserve;
- kWhBeforeUpdate := OtherStorageObj.kWhBeforeUpdate;
- pctReserve := OtherStorageObj.pctReserve;
- DischargeTrigger := OtherStorageObj.DischargeTrigger;
- ChargeTrigger := OtherStorageObj.ChargeTrigger;
- pctChargeEff := OtherStorageObj.pctChargeEff;
- pctDischargeEff := OtherStorageObj.pctDischargeEff;
- pctkWout := OtherStorageObj.pctkWout;
- pctkWin := OtherStorageObj.pctkWin;
- pctIdlekW := OtherStorageObj.pctIdlekW;
- pctIdlekvar := OtherStorageObj.pctIdlekvar;
- ChargeTime := OtherStorageObj.ChargeTime;
-
- pctR := OtherStorageObj.pctR;
- pctX := OtherStorageObj.pctX;
-
- RandomMult := OtherStorageObj.RandomMult;
- FVWMode := OtherStorageObj.FVWMode;
- FVVMode := OtherStorageObj.FVVMode;
- FDRCMode := OtherStorageObj.FDRCMode;
- FWPMode := OtherStorageObj.FWPMode;
- FWVMode := OtherStorageObj.FWVMode;
-
- UserModel.Name := OtherStorageObj.UserModel.Name; // Connect to user written models
- DynaModel.Name := OtherStorageObj.DynaModel.Name;
- IsUserModel := OtherStorageObj.IsUserModel;
- ForceBalanced := OtherStorageObj.ForceBalanced;
- CurrentLimited := OtherStorageObj.CurrentLimited;
-
- ClassMakeLike(OtherStorageObj);
-
- for i := 1 to ParentClass.NumProperties do
- FPropertyValue^[i] := OtherStorageObj.FPropertyValue^[i];
-
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Storage MakeLike: "' + OtherStorage2ObjName + '" Not Found.', 562);
+ Other := TObj(OtherPtr);
+ if (Fnphases <> Other.Fnphases) then
+ begin
+ FNphases := Other.Fnphases;
+ NConds := Fnphases; // Forces reallocation of terminal stuff
+ Yorder := Fnconds * Fnterms;
+ YprimInvalid := TRUE;
+ end;
+ StorageVars.kVStorageBase := Other.StorageVars.kVStorageBase;
+ Vbase := Other.Vbase;
+ Vminpu := Other.Vminpu;
+ Vmaxpu := Other.Vmaxpu;
+ Vbase95 := Other.Vbase95;
+ Vbase105 := Other.Vbase105;
+ kW_out := Other.kW_out;
+ kvar_out := Other.kvar_out;
+ Pnominalperphase := Other.Pnominalperphase;
+ PFNominal := Other.PFNominal;
+ Qnominalperphase := Other.Qnominalperphase;
+ Connection := Other.Connection;
+ YearlyShapeObj := Other.YearlyShapeObj;
+ DailyShapeObj := Other.DailyShapeObj;
+ DutyShapeObj := Other.DutyShapeObj;
+ DispatchMode := Other.DispatchMode;
+ InverterCurveObj := Other.InverterCurveObj;
+ StorageClass := Other.StorageClass;
+ VoltageModel := Other.VoltageModel;
+
+ Fstate := Other.Fstate;
+ FstateChanged := Other.FstateChanged;
+ kvarLimitSet := Other.kvarLimitSet;
+ kvarLimitNegSet := Other.kvarLimitNegSet;
+
+ FpctCutin := Other.FpctCutin;
+ FpctCutout := Other.FpctCutout;
+ VarFollowInverter := Other.VarFollowInverter;
+ StorageVars.Fkvarlimit := Other.StorageVars.Fkvarlimit;
+ StorageVars.Fkvarlimitneg := Other.StorageVars.Fkvarlimitneg;
+ StorageVars.FkVArating := Other.StorageVars.FkVArating;
+
+ FpctPminNoVars := Other.FpctPminNoVars;
+ FpctPminkvarLimit := Other.FpctPminkvarLimit;
+
+ kWOutIdling := Other.kWOutIdling;
+
+ StorageVars.kWRating := Other.StorageVars.kWRating;
+ StorageVars.kWhRating := Other.StorageVars.kWhRating;
+ StorageVars.kWhStored := Other.StorageVars.kWhStored;
+ StorageVars.kWhReserve := Other.StorageVars.kWhReserve;
+ kWhBeforeUpdate := Other.kWhBeforeUpdate;
+ pctReserve := Other.pctReserve;
+ DischargeTrigger := Other.DischargeTrigger;
+ ChargeTrigger := Other.ChargeTrigger;
+ pctChargeEff := Other.pctChargeEff;
+ pctDischargeEff := Other.pctDischargeEff;
+ pctkWout := Other.pctkWout;
+ pctkWin := Other.pctkWin;
+ pctIdlekW := Other.pctIdlekW;
+ pctIdlekvar := Other.pctIdlekvar;
+ ChargeTime := Other.ChargeTime;
+
+ pctR := Other.pctR;
+ pctX := Other.pctX;
+
+ RandomMult := Other.RandomMult;
+ VWMode := Other.VWMode;
+ VVMode := Other.VVMode;
+ DRCMode := Other.DRCMode;
+ WPMode := Other.WPMode;
+ WVMode := Other.WVMode;
+ AVRMode := Other.AVRMode;
+
+ UserModel.Name := Other.UserModel.Name;
+ DynaModel.Name := Other.DynaModel.Name;
+ UserModelNameStr := Other.UserModelNameStr;
+ DynaModelNameStr := Other.DynaModelNameStr;
+
+ //TODO: this doesn't copy the parameters of the user models
+
+ IsUserModel := Other.IsUserModel;
+ ForceBalanced := Other.ForceBalanced;
+ CurrentLimited := Other.CurrentLimited;
end;
-{--------------------------------------------------------------------------}
procedure TStorage2.ResetRegistersAll; // Force all EnergyMeters in the circuit to reset
-
var
idx: Integer;
-
begin
idx := First;
while idx > 0 do
@@ -1237,9 +921,7 @@ procedure TStorage2.ResetRegistersAll; // Force all EnergyMeters in the circuit
end;
end;
-{--------------------------------------------------------------------------}
procedure TStorage2.SampleAll(); // Force all Storage elements in the circuit to take a sample
-
var
i: Integer;
begin
@@ -1249,32 +931,26 @@ procedure TStorage2.SampleAll(); // Force all Storage elements in the circuit t
TakeSample();
end;
-//----------------------------------------------------------------------------
constructor TStorage2Obj.Create(ParClass: TDSSClass; const SourceName: String);
begin
-
inherited create(ParClass);
- Name := LowerCase(SourceName);
+ Name := AnsiLowerCase(SourceName);
DSSObjType := ParClass.DSSClassType; // + Storage_ELEMENT; // In both PCelement and Storageelement list
TraceFile := nil;
- Nphases := 3;
+ FNphases := 3;
Fnconds := 4; // defaults to wye
Yorder := 0; // To trigger an initial allocation
Nterms := 1; // forces allocations
- YearlyShape := '';
- YearlyShapeObj := NIL; // If YearlyShapeobj = nil Then the load alway stays nominal * global multipliers
- DailyShape := '';
- DailyShapeObj := NIL; // If DaillyShapeobj = nil Then the load alway stays nominal * global multipliers
- DutyShape := '';
- DutyShapeObj := NIL; // If DutyShapeobj = nil Then the load alway stays nominal * global multipliers
+ YearlyShapeObj := NIL;
+ DailyShapeObj := NIL;
+ DutyShapeObj := NIL;
InverterCurveObj := NIL;
- InverterCurve := '';
Connection := 0; // Wye (star)
- VoltageModel := 1; {Typical fixed kW negative load}
+ VoltageModel := 1; // Typical fixed kW negative load
StorageClass := 1;
StorageSolutionCount := -1; // For keep track of the present solution in Injcurrent calcs
@@ -1291,9 +967,9 @@ constructor TStorage2Obj.Create(ParClass: TDSSClass; const SourceName: String);
RandomMult := 1.0;
varMode := VARMODEPF;
- FInverterON := TRUE; // start with inverterON
+ InverterON := TRUE; // start with inverterON
kVA_exceeded := FALSE;
- FVarFollowInverter := FALSE;
+ VarFollowInverter := FALSE;
ForceBalanced := FALSE;
CurrentLimited := FALSE;
@@ -1308,7 +984,7 @@ constructor TStorage2Obj.Create(ParClass: TDSSClass; const SourceName: String);
kWhReserve := kWhRating * pctReserve / 100.0;
Fkvarlimit := FkVArating;
Fkvarlimitneg := FkVArating;
- FpctkWrated := 1.0;
+ pctkWrated := 1.0;
P_Priority := FALSE;
PF_Priority := FALSE;
@@ -1333,24 +1009,27 @@ constructor TStorage2Obj.Create(ParClass: TDSSClass; const SourceName: String);
FpctPminNoVars := -1.0; // Deactivated by default
FpctPminkvarLimit := -1.0; // Deactivated by default
- Fpf_wp_nominal := 1.0;
+ pf_wp_nominal := 1.0;
- {Output rating stuff}
+ // Output rating stuff
kvar_out := 0.0;
// removed kvarBase := kvar_out; // initialize
PFNominal := 1.0;
pctR := 0.0;
- ;
pctX := 50.0;
- {Make the StorageVars struct as public}
+ // Make the StorageVars struct as public
PublicDataStruct := @StorageVars;
PublicDataSize := SizeOf(TStorage2Vars);
IsUserModel := FALSE;
UserModel := TStoreUserModel.Create(DSS);
DynaModel := TStoreDynaModel.Create(DSS);
+ UserModelNameStr := '';
+ UserModelEditStr := '';
+ DynaModelNameStr := '';
+ DynaModelEditStr := '';
FState := STORE_IDLING; // Idling and fully charged
FStateChanged := TRUE; // Force building of YPrim
@@ -1362,8 +1041,8 @@ constructor TStorage2Obj.Create(ParClass: TDSSClass; const SourceName: String);
ChargeTrigger := 0.0;
pctChargeEff := 90.0;
pctDischargeEff := 90.0;
- FpctkWout := 100.0;
- FpctkWin := 100.0;
+ pctkWOut := 100.0;
+ pctkWIn := 100.0;
ChargeTime := 2.0; // 2 AM
@@ -1380,223 +1059,19 @@ constructor TStorage2Obj.Create(ParClass: TDSSClass; const SourceName: String);
DebugTrace := FALSE;
Storage2ObjSwitchOpen := FALSE;
- Spectrum := ''; // override base class
- SpectrumObj := NIL;
- FVWMode := FALSE;
- FVVMode := FALSE;
- FDRCMode := FALSE;
- FWPMode := FALSE;
- FWVMode := FALSE;
-
- InitPropertyValues(0);
- RecalcElementData();
-
-end;
-
-
-//----------------------------------------------------------------------------
-function TStorage2Obj.DecodeState: String;
-begin
- case Fstate of
- STORE_CHARGING:
- Result := 'CHARGING';
- STORE_DISCHARGING:
- Result := 'DISCHARGING';
- else
- Result := 'IDLING';
- end;
-end;
-
-//----------------------------------------------------------------------------
-procedure TStorage2Obj.InitPropertyValues(ArrayOffset: Integer);
-
-// Define default values for the properties
+ SpectrumObj := NIL; // override base class
+ VWMode := FALSE;
+ VVMode := FALSE;
+ DRCMode := FALSE;
+ WPMode := FALSE;
+ WVMode := FALSE;
+ AVRMode := FALSE;
-begin
-
- PropertyValue[1] := '3'; //'phases';
- PropertyValue[2] := Getbus(1); //'bus1';
-
- PropertyValue[propKV] := Format('%-g', [StorageVars.kVStorageBase]);
- PropertyValue[propKW] := Format('%-g', [kW_out]);
- PropertyValue[propPF] := Format('%-g', [PFNominal]);
- PropertyValue[propMODEL] := '1';
- PropertyValue[propYEARLY] := '';
- PropertyValue[propDAILY] := '';
- PropertyValue[propDUTY] := '';
- PropertyValue[propDISPMODE] := 'Default';
- PropertyValue[propCONNECTION] := 'wye';
- PropertyValue[propKVAR] := Format('%-g', [Presentkvar]);
-
- PropertyValue[propPCTR] := Format('%-g', [pctR]);
- PropertyValue[propPCTX] := Format('%-g', [pctX]);
-
- PropertyValue[propIDLEKW] := '1'; // PERCENT
- PropertyValue[propIDLEKVAR] := ''; // deprecated
- PropertyValue[propCLASS] := '1'; //'class'
- PropertyValue[propDISPOUTTRIG] := '0'; // 0 MEANS NO TRIGGER LEVEL
- PropertyValue[propDISPINTRIG] := '0';
- PropertyValue[propCHARGEEFF] := '90';
- PropertyValue[propDISCHARGEEFF] := '90';
- PropertyValue[propPCTKWOUT] := '100';
- PropertyValue[propPCTKWIN] := '100';
-
- PropertyValue[propInvEffCurve] := '';
- PropertyValue[propCutin] := '0';
- PropertyValue[propCutout] := '0';
- PropertyValue[propVarFollowInverter] := 'NO';
-
- PropertyValue[propVMINPU] := '0.90';
- PropertyValue[propVMAXPU] := '1.10';
- PropertyValue[propSTATE] := 'IDLING';
-
- with StorageVars do
- begin
- PropertyValue[propKVA] := Format('%-g', [StorageVars.FkVARating]);
- PropertyValue[propkvarLimit] := Format('%-g', [Fkvarlimit]);
- PropertyValue[propkvarLimitneg] := Format('%-g', [Fkvarlimitneg]);
- PropertyValue[propKWRATED] := Format('%-g', [kWRating]);
- PropertyValue[propKWHRATED] := Format('%-g', [kWhRating]);
- PropertyValue[propKWHSTORED] := Format('%-g', [kWhStored]);
- PropertyValue[propPCTSTORED] := Format('%-g', [kWhStored / kWhRating * 100.0])
- end;
-
- PropertyValue[propPCTRESERVE] := Format('%-g', [pctReserve]);
- PropertyValue[propCHARGETIME] := Format('%-g', [ChargeTime]);
-
- PropertyValue[propUSERMODEL] := ''; // Usermodel
- PropertyValue[propUSERDATA] := ''; // Userdata
- PropertyValue[propDYNADLL] := ''; //
- PropertyValue[propDYNADATA] := ''; //
- PropertyValue[propDEBUGTRACE] := 'NO';
- PropertyValue[propBalanced] := 'NO';
- PropertyValue[propLimited] := 'NO';
- PropertyValue[proppctkWrated] := '100'; // Included
- PropertyValue[propPpriority] := 'NO'; // Included
- PropertyValue[propPFPriority] := 'NO';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-
-//----------------------------------------------------------------------------
-function TStorage2Obj.GetPropertyValue(Index: Integer): String;
-begin
-
- Result := '';
- with StorageVars do
- case Index of
- propKV:
- Result := Format('%.6g', [StorageVars.kVStorageBase]);
- propKW:
- Result := Format('%.6g', [kW_out]);
- propPF:
- Result := Format('%.6g', [PFNominal]);
- propMODEL:
- Result := Format('%d', [VoltageModel]);
- propYEARLY:
- Result := YearlyShape;
- propDAILY:
- Result := DailyShape;
- propDUTY:
- Result := DutyShape;
-
- propDISPMODE:
- Result := ReturnDispMode(DispatchMode);
-
- {propCONNECTION :;}
- propKVAR:
- Result := Format('%.6g', [kvar_out]);
- propPCTR:
- Result := Format('%.6g', [pctR]);
- propPCTX:
- Result := Format('%.6g', [pctX]);
- propIDLEKW:
- Result := Format('%.6g', [pctIdlekW]);
- propIDLEKVAR:
- Result := ''; // deprecated
- {propCLASS = 17;}
- propInvEffCurve:
- Result := InverterCurve;
- propCutin:
- Result := Format('%.6g', [FpctCutin]);
- propCutOut:
- Result := Format('%.6g', [FpctCutOut]);
- propVarFollowInverter:
- Result := StrYorN(FVarFollowInverter);
- propPminNoVars:
- Result := Format('%.6g', [FpctPminNoVars]);
- propPminkvarLimit:
- Result := Format('%.6g', [FpctPminkvarLimit]);
-
- propDISPOUTTRIG:
- Result := Format('%.6g', [DischargeTrigger]);
- propDISPINTRIG:
- Result := Format('%.6g', [ChargeTrigger]);
- propCHARGEEFF:
- Result := Format('%.6g', [pctChargeEff]);
- propDISCHARGEEFF:
- Result := Format('%.6g', [pctDischargeEff]);
- propPCTKWOUT:
- Result := Format('%.6g', [pctkWout]);
-
- propVMINPU:
- Result := Format('%.6g', [VMinPu]);
- propVMAXPU:
- Result := Format('%.6g', [VMaxPu]);
- propSTATE:
- Result := DecodeState;
-
- {StorageVars}
- propKVA:
- Result := Format('%.6g', [FkVArating]);
- propKWRATED:
- Result := Format('%.6g', [kWrating]);
- propKWHRATED:
- Result := Format('%.6g', [kWhrating]);
- propKWHSTORED:
- Result := Format('%.6g', [kWHStored]);
-
-
- propPCTRESERVE:
- Result := Format('%.6g', [pctReserve]);
- propUSERMODEL:
- Result := UserModel.Name;
- propUSERDATA:
- Result := '(' + inherited GetPropertyValue(index) + ')';
- proppctkWrated:
- Result := Format('%.6g', [FpctkWrated * 100.0]);
- propDynaDLL:
- Result := DynaModel.Name;
- propdynaDATA:
- Result := '(' + inherited GetPropertyValue(index) + ')';
- {propDEBUGTRACE = 33;}
- propPCTKWIN:
- Result := Format('%.6g', [pctkWin]);
- propPCTSTORED:
- Result := Format('%.6g', [kWhStored / kWhRating * 100.0]);
- propCHARGETIME:
- Result := Format('%.6g', [Chargetime]);
- propBalanced:
- Result := StrYorN(ForceBalanced);
- propLimited:
- Result := StrYorN(CurrentLimited);
- propkvarLimit:
- Result := Format('%.6g', [Fkvarlimit]);
- propkvarLimitneg:
- Result := Format('%.6g', [Fkvarlimitneg]);
-
- else // take the generic handler
- Result := inherited GetPropertyValue(index);
- end;
+ RecalcElementData();
end;
-
-//----------------------------------------------------------------------------
procedure TStorage2Obj.Randomize(Opt: Integer);
begin
-
case Opt of
0:
RandomMult := 1.0;
@@ -1607,10 +1082,8 @@ procedure TStorage2Obj.Randomize(Opt: Integer);
LOGNORMAL:
RandomMult := QuasiLognormal(YearlyShapeObj.Mean);
end;
-
end;
-//----------------------------------------------------------------------------
destructor TStorage2Obj.Destroy;
begin
YPrimOpenCond.Free;
@@ -1620,10 +1093,7 @@ destructor TStorage2Obj.Destroy;
inherited Destroy;
end;
-
-//----------------------------------------------------------------------------
procedure TStorage2Obj.CalcDailyMult(Hr: Double);
-
begin
if (DailyShapeObj <> NIL) then
begin
@@ -1635,10 +1105,7 @@ procedure TStorage2Obj.CalcDailyMult(Hr: Double);
CheckStateTriggerLevel(ShapeFactor.re); // last recourse
end;
-
-//----------------------------------------------------------------------------
procedure TStorage2Obj.CalcDutyMult(Hr: Double);
-
begin
if DutyShapeObj <> NIL then
begin
@@ -1649,9 +1116,7 @@ procedure TStorage2Obj.CalcDutyMult(Hr: Double);
CalcDailyMult(Hr); // Default to Daily Mult If no duty curve specified
end;
-//----------------------------------------------------------------------------
procedure TStorage2Obj.CalcYearlyMult(Hr: Double);
-
begin
if YearlyShapeObj <> NIL then
begin
@@ -1662,21 +1127,16 @@ procedure TStorage2Obj.CalcYearlyMult(Hr: Double);
CalcDailyMult(Hr); // Defaults to Daily curve
end;
-//----------------------------------------------------------------------------
procedure TStorage2Obj.RecalcElementData();
begin
-
VBase95 := VMinPu * VBase;
VBase105 := VMaxPu * VBase;
- // removed 5/8/17 kvarBase := kvar_out ; // remember this for Follow Mode
-
with StorageVars do
begin
-
YeqDischarge := Cmplx((kWrating * 1000.0 / SQR(vbase) / FNPhases), 0.0);
- // values in ohms for thevenin equivalents
+ // values in ohms for thevenin equivalents
RThev := pctR * 0.01 * SQR(PresentkV) / FkVARating * 1000.0; // Changed
XThev := pctX * 0.01 * SQR(PresentkV) / FkVARating * 1000.0; // Changed
@@ -1693,7 +1153,7 @@ procedure TStorage2Obj.RecalcElementData();
else
PminkvarLimit := FpctPminkvarLimit * kWrating / 100.0;
- // efficiencies
+ // efficiencies
ChargeEff := pctChargeEff * 0.01;
DisChargeEff := pctDisChargeEff * 0.01;
@@ -1704,55 +1164,33 @@ procedure TStorage2Obj.RecalcElementData();
kWOutIdling := PIdling / (InverterCurveObj.GetYValue(Pidling / (FkVArating)));
end
else
- kWOutIdling := 0.0; //PIdling; //producing error- removed on 11/05/2020
+ kWOutIdling := PIdling;
end;
SetNominalStorageOutput();
- {Now check for errors. If any of these came out nil and the string was not nil, give warning}
- if YearlyShapeObj = NIL then
- if Length(YearlyShape) > 0 then
- DoSimpleMsg('WARNING! Yearly load shape: "' + YearlyShape + '" Not Found.', 563);
- if DailyShapeObj = NIL then
- if Length(DailyShape) > 0 then
- DoSimpleMsg('WARNING! Daily load shape: "' + DailyShape + '" Not Found.', 564);
- if DutyShapeObj = NIL then
- if Length(DutyShape) > 0 then
- DoSimpleMsg('WARNING! Duty load shape: "' + DutyShape + '" Not Found.', 565);
-
- if Length(Spectrum) > 0 then
- begin
- SpectrumObj := DSS.SpectrumClass.Find(Spectrum);
- if SpectrumObj = NIL then
- DoSimpleMsg('ERROR! Spectrum "' + Spectrum + '" Not Found.', 566);
- end
- else
- SpectrumObj := NIL;
-
// Initialize to Zero - defaults to PQ Storage element
// Solution object will reset after circuit modifications
Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
- {Update any user-written models}
+ // Update any user-written models
if Usermodel.Exists then
UserModel.FUpdateModel; // Checks for existence and Selects
if Dynamodel.Exists then
Dynamodel.FUpdateModel; // Checks for existence and Selects
-
end;
-//----------------------------------------------------------------------------
+
procedure TStorage2Obj.SetNominalStorageOutput();
begin
-
ShapeFactor := CDOUBLEONE; // init here; changed by curve routine
// Check to make sure the Storage element is ON
with ActiveCircuit, ActiveCircuit.Solution do
begin
if not (IsDynamicModel or IsHarmonicModel) then // Leave Storage element in whatever state it was prior to entering Dynamic mode
begin
- // Check dispatch to see what state the Storage element should be in
+ // Check dispatch to see what state the Storage element should be in
case DispatchMode of
STORE_EXTERNALMODE: ; // Do nothing
@@ -1762,20 +1200,17 @@ procedure TStorage2Obj.SetNominalStorageOutput();
CheckStateTriggerLevel(PriceSignal);
else // dispatch off element's loadshapes, If any
-
with Solution do
case Mode of
- TSolveMode.SNAPSHOT: ; {Just solve for the present kW, kvar} // Don't check for state change
+ TSolveMode.SNAPSHOT: ; // Just solve for the present kW, kvar // Don't check for state change
TSolveMode.DAILYMODE:
CalcDailyMult(DynaVars.dblHour); // Daily dispatch curve
TSolveMode.YEARLYMODE:
CalcYearlyMult(DynaVars.dblHour);
- (*
- MONTECARLO1,
- MONTEFAULT,
- FAULTSTUDY,
- DYNAMICMODE: ; // {do nothing for these modes}
- *)
+ // MONTECARLO1,
+ // MONTEFAULT,
+ // FAULTSTUDY,
+ // DYNAMICMODE: ; // do nothing for these modes
TSolveMode.GENERALTIME:
begin
// This mode allows use of one class of load shape
@@ -1790,7 +1225,7 @@ procedure TStorage2Obj.SetNominalStorageOutput();
ShapeFactor := CDOUBLEONE // default to 1 + j1 if not known
end;
end;
- // Assume Daily curve, If any, for the following
+ // Assume Daily curve, If any, for the following
TSolveMode.MONTECARLO2,
TSolveMode.MONTECARLO3,
TSolveMode.LOADDURATION1,
@@ -1801,18 +1236,16 @@ procedure TStorage2Obj.SetNominalStorageOutput();
TSolveMode.DUTYCYCLE:
CalcDutyMult(DynaVars.dblHour);
- {AUTOADDFLAG: ; }
+ // AUTOADDFLAG: ;
end;
end;
ComputekWkvar;
- {
- Pnominalperphase is net at the terminal. If supplying idling losses, when discharging,
- the Storage supplies the idling losses. When charging, the idling losses are subtracting from the amount
- entering the Storage element.
- }
+ // Pnominalperphase is net at the terminal. If supplying idling losses, when discharging,
+ // the Storage supplies the idling losses. When charging, the idling losses are subtracting from the amount
+ // entering the Storage element.
with StorageVars do
begin
@@ -1825,33 +1258,30 @@ procedure TStorage2Obj.SetNominalStorageOutput();
//**** Fix this when user model gets connected in
3: // Yeq := Cinv(cmplx(0.0, -StoreVARs.Xd)) ; // Gets negated in CalcYPrim
else
- {
- Yeq no longer used for anything other than this calculation of Yeq95, Yeq105 and
- constant Z power flow model
- }
- Yeq := CDivReal(Cmplx(Pnominalperphase, -Qnominalperphase), Sqr(Vbase)); // Vbase must be L-N for 3-phase
+ // Yeq no longer used for anything other than this calculation of Yeq95, Yeq105 and
+ // constant Z power flow model
+ Yeq := Cmplx(Pnominalperphase, -Qnominalperphase) / Sqr(Vbase); // Vbase must be L-N for 3-phase
if (Vminpu <> 0.0) then
- Yeq95 := CDivReal(Yeq, sqr(Vminpu)) // at 95% voltage
+ Yeq95 := Yeq / sqr(Vminpu) // at 95% voltage
else
Yeq95 := Yeq; // Always a constant Z model
if (Vmaxpu <> 0.0) then
- Yeq105 := CDivReal(Yeq, Sqr(Vmaxpu)) // at 105% voltage
+ Yeq105 := Yeq / Sqr(Vmaxpu) // at 105% voltage
else
Yeq105 := Yeq;
end;
- { Like Model 7 generator, max current is based on amount of current to get out requested power at min voltage
- }
+ // Like Model 7 generator, max current is based on amount of current to get out requested power at min voltage
with StorageVars do
begin
- PhaseCurrentLimit := Cdivreal(Cmplx(Pnominalperphase, Qnominalperphase), VBase95);
+ PhaseCurrentLimit := Cmplx(Pnominalperphase, Qnominalperphase) / VBase95;
MaxDynPhaseCurrent := Cabs(PhaseCurrentLimit);
end;
- { When we leave here, all the Yeq's are in L-N values}
+ // When we leave here, all the Yeq's are in L-N values
- end; {If NOT (IsDynamicModel or IsHarmonicModel)}
- end; {With ActiveCircuit}
+ end; // If NOT (IsDynamicModel or IsHarmonicModel)
+ end; // With ActiveCircuit
// If Storage element state changes, force re-calc of Y matrix
if FStateChanged then
@@ -1859,26 +1289,22 @@ procedure TStorage2Obj.SetNominalStorageOutput();
YprimInvalid := TRUE;
FStateChanged := FALSE; // reset the flag
end;
-
end;
-// ===========================================================================================
+
procedure TStorage2Obj.ComputekWkvar;
begin
-
ComputePresentkW;
ComputeInverterPower; // apply inverter eff after checking for cutin/cutout
-
end;
-//----------------------------------------------------------------------------
+
procedure TStorage2Obj.ComputePresentkW;
var
OldState: Integer;
begin
OldState := Fstate;
- FStateDesired := OldState;
+ StateDesired := OldState;
with StorageVars do
case FState of
-
STORE_CHARGING:
begin
if kWhStored < kWhRating then
@@ -1886,7 +1312,7 @@ procedure TStorage2Obj.ComputePresentkW;
STORE_FOLLOW:
begin
kW_out := kWRating * ShapeFactor.re;
- FpctkWin := abs(ShapeFactor.re) * 100.0; // keep %charge updated
+ pctkWIn := abs(ShapeFactor.re) * 100.0; // keep %charge updated
end
else
kW_out := -kWRating * pctkWin / 100.0;
@@ -1894,8 +1320,6 @@ procedure TStorage2Obj.ComputePresentkW;
else
Fstate := STORE_IDLING; // all charged up
end;
-
-
STORE_DISCHARGING:
begin
if kWhStored > kWhReserve then
@@ -1903,7 +1327,7 @@ procedure TStorage2Obj.ComputePresentkW;
STORE_FOLLOW:
begin
kW_out := kWRating * ShapeFactor.re;
- FpctkWOut := abs(ShapeFactor.re) * 100.0; // keep %discharge updated
+ pctkWOut := abs(ShapeFactor.re) * 100.0; // keep %discharge updated
end
else
kW_out := kWRating * pctkWout / 100.0;
@@ -1911,75 +1335,66 @@ procedure TStorage2Obj.ComputePresentkW;
else
Fstate := STORE_IDLING; // not enough Storage to discharge
end;
-
end;
- {If idling output is only losses}
-
+ // If idling output is only losses
if Fstate = STORE_IDLING then
begin
- kW_out := 0.0; // -kWIdlingLosses; Just use YeqIdling
- kvar_out := 0.0;
+ kW_out := -kWOutIdling;
end;
if OldState <> Fstate then
FstateChanged := TRUE;
-
end;
-// ===========================================================================================
procedure TStorage2Obj.ComputeInverterPower;
var
-
kVA_Gen: Double;
OldState: Integer;
- TempPF: Double; // temporary power factor
- Qramp_limit: Double;
-
+ TempPF: Double = 0.0; // temporary power factor
+ Qramp_limit: Double = 0.0;
begin
-
// Reset CurrentkvarLimit to kvarLimit
CurrentkvarLimit := StorageVars.Fkvarlimit;
CurrentkvarLimitNeg := StorageVars.Fkvarlimitneg;
with StorageVars do
begin
-
if Assigned(InverterCurveObj) then
begin
if Fstate = STORE_DISCHARGING then
begin
- FCutOutkWAC := CutOutkW * InverterCurveObj.GetYValue(abs(CutOutkW) / FkVArating);
- FCutInkWAC := CutInkW * InverterCurveObj.GetYValue(abs(CutInkW) / FkVArating);
+ CutOutkWAC := CutOutkW * InverterCurveObj.GetYValue(abs(CutOutkW) / FkVArating);
+ CutInkWAC := CutInkW * InverterCurveObj.GetYValue(abs(CutInkW) / FkVArating);
end
else // Charging or Idling
begin
- FCutOutkWAC := CutOutkW / InverterCurveObj.GetYValue(abs(CutOutkW) / FkVArating);
- FCutInkWAC := CutInkW / InverterCurveObj.GetYValue(abs(CutInkW) / FkVArating);
+ CutOutkWAC := CutOutkW / InverterCurveObj.GetYValue(abs(CutOutkW) / FkVArating);
+ CutInkWAC := CutInkW / InverterCurveObj.GetYValue(abs(CutInkW) / FkVArating);
end;
end
else // Assume Ideal Inverter
begin
- FCutOutkWAC := CutOutkW;
- FCutInkWAC := CutInkW;
+ CutOutkWAC := CutOutkW;
+ CutInkWAC := CutInkW;
end;
OldState := Fstate;
// CutIn/CutOut checking performed on the AC side.
- if FInverterON then
+ if InverterON then
begin
- if abs(kW_Out) < FCutOutkWAC then
+ if abs(kW_Out) < CutOutkWAC then
begin
- FInverterON := FALSE;
+ InverterON := FALSE;
Fstate := STORE_IDLING;
end;
end
else
begin
- if abs(kW_Out) >= FCutInkWAC then
+ if abs(kW_Out) >= CutInkWAC then
begin
- FInverterON := TRUE;
+ InverterON := TRUE;
end
else
begin
@@ -1987,29 +1402,27 @@ procedure TStorage2Obj.ComputeInverterPower;
end;
end;
-
if OldState <> Fstate then
FstateChanged := TRUE;
- // Set inverter output
- if FInverterON then
+ // Set inverter output
+ if InverterON then
begin
kWOut_Calc;
end
else
begin
- // Idling
+ // Idling
kW_Out := -kWOutIdling; // In case it has just turned off due to %CutIn/%CutOut. Necessary to make sure SOC will be kept constant (higher priority than the %CutIn/%CutOut operation)
end;
-
- // Calculate kvar value based on operation mode (PF or kvar)
+ // Calculate kvar value based on operation mode (PF or kvar)
if FState = STORE_IDLING then // Should watt-pf with watt=0 be applied here?
// If in Idling state, check for kvarlimit only
begin
if varMode = VARMODEPF then
begin
-// kvar_out := 0.0; //kW = 0 leads to kvar = 0 in constant PF Mode
+ // kvar_out := 0.0; //kW = 0 leads to kvar = 0 in constant PF Mode
kvar_out := kW_out * sqrt(1.0 / SQR(PFnominal) - 1.0) * sign(PFnominal);
if (kvar_out > 0.0) and (abs(kvar_out) > Fkvarlimit) then
@@ -2021,7 +1434,6 @@ procedure TStorage2Obj.ComputeInverterPower;
end
else // kvarRequested might have been set either internally or by an InvControl
begin
-
if (kvarRequested > 0.0) and (abs(kvarRequested) > Fkvarlimit) then
kvar_Out := Fkvarlimit
else
@@ -2029,7 +1441,6 @@ procedure TStorage2Obj.ComputeInverterPower;
kvar_Out := Fkvarlimitneg * sign(kvarRequested)
else
kvar_Out := kvarRequested;
-
end;
end
else
@@ -2051,12 +1462,12 @@ procedure TStorage2Obj.ComputeInverterPower;
begin
kvar_out := kW_out * sqrt(1.0 / SQR(PFnominal) - 1.0) * sign(PFnominal); //kvar_out desired by constant PF
- // Check Limits
+ // Check Limits
if abs(kW_out) < PminkvarLimit then // straight line limit check. if PminkvarLimit is disabled (-1), this will always be false.
begin
- // straight line starts at max(PminNoVars, FCutOutkWAC)
- // if CutOut differs from CutIn, take cutout since it is assumed that CutOut <= CutIn always.
- if abs(kW_out) >= max(PminNoVars, FCutOutkWAC) then
+ // straight line starts at max(PminNoVars, CutOutkWAC)
+ // if CutOut differs from CutIn, take cutout since it is assumed that CutOut <= CutIn always.
+ if abs(kW_out) >= max(PminNoVars, CutOutkWAC) then
begin
if (kvar_Out > 0.0) then
begin
@@ -2076,9 +1487,7 @@ procedure TStorage2Obj.ComputeInverterPower;
CurrentkvarLimit := Qramp_limit; // For use in InvControl
if kvar_out < 0 then
CurrentkvarLimitNeg := Qramp_limit; // For use in InvControl
-
end;
-
end
end
else
@@ -2093,19 +1502,17 @@ procedure TStorage2Obj.ComputeInverterPower;
begin
kW_out := kvar_out * sqrt(1.0 / (1.0 - Sqr(PFnominal)) - 1.0) * sign(PFnominal);
end;
-
end;
-
end;
end
else // VARMODE kvar
begin
- // Check limits
+ // Check limits
if abs(kW_out) < PminkvarLimit then // straight line limit check. if PminkvarLimit is disabled (-1), this will always be false.
begin
- // straight line starts at max(PminNoVars, FCutOutkWAC)
- // if CutOut differs from CutIn, take cutout since it is assumed that CutOut <= CutIn always.
- if abs(kW_out) >= max(PminNoVars, FCutOutkWAC) then
+ // straight line starts at max(PminNoVars, CutOutkWAC)
+ // if CutOut differs from CutIn, take cutout since it is assumed that CutOut <= CutIn always.
+ if abs(kW_out) >= max(PminNoVars, CutOutkWAC) then
begin
if (kvarRequested > 0.0) then
begin
@@ -2123,61 +1530,55 @@ procedure TStorage2Obj.ComputeInverterPower;
kvar_out := Qramp_limit * sign(kvarRequested)
else
kvar_out := kvarRequested;
-
end;
-
end
else
if ((kvarRequested > 0.0) and (abs(kvarRequested) > Fkvarlimit)) or ((kvarRequested < 0.0) and (abs(kvarRequested) > Fkvarlimitneg)) then
begin
-
if (kvarRequested > 0.0) then
kvar_Out := Fkvarlimit * sign(kvarRequested)
else
kvar_Out := Fkvarlimitneg * sign(kvarRequested);
- if (varMode = VARMODEKVAR) and PF_Priority and FWPMode then
+ if (varMode = VARMODEKVAR) and PF_Priority and WPMode then
begin
- kW_out := abs(kvar_out) * sqrt(1.0 / (1.0 - Sqr(Fpf_wp_nominal)) - 1.0) * sign(kW_out);
+ kW_out := abs(kvar_out) * sqrt(1.0 / (1.0 - Sqr(pf_wp_nominal)) - 1.0) * sign(kW_out);
end
- // Forces constant power factor when kvar limit is exceeded and PF Priority is true. Temp PF is calculated based on kvarRequested
- // PF Priority is not valid if controlled by an InvControl operating in at least one amongst VV and DRC modes
+ // Forces constant power factor when kvar limit is exceeded and PF Priority is true. Temp PF is calculated based on kvarRequested
+ // PF Priority is not valid if controlled by an InvControl operating in at least one amongst VV and DRC modes
else
- if PF_Priority and (not FVVMode or not FDRCMode or not FWVmode) then
+ if PF_Priority and (not VVMode or not DRCMode or not WVmode) then
begin
if abs(kvarRequested) > 0.0 then
begin
-
TempPF := cos(arctan(abs(kvarRequested / kW_out)));
kW_out := abs(kvar_out) * sqrt(1.0 / (1.0 - Sqr(TempPF)) - 1.0) * sign(kW_out);
end
end
-
end
else
kvar_Out := kvarRequested;
end;
end;
- if (FInverterON = FALSE) and (FVarFollowInverter = TRUE) then
+ if (InverterON = FALSE) and (VarFollowInverter = TRUE) then
kvar_out := 0.0;
- // Limit kvar and kW so that kVA of inverter is not exceeded
+ // Limit kvar and kW so that kVA of inverter is not exceeded
kVA_Gen := Sqrt(Sqr(kW_out) + Sqr(kvar_out));
if kVA_Gen > FkVArating then
begin
-
kVA_exceeded := TRUE;
- // Expectional case: When kVA is exceeded and in idling state, we force P priority always
+ // Expectional case: When kVA is exceeded and in idling state, we force P priority always
if (FState = STORE_IDLING) then
begin
kvar_Out := Sqrt(SQR(FkVArating) - SQR(kW_Out)) * sign(kvar_Out);
end
- // Regular Cases
+ // Regular Cases
else
if (varMode = VARMODEPF) and PF_Priority then
// Operates under constant power factor when kVA rating is exceeded. PF must be specified and PFPriority must be TRUE
@@ -2187,13 +1588,13 @@ procedure TStorage2Obj.ComputeInverterPower;
kvar_out := FkVArating * sqrt(1 - Sqr(PFnominal)) * sign(kW_out) * sign(PFnominal);
end
else
- if (varMode = VARMODEKVAR) and PF_Priority and FWPMode then
+ if (varMode = VARMODEKVAR) and PF_Priority and WPMode then
begin
- kW_out := FkVArating * abs(Fpf_wp_nominal) * sign(kW_out);
- kvar_out := FkVArating * abs(sin(ArcCos(Fpf_wp_nominal))) * sign(kvarRequested)
+ kW_out := FkVArating * abs(pf_wp_nominal) * sign(kW_out);
+ kvar_out := FkVArating * abs(sin(ArcCos(pf_wp_nominal))) * sign(kvarRequested)
end
else
- if (varMode = VARMODEKVAR) and PF_Priority and (not FVVMode or not FDRCMode or not FWVmode) then
+ if (varMode = VARMODEKVAR) and PF_Priority and (not VVMode or not DRCMode or not WVmode) then
// Operates under constant power factor (PF implicitly calculated based on kw and kvar)
begin
if abs(kvar_out) = Fkvarlimit then
@@ -2224,40 +1625,34 @@ procedure TStorage2Obj.ComputeInverterPower;
kW_Out := Sqrt(SQR(FkVArating) - SQR(kvar_Out)) * sign(kW_Out); // Q Priority (Default) back off the kW
end;
-
- end {With StorageVars}
+ end // With StorageVars
else
if abs(kVA_Gen - FkVArating) / FkVArating < 0.0005 then
kVA_exceeded := TRUE
else
kVA_exceeded := FALSE;
-
end;
-
end;
-//----------------------------------------------------------------------------
-procedure TStorage2Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
+procedure TStorage2Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
var
Y, Yij: Complex;
i, j: Integer;
FreqMultiplier: Double;
-
begin
-
FYprimFreq := ActiveCircuit.Solution.Frequency;
FreqMultiplier := FYprimFreq / BaseFrequency;
-
- with ActiveCircuit.solution do
- if {IsDynamicModel or} IsHarmonicModel then
+
+ with ActiveCircuit.Solution do
+ if IsHarmonicModel then // IsDynamicModel or
begin
- {Yeq is computed from %R and %X -- inverse of Rthev + j Xthev}
+ // Yeq is computed from %R and %X -- inverse of Rthev + j Xthev
Y := Yeq;
if Connection = 1 then
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
+ Y := Y / 3.0; // Convert to delta impedance
Y.im := Y.im / FreqMultiplier;
- Yij := Cnegate(Y);
+ Yij := -Y;
for i := 1 to Fnphases do
begin
case Connection of
@@ -2268,7 +1663,7 @@ procedure TStorage2Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
Ymatrix.SetElemsym(i, Fnconds, Yij);
end;
1:
- begin {Delta connection}
+ begin // Delta connection
Ymatrix.SetElement(i, i, Y);
Ymatrix.AddElement(i, i, Y); // put it in again
for j := 1 to i - 1 do
@@ -2277,33 +1672,26 @@ procedure TStorage2Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
end;
end;
end
-
else
begin // Regular power flow Storage element model
-
- {Yeq is always expected as the equivalent line-neutral admittance}
-
-
+ // Yeq is always expected as the equivalent line-neutral admittance
case Fstate of
STORE_CHARGING:
Y := YeqDischarge;
STORE_IDLING:
Y := cmplx(0.0, 0.0);
STORE_DISCHARGING:
- Y := cnegate(YeqDischarge);
+ Y := -YeqDischarge;
end;
-
- //---DEBUG--- WriteDLLDebugFile(Format('t=%.8g, Change To State=%s, Y=%.8g +j %.8g',[ActiveCircuit.Solution.dblHour, StateToStr, Y.re, Y.im]));
-
- // ****** Need to modify the base admittance for real harmonics calcs
+ //---DEBUG--- WriteDLLDebugFile(Format('t=%.8g, Change To State=%s, Y=%.8g +j %.8g',[ActiveCircuit.Solution.dblHour, StateToStr, Y.re, Y.im]));
+ // ****** Need to modify the base admittance for real harmonics calcs
Y.im := Y.im / FreqMultiplier;
case Connection of
-
0:
with YMatrix do
begin // WYE
- Yij := Cnegate(Y);
+ Yij := -Y;
for i := 1 to Fnphases do
begin
SetElement(i, i, Y);
@@ -2311,12 +1699,11 @@ procedure TStorage2Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
SetElemsym(i, Fnconds, Yij);
end;
end;
-
1:
with YMatrix do
begin // Delta or L-L
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
- Yij := Cnegate(Y);
+ Y := Y / 3.0; // Convert to delta impedance
+ Yij := -Y;
for i := 1 to Fnphases do
begin
j := i + 1;
@@ -2327,21 +1714,16 @@ procedure TStorage2Obj.CalcYPrimMatrix(Ymatrix: TcMatrix);
AddElemSym(i, j, Yij);
end;
end;
-
end;
- end; {ELSE IF Solution.mode}
-
+ end; // ELSE IF Solution.mode
end;
-//----------------------------------------------------------------------------
function TStorage2Obj.NormalizeToTOD(h: Integer; sec: Double): Double;
// Normalize time to a floating point number representing time of day If Hour > 24
// time should be 0 to 24.
var
HourOfDay: Integer;
-
begin
-
if h > 23 then
HourOfDay := (h - (h div 24) * 24)
else
@@ -2351,16 +1733,12 @@ function TStorage2Obj.NormalizeToTOD(h: Integer; sec: Double): Double;
if Result > 24.0 then
Result := Result - 24.0; // Wrap around
-
end;
-//----------------------------------------------------------------------------
procedure TStorage2Obj.CheckStateTriggerLevel(Level: Double);
-{This is where we set the state of the Storage element}
-
+// This is where we set the state of the Storage element
var
OldState: Integer;
-
begin
FStateChanged := FALSE;
@@ -2369,8 +1747,7 @@ procedure TStorage2Obj.CheckStateTriggerLevel(Level: Double);
with StorageVars do
if DispatchMode = STORE_FOLLOW then
begin
-
- // set charge and discharge modes based on sign of loadshape
+ // set charge and discharge modes based on sign of loadshape
if (Level > 0.0) and (kWhStored > kWhReserve) then
StorageState := STORE_DISCHARGING
else
@@ -2378,15 +1755,13 @@ procedure TStorage2Obj.CheckStateTriggerLevel(Level: Double);
StorageState := STORE_CHARGING
else
StorageState := STORE_IDLING;
-
end
else
begin // All other dispatch modes Just compare to trigger value
-
if (ChargeTrigger = 0.0) and (DischargeTrigger = 0.0) then
Exit;
- // First see If we want to turn off Charging or Discharging State
+ // First see If we want to turn off Charging or Discharging State
case Fstate of
STORE_CHARGING:
if (ChargeTrigger <> 0.0) then
@@ -2398,7 +1773,7 @@ procedure TStorage2Obj.CheckStateTriggerLevel(Level: Double);
Fstate := STORE_IDLING;
end;
- // Now check to see If we want to turn on the opposite state
+ // Now check to see If we want to turn on the opposite state
case Fstate of
STORE_IDLING:
begin
@@ -2427,16 +1802,12 @@ procedure TStorage2Obj.CheckStateTriggerLevel(Level: Double);
end;
end;
-//----------------------------------------------------------------------------
procedure TStorage2Obj.CalcYPrim();
-
var
i: Integer;
-
begin
-
- // Build only shunt Yprim
- // Build a dummy Yprim Series so that CalcV Does not fail
+ // Build only shunt Yprim
+ // Build a dummy Yprim Series so that CalcV Does not fail
if YprimInvalid then
begin
if YPrim_Shunt <> NIL then
@@ -2461,50 +1832,42 @@ procedure TStorage2Obj.CalcYPrim();
// Set YPrim_Series based on diagonals of YPrim_shunt so that CalcVoltages Doesn't fail
for i := 1 to Yorder do
- Yprim_Series.SetElement(i, i, CmulReal(Yprim_Shunt.Getelement(i, i), 1.0e-10));
+ Yprim_Series.SetElement(i, i, Yprim_Shunt.Getelement(i, i) * 1.0e-10);
YPrim.CopyFrom(YPrim_Shunt);
// Account for Open Conductors
inherited CalcYPrim();
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.StickCurrInTerminalArray(TermArray: pComplexArray; const Curr: Complex; i: Integer);
- {Add the current into the proper location according to connection}
-
- {Reverse of similar routine in load (Cnegates are switched)}
-
+// Add the current into the proper location according to connection
+// Reverse of similar routine in load (Cnegates are switched)
var
j: Integer;
-
begin
case Connection of
0:
begin //Wye
- Caccum(TermArray^[i], Curr);
- Caccum(TermArray^[Fnconds], Cnegate(Curr)); // Neutral
+ TermArray^[i] += Curr;
+ TermArray^[Fnconds] += -Curr; // Neutral
end;
1:
begin //DELTA
- Caccum(TermArray^[i], Curr);
+ TermArray^[i] += Curr;
j := i + 1;
if j > Fnconds then
j := 1;
- Caccum(TermArray^[j], Cnegate(Curr));
+ TermArray^[j] -= Curr;
end;
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.WriteTraceRecord(const s: String);
-
var
i: Integer;
sout: String;
begin
-
try
if (not DSS.InshowResults) then
begin
@@ -2512,8 +1875,8 @@ procedure TStorage2Obj.WriteTraceRecord(const s: String);
[ActiveCircuit.Solution.DynaVars.dblHour,
ActiveCircuit.Solution.Iteration,
ActiveCircuit.LoadMultiplier]),
- GetSolutionModeID(DSS), ', ',
- GetLoadModel(DSS), ', ',
+ DSS.SolveModeEnum.OrdinalToString(ord(DSS.ActiveCircuit.Solution.mode)), ', ',
+ DSS.DefaultLoadModelEnum.OrdinalToString(DSS.ActiveCircuit.Solution.LoadModel), ', ',
VoltageModel: 0, ', ',
(Qnominalperphase * 3.0 / 1.0e6): 8: 2, ', ',
(Pnominalperphase * 3.0 / 1.0e6): 8: 2, ', ',
@@ -2538,7 +1901,6 @@ procedure TStorage2Obj.WriteTraceRecord(const s: String);
for i := 1 to NumVariables do
FSWrite(TraceFile, Format('%-.g, ', [Variable[i]]));
- //**** Write(TraceFile,VThevMag:8:1 ,', ', StoreVARs.Theta*180.0/PI);
FSWriteln(TRacefile);
FSFlush(TraceFile);
end;
@@ -2549,11 +1911,9 @@ procedure TStorage2Obj.WriteTraceRecord(const s: String);
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TStorage2Obj.DoConstantPQStorage2Obj();
-
-{Compute total terminal current for Constant PQ}
+procedure TStorage2Obj.DoConstantPQStorage2Obj();
+// Compute total terminal current for Constant PQ
var
i: Integer;
Curr,
@@ -2565,29 +1925,11 @@ procedure TStorage2Obj.DoConstantPQStorage2Obj();
V012: array[0..2] of Complex; // Sequence voltages
begin
- //Treat this just like the Load model
+ //Treat this just like the Load model
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
ZeroITerminal;
- //---DEBUG--- WriteDLLDebugFile(Format('t=%.8g, State=%s, Iyprim= %s', [ActiveCircuit.Solution.dblHour, StateToStr, CmplxArrayToString(InjCurrent, Yprim.Order) ]));
-
-// CASE FState of
-// STORE_IDLING: // YPrim current is only current
-// Begin
-// For i := 1 to FNPhases Do
-// Begin
-// Curr := InjCurrent^[i];
-// StickCurrInTerminalArray(ITerminal, Curr, i); // Put YPrim contribution into Terminal array taking into account connection
-// set_ITerminalUpdated(True);
-// StickCurrInTerminalArray(InjCurrent, Cnegate(Curr), i); // Compensation current is zero since terminal current is same as Yprim contribution
-// //---DEBUG--- S := Cmul(Vterminal^[i] , Conjg(Iterminal^[i])); // for debugging below
-// //---DEBUG--- WriteDLLDebugFile(Format(' Phase=%d, Pnom=%.8g +j %.8g',[i, S.re, S.im ]));
-// End;
-// //---DEBUG--- WriteDLLDebugFile(Format(' Icomp=%s ', [CmplxArrayToString(InjCurrent, Yprim.Order) ]));
-// End;
-// ELSE // For Charging and Discharging
-
CalcVTerminalPhase(); // get actual voltage across each phase of the load
if ForceBalanced and (Fnphases = 3) then
@@ -2600,28 +1942,26 @@ procedure TStorage2Obj.DoConstantPQStorage2Obj();
for i := 1 to Fnphases do
begin
-
case Connection of
-
0:
- begin {Wye}
+ begin // Wye
VLN := Vterminal^[i];
VMagLN := Cabs(VLN);
if VMagLN <= VBase95 then
- Curr := Cmul(Yeq95, VLN) // Below 95% use an impedance model
+ Curr := Yeq95 * VLN // Below 95% use an impedance model
else
if VMagLN > VBase105 then
- Curr := Cmul(Yeq105, VLN) // above 105% use an impedance model
+ Curr := Yeq105 * VLN // above 105% use an impedance model
else
- Curr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLN)); // Between 95% -105%, constant PQ
+ Curr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLN); // Between 95% -105%, constant PQ
if CurrentLimited then
if Cabs(Curr) > MaxDynPhaseCurrent then
- Curr := Conjg(Cdiv(PhaseCurrentLimit, CDivReal(VLN, VMagLN)));
+ Curr := cong(PhaseCurrentLimit / (VLN / VMagLN));
end;
1:
- begin {Delta}
+ begin // Delta
VLL := Vterminal^[i];
VMagLL := Cabs(VLL);
if Fnphases > 1 then
@@ -2629,52 +1969,45 @@ procedure TStorage2Obj.DoConstantPQStorage2Obj();
else
VMagLN := VmagLL; // L-N magnitude
if VMagLN <= VBase95 then
- Curr := Cmul(CdivReal(Yeq95, 3.0), VLL) // Below 95% use an impedance model
+ Curr := (Yeq95 / 3.0) * VLL // Below 95% use an impedance model
else
if VMagLN > VBase105 then
- Curr := Cmul(CdivReal(Yeq105, 3.0), VLL) // above 105% use an impedance model
+ Curr := (Yeq105 / 3.0) * VLL // above 105% use an impedance model
else
- Curr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLL)); // Between 95% -105%, constant PQ
+ Curr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLL); // Between 95% -105%, constant PQ
if CurrentLimited then
if Cabs(Curr) * SQRT3 > MaxDynPhaseCurrent then
- Curr := Conjg(Cdiv(PhaseCurrentLimit, CDivReal(VLL, VMagLN))); // Note VmagLN has sqrt3 factor in it
+ Curr := cong(PhaseCurrentLimit / (VLL / VMagLN)); // Note VmagLN has sqrt3 factor in it
end;
end;
- //---DEBUG--- WriteDLLDebugFile(Format(' Phase=%d, Pnom=%.8g +j %.8g', [i, Pnominalperphase, Qnominalperphase ]));
+ //---DEBUG--- WriteDLLDebugFile(Format(' Phase=%d, Pnom=%.8g +j %.8g', [i, Pnominalperphase, Qnominalperphase ]));
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
set_ITerminalUpdated(TRUE);
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
- //---DEBUG--- WriteDLLDebugFile(Format(' Icomp=%s ', [CmplxArrayToString(InjCurrent, Yprim.Order) ]));
-// END;
-
+ //---DEBUG--- WriteDLLDebugFile(Format(' Icomp=%s ', [CmplxArrayToString(InjCurrent, Yprim.Order) ]));
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.DoConstantZStorage2Obj();
-
-{constant Z model}
+// constant Z model
var
i: Integer;
Curr,
Yeq2: Complex;
V012: array[0..2] of Complex; // Sequence voltages
-
begin
-
-// Assume Yeq is kept up to date
-
+ // Assume Yeq is kept up to date
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase(); // get actual voltage across each phase of the load
ZeroITerminal;
if Connection = 0 then
Yeq2 := Yeq
else
- Yeq2 := CdivReal(Yeq, 3.0);
+ Yeq2 := Yeq / 3.0;
if ForceBalanced and (Fnphases = 3) then
begin // convert to pos-seq only
@@ -2686,25 +2019,18 @@ procedure TStorage2Obj.DoConstantZStorage2Obj();
for i := 1 to Fnphases do
begin
-
- Curr := Cmul(Yeq2, Vterminal^[i]); // Yeq is always line to neutral
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ Curr := Yeq2 * Vterminal^[i]; // Yeq is always line to neutral
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
set_ITerminalUpdated(TRUE);
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
-
end;
-
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.DoUserModel();
-{Compute total terminal Current from User-written model}
+// Compute total terminal Current from User-written model
var
i: Integer;
-
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
if UserModel.Exists then // Check automatically selects the usermodel If true
@@ -2714,49 +2040,38 @@ procedure TStorage2Obj.DoUserModel();
with ActiveCircuit.Solution do
begin // Negate currents from user model for power flow Storage element model
for i := 1 to FnConds do
- Caccum(InjCurrent^[i], Cnegate(Iterminal^[i]));
+ InjCurrent^[i] -= Iterminal^[i];
end;
end
else
begin
- DoSimpleMsg('Storage.' + name + ' model designated to use user-written model, but user-written model is not defined.', 567);
+ DoSimpleMsg('%s model designated to use user-written model, but user-written model is not defined.', [FullName], 567);
end;
-
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.DoDynamicMode;
+// Compute Total Current and add into InjTemp
-{Compute Total Current and add into InjTemp}
-{
- For now, just assume the Storage element Thevenin voltage is constant
- for the duration of the dynamic simulation.
-}
-{****}
+// For now, just assume the Storage element Thevenin voltage is constant
+// for the duration of the dynamic simulation.
var
i: Integer;
V012,
I012: array[0..2] of Complex;
-
procedure CalcVthev_Dyn;
begin
with StorageVars do
Vthev := pclx(VthevMag, Theta); // keeps theta constant
end;
-
begin
-
-{****} // Test using DESS model
+ // Test using DESS model
// Compute Vterminal
if DynaModel.Exists then
DoDynaModel() // do user-written model
-
else
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
ZeroITerminal;
@@ -2768,11 +2083,11 @@ procedure TStorage2Obj.DoDynamicMode;
1:
begin
CalcVthev_Dyn; // Update for latest phase angle
- ITerminal^[1] := CDiv(CSub(Csub(VTerminal^[1], Vthev), VTerminal^[2]), Zthev);
+ ITerminal^[1] := (VTerminal^[1] - Vthev - VTerminal^[2]) / Zthev;
if CurrentLimited then
if Cabs(Iterminal^[1]) > MaxDynPhaseCurrent then // Limit the current but keep phase angle
ITerminal^[1] := ptocomplex(topolar(MaxDynPhaseCurrent, cang(Iterminal^[1])));
- ITerminal^[2] := Cnegate(ITerminal^[1]);
+ ITerminal^[2] := -ITerminal^[1];
end;
3:
begin
@@ -2782,7 +2097,7 @@ procedure TStorage2Obj.DoDynamicMode;
CalcVthev_Dyn; // Update for latest phase angle
// Positive Sequence Contribution to Iterminal
- I012[1] := CDiv(Csub(V012[1], Vthev), Zthev);
+ I012[1] := (V012[1] - Vthev) / Zthev;
if CurrentLimited and (Cabs(I012[1]) > MaxDynPhaseCurrent) then // Limit the pos seq current but keep phase angle
I012[1] := ptocomplex(topolar(MaxDynPhaseCurrent, cang(I012[1])));
@@ -2792,7 +2107,7 @@ procedure TStorage2Obj.DoDynamicMode;
I012[2] := CZERO;
end
else
- I012[2] := Cdiv(V012[2], Zthev); // for inverter
+ I012[2] := V012[2] / Zthev; // for inverter
I012[0] := CZERO;
@@ -2800,27 +2115,22 @@ procedure TStorage2Obj.DoDynamicMode;
end;
else
- DoSimpleMsg(Format('Dynamics mode is implemented only for 1- or 3-phase Storage Element. Storage.%s has %d phases.', [name, Fnphases]), 5671);
+ DoSimpleMsg('Dynamics mode is implemented only for 1- or 3-phase Storage Element. %s has %d phases.', [FullName, Fnphases], 5671);
DSS.SolutionAbort := TRUE;
end;
- {Add it into inj current array}
+ // Add it into inj current array
for i := 1 to FnConds do
- Caccum(InjCurrent^[i], Cnegate(Iterminal^[i]));
-
+ InjCurrent^[i] -= Iterminal^[i];
end;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.DoDynaModel();
var
DESSCurr: array[1..6] of Complex; // Temporary biffer
i: Integer;
-
begin
-// do user written dynamics model
-
+ // do user written dynamics model
with ActiveCircuit.Solution do
begin // Just pass node voltages to ground and let dynamic model take care of it
for i := 1 to FNconds do
@@ -2835,75 +2145,63 @@ procedure TStorage2Obj.DoDynaModel();
for i := 1 to Fnphases do
begin
- StickCurrInTerminalArray(ITerminal, Cnegate(DESSCurr[i]), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -DESSCurr[i], i); // Put into Terminal array taking into account connection
set_ITerminalUpdated(TRUE);
StickCurrInTerminalArray(InjCurrent, DESSCurr[i], i); // Put into Terminal array taking into account connection
end;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.DoHarmonicMode();
+// Compute Injection Current Only when in harmonics mode
-{Compute Injection Current Only when in harmonics mode}
-
-{Assumes spectrum is a voltage source behind subtransient reactance and YPrim has been built}
-{Vd is the fundamental frequency voltage behind Xd" for phase 1}
-
+// Assumes spectrum is a voltage source behind subtransient reactance and YPrim has been built
+// Vd is the fundamental frequency voltage behind Xd" for phase 1
var
i: Integer;
E: Complex;
StorageHarmonic: Double;
-
+ pBuffer: PCBuffer24;
begin
-
+ pBuffer := @TStorage2(ParentClass).cBuffer;
ComputeVterminal();
with ActiveCircuit.Solution do
begin
StorageHarmonic := Frequency / StorageFundamental;
if SpectrumObj <> NIL then
- E := CmulReal(SpectrumObj.GetMult(StorageHarmonic), StorageVars.VThevHarm) // Get base harmonic magnitude
+ E := SpectrumObj.GetMult(StorageHarmonic) * StorageVars.VThevHarm // Get base harmonic magnitude
else
E := CZERO;
RotatePhasorRad(E, StorageHarmonic, StorageVars.ThetaHarm); // Time shift by fundamental frequency phase shift
for i := 1 to Fnphases do
begin
- cBuffer[i] := E;
+ pBuffer[i] := E;
if i < Fnphases then
RotatePhasorDeg(E, StorageHarmonic, -120.0); // Assume 3-phase Storage element
end;
end;
- {Handle Wye Connection}
+ // Handle Wye Connection
if Connection = 0 then
- cbuffer[Fnconds] := Vterminal^[Fnconds]; // assume no neutral injection voltage
-
- {Inj currents = Yprim (E) }
- YPrim.MVMult(InjCurrent, pComplexArray(@cBuffer));
+ pBuffer[Fnconds] := Vterminal^[Fnconds]; // assume no neutral injection voltage
+ // Inj currents = Yprim (E)
+ YPrim.MVMult(InjCurrent, pComplexArray(pBuffer));
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.CalcVTerminalPhase();
-
var
i, j: Integer;
-
begin
-
-{ Establish phase voltages and stick in Vterminal}
+ // Establish phase voltages and stick in Vterminal
case Connection of
-
0:
begin
with ActiveCircuit.Solution do
for i := 1 to Fnphases do
Vterminal^[i] := VDiff(NodeRef^[i], NodeRef^[Fnconds]);
end;
-
1:
begin
with ActiveCircuit.Solution do
@@ -2915,31 +2213,14 @@ procedure TStorage2Obj.CalcVTerminalPhase();
Vterminal^[i] := VDiff(NodeRef^[i], NodeRef^[j]);
end;
end;
-
end;
StorageSolutionCount := ActiveCircuit.Solution.SolutionCount;
-
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-(*
-PROCEDURE TStorage2Obj.CalcVTerminal;
-{Put terminal voltages in an array}
-Begin
- ComputeVTerminal;
- StorageSolutionCount := ActiveCircuit.Solution.SolutionCount;
-End;
-*)
-
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.CalcStorageModelContribution();
-
// Calculates Storage element current and adds it properly into the injcurrent array
// routines may also compute ITerminal (ITerminalUpdated flag)
-
begin
set_ITerminalUpdated(FALSE);
with ActiveCircuit, ActiveCircuit.Solution do
@@ -2962,29 +2243,23 @@ procedure TStorage2Obj.CalcStorageModelContribution();
else
DoConstantPQStorage2Obj(); // for now, until we implement the other models.
end;
- end; {ELSE}
- end; {WITH}
-
- {When this is Done, ITerminal is up to date}
-
+ end;
+ end;
+ // When this is Done, ITerminal is up to date
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.CalcInjCurrentArray();
// Difference between currents in YPrim and total current
begin
- // Now Get Injection Currents
+ // Now Get Injection Currents
if Storage2ObjSwitchOpen then
ZeroInjCurrent
else
CalcStorageModelContribution();
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.GetTerminalCurrents(Curr: pComplexArray);
-
// Compute total Currents
-
begin
with ActiveCircuit.Solution do
begin
@@ -2998,35 +2273,28 @@ procedure TStorage2Obj.GetTerminalCurrents(Curr: pComplexArray);
if (DebugTrace) then
WriteTraceRecord('TotalCurrent');
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
function TStorage2Obj.InjCurrents(): Integer;
-
begin
with ActiveCircuit.Solution do
begin
if LoadsNeedUpdating then
SetNominalStorageOutput(); // Set the nominal kW, etc for the type of solution being Done
- CalcInjCurrentArray(); // Difference between currents in YPrim and total terminal current
+ CalcInjCurrentArray(); // Difference between currents in YPrim and total terminal current
if (DebugTrace) then
WriteTraceRecord('Injection');
// Add into System Injection Current Array
-
Result := inherited InjCurrents();
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.ResetRegisters;
-
var
i: Integer;
-
begin
for i := 1 to NumStorage2Registers do
Registers[i] := 0.0;
@@ -3035,38 +2303,31 @@ procedure TStorage2Obj.ResetRegisters;
FirstSampleAfterReset := TRUE; // initialize for trapezoidal integration
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.Integrate(Reg: Integer; const Deriv: Double; const Interval: Double);
-
begin
if ActiveCircuit.TrapezoidalIntegration then
begin
- {Trapezoidal Rule Integration}
+ // Trapezoidal Rule Integration
if not FirstSampleAfterReset then
Registers[Reg] := Registers[Reg] + 0.5 * Interval * (Deriv + Derivatives[Reg]);
end
- else {Plain Euler integration}
+ else // Plain Euler integration
Registers[Reg] := Registers[Reg] + Interval * Deriv;
Derivatives[Reg] := Deriv;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TStorage2Obj.TakeSample();
// Update Energy from metered zone
-
var
S: Complex;
Smag: Double;
HourValue: Double;
-
begin
-
-// Compute energy in Storage element branch
+ // Compute energy in Storage element branch
if Enabled then
begin
-
- // Only tabulate discharge hours
+ // Only tabulate discharge hours
if FSTate = STORE_DISCHARGING then
begin
S := cmplx(Get_PresentkW, Get_Presentkvar);
@@ -3081,13 +2342,13 @@ procedure TStorage2Obj.TakeSample();
end;
if (FState = STORE_DISCHARGING) or ActiveCircuit.TrapezoidalIntegration then
- {Make sure we always integrate for Trapezoidal case
- Don't need to for Gen Off and normal integration}
+ // Make sure we always integrate for Trapezoidal case
+ // Don't need to for Gen Off and normal integration
with ActiveCircuit.Solution do
begin
if ActiveCircuit.PositiveSequence then
begin
- S := CmulReal(S, 3.0);
+ S := S * 3;
Smag := 3.0 * Smag;
end;
Integrate(Reg_kWh, S.re, IntervalHrs); // Accumulate the power
@@ -3101,27 +2362,21 @@ procedure TStorage2Obj.TakeSample();
end;
end;
-//----------------------------------------------------------------------------
-
procedure TStorage2Obj.UpdateStorage();
-{Update Storage levels}
+// Update Storage levels
begin
-
with StorageVars do
begin
-
kWhBeforeUpdate := kWhStored; // keep this for reporting change in Storage as a variable
- {Assume User model will take care of updating Storage in dynamics mode}
+ // Assume User model will take care of updating Storage in dynamics mode
if ActiveCircuit.solution.IsDynamicModel and IsUserModel then
Exit;
with ActiveCircuit.Solution do
case FState of
-
STORE_DISCHARGING:
begin
-
kWhStored := kWhStored - (DCkW + kWIdlingLosses) / DischargeEff * IntervalHrs;
if kWhStored < kWhReserve then
begin
@@ -3133,7 +2388,6 @@ procedure TStorage2Obj.UpdateStorage();
STORE_CHARGING:
begin
-
if (abs(DCkW) - kWIdlingLosses) >= 0 then // 99.9 % of the cases will fall here
begin
kWhStored := kWhStored + (abs(DCkW) - kWIdlingLosses) * ChargeEff * IntervalHrs;
@@ -3163,24 +2417,19 @@ procedure TStorage2Obj.UpdateStorage();
end;
- // the update is done at the end of a time step so have to force
- // a recalc of the Yprim for the next time step. Else it will stay the same.
+ // the update is done at the end of a time step so have to force
+ // a recalc of the Yprim for the next time step. Else it will stay the same.
if FstateChanged then
YprimInvalid := TRUE;
-
end;
-//----------------------------------------------------------------------------
-
procedure TStorage2Obj.ComputeDCkW;
// Computes actual DCkW to Update Storage SOC
var
-
coefGuess: TCoeff;
coef: TCoeff;
N_tentatives: Integer;
begin
-
coefGuess[1] := 0.0;
coefGuess[2] := 0.0;
@@ -3189,7 +2438,6 @@ procedure TStorage2Obj.ComputeDCkW;
FDCkW := Power[1].re * 0.001; // Assume ideal inverter
-
if not Assigned(InverterCurveObj) then
begin
// make sure sign is correct
@@ -3200,7 +2448,6 @@ procedure TStorage2Obj.ComputeDCkW;
Exit;
end;
-
N_tentatives := 0;
while (coef[1] <> coefGuess[1]) and (coef[2] <> coefGuess[2]) or (N_tentatives > 9) do
begin
@@ -3217,7 +2464,7 @@ procedure TStorage2Obj.ComputeDCkW;
FDCkW := abs(FDCkW) * coefGuess[2] / (1.0 - (coefGuess[1] * abs(FDCkW) / StorageVars.FkVArating));
end;
- // Final coefficients
+ // Final coefficients
coef := InverterCurveObj.GetCoefficients(abs(FDCkW) / StorageVars.FkVArating);
end;
@@ -3226,60 +2473,38 @@ procedure TStorage2Obj.ComputeDCkW;
FDCkW := abs(FDCkW) * -1
else
FDCkW := abs(FDCkW) * FState;
-
end;
-//----------------------------------------------------------------------------
-
function TStorage2Obj.Get_PresentkW: Double;
begin
Result := Pnominalperphase * 0.001 * Fnphases;
end;
-//----------------------------------------------------------------------------
-
function TStorage2Obj.Get_DCkW: Double;
begin
-
ComputeDCkW;
Result := FDCkW;
-
end;
-//----------------------------------------------------------------------------
-
function TStorage2Obj.Get_kWDesired: Double;
begin
-
- case FStateDesired of
+ case StateDesired of
STORE_CHARGING:
Result := -pctkWIn * StorageVars.kWRating / 100.0;
STORE_DISCHARGING:
Result := pctkWOut * StorageVars.kWRating / 100.0;
STORE_IDLING:
Result := 0.0;
+ else
+ Result := 0.0;
end;
-
-end;
-
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.Set_StateDesired(i: Integer);
-begin
-
- FStateDesired := i;
-
end;
-//-----------------------------------------------------------------------------
-
function TStorage2Obj.Get_kWTotalLosses: Double;
begin
Result := kWIdlingLosses + kWInverterLosses + kWChDchLosses;
end;
-//----------------------------------------------------------------------------
-
function TStorage2Obj.Get_InverterLosses: Double;
begin
Result := 0.0;
@@ -3298,11 +2523,8 @@ function TStorage2Obj.Get_InverterLosses: Double;
end;
end;
-//----------------------------------------------------------------------------
-
function TStorage2Obj.Get_kWIdlingLosses: Double;
begin
-
if (FState = STORE_IDLING) then
begin
Result := abs(DCkW); // For consistency keeping with voltage variations
@@ -3311,8 +2533,6 @@ function TStorage2Obj.Get_kWIdlingLosses: Double;
Result := Pidling;
end;
-//----------------------------------------------------------------------------
-
function TStorage2Obj.Get_kWChDchLosses: Double;
begin
Result := 0.0;
@@ -3336,8 +2556,6 @@ function TStorage2Obj.Get_kWChDchLosses: Double;
end;
end;
-//----------------------------------------------------------------------------
-
procedure TStorage2Obj.Update_EfficiencyFactor;
begin
with StorageVars do
@@ -3349,88 +2567,48 @@ procedure TStorage2Obj.Update_EfficiencyFactor;
end;
end;
-//----------------------------------------------------------------------------
-
function TStorage2Obj.Get_PresentkV: Double;
begin
Result := StorageVars.kVStorageBase;
end;
-//----------------------------------------------------------------------------
-
function TStorage2Obj.Get_Presentkvar: Double;
begin
Result := Qnominalperphase * 0.001 * Fnphases;
end;
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i, idx: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- idx := PropertyIdxMap[i];
- case idx of
- propUSERDATA:
- FSWriteln(F, '~ ' + PropertyName^[i] + '=(' + PropertyValue[idx] + ')');
- propDynaData:
- FSWriteln(F, '~ ' + PropertyName^[i] + '=(' + PropertyValue[idx] + ')');
- else
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[idx]);
- end;
- end;
-
- FSWriteln(F);
-end;
-
-
-//----------------------------------------------------------------------------
-
procedure TStorage2Obj.InitHarmonics();
-
// This routine makes a thevenin equivalent behis the reactance spec'd in %R and %X
-
var
E, Va: complex;
-
begin
YprimInvalid := TRUE; // Force rebuild of YPrims
StorageFundamental := ActiveCircuit.Solution.Frequency; // Whatever the frequency is when we enter here.
Yeq := Cinv(Cmplx(StorageVars.RThev, StorageVars.XThev)); // used for current calcs Always L-N
- {Compute reference Thevinen voltage from phase 1 current}
+ // Compute reference Thevinen voltage from phase 1 current
ComputeIterminal(); // Get present value of current
with ActiveCircuit.solution do
case Connection of
0:
- begin {wye - neutral is explicit}
- Va := Csub(NodeV^[NodeRef^[1]], NodeV^[NodeRef^[Fnconds]]);
+ begin // wye - neutral is explicit
+ Va := NodeV^[NodeRef^[1]] - NodeV^[NodeRef^[Fnconds]];
end;
1:
- begin {delta -- assume neutral is at zero}
+ begin // delta -- assume neutral is at zero
Va := NodeV^[NodeRef^[1]];
end;
end;
- E := Csub(Va, Cmul(Iterminal^[1], cmplx(StorageVars.Rthev, StorageVars.Xthev)));
+ E := Va - Iterminal^[1] * cmplx(StorageVars.Rthev, StorageVars.Xthev);
StorageVars.Vthevharm := Cabs(E); // establish base mag and angle
StorageVars.ThetaHarm := Cang(E);
end;
-
-//----------------------------------------------------------------------------
procedure TStorage2Obj.InitStateVars();
-
// for going into dynamics mode
var
// VNeut: Complex;
@@ -3439,10 +2617,7 @@ procedure TStorage2Obj.InitStateVars();
V012,
I012: array[0..2] of Complex;
Vabc: array[1..3] of Complex;
-
-
begin
-
YprimInvalid := TRUE; // Force rebuild of YPrims
with StorageVars do
@@ -3451,7 +2626,6 @@ procedure TStorage2Obj.InitStateVars();
Yeq := Cinv(ZThev); // used to init state vars
end;
-
if DynaModel.Exists then // Checks existence and selects
begin
ComputeIterminal();
@@ -3464,12 +2638,9 @@ procedure TStorage2Obj.InitStateVars();
end;
DynaModel.FInit(Vterminal, Iterminal);
end
-
else
begin
-
- {Compute nominal Positive sequence voltage behind equivalent filter impedance}
-
+ // Compute nominal Positive sequence voltage behind equivalent filter impedance
if FState = STORE_DISCHARGING then
with ActiveCircuit.Solution do
begin
@@ -3478,21 +2649,14 @@ procedure TStorage2Obj.InitStateVars();
if FnPhases = 3 then
begin
Phase2SymComp(ITerminal, pComplexArray(@I012));
- // Voltage behind Xdp (transient reactance), volts
-// case Connection of
-// 0:
-// Vneut := NodeV^[NodeRef^[Fnconds]]
-// else
-// Vneut := CZERO;
-// end;
-//
+
for i := 1 to FNphases do
Vabc[i] := NodeV^[NodeRef^[i]]; // Wye Voltage
Phase2SymComp(pComplexArray(@Vabc), pComplexArray(@V012));
with StorageVars do
begin
- Vthev := Csub(V012[1], Cmul(I012[1], ZThev)); // Pos sequence
+ Vthev := V012[1] - I012[1] * ZThev; // Pos sequence
VThevPolar := cToPolar(VThev);
VThevMag := VThevPolar.mag;
Theta := VThevPolar.ang; // Initial phase angle
@@ -3504,7 +2668,7 @@ procedure TStorage2Obj.InitStateVars();
Vabc[i] := NodeV^[NodeRef^[i]];
with StorageVars do
begin
- Vthev := Csub(VDiff(NodeRef^[1], NodeRef^[2]), Cmul(ITerminal^[1], ZThev)); // Pos sequence
+ Vthev := VDiff(NodeRef^[1], NodeRef^[2]) - ITerminal^[1] * ZThev; // Pos sequence
VThevPolar := cToPolar(VThev);
VThevMag := VThevPolar.mag;
Theta := VThevPolar.ang; // Initial phase angle
@@ -3513,53 +2677,20 @@ procedure TStorage2Obj.InitStateVars();
end;
end;
end;
-
end;
-//----------------------------------------------------------------------------
procedure TStorage2Obj.IntegrateStates();
-
// dynamics mode integration routine
-
-//var
-// TracePower: Complex;
-
begin
- // Compute Derivatives and Then integrate
-
+ // Compute Derivatives and Then integrate
ComputeIterminal();
if Dynamodel.Exists then // Checks for existence and Selects
-
DynaModel.Integrate
-
else
-
with ActiveCircuit.Solution, StorageVars do
begin
-
- with StorageVars do
- if (Dynavars.IterationFlag = 0) then
- begin {First iteration of new time step}
-//**** ThetaHistory := Theta + 0.5*h*dTheta;
-//**** SpeedHistory := Speed + 0.5*h*dSpeed;
- end;
-
- // Compute shaft dynamics
- // TracePower := TerminalPowerIn(Vterminal, Iterminal, FnPhases);
-
-//**** dSpeed := (Pshaft + TracePower.re - D*Speed) / Mmass;
-// dSpeed := (Torque + TerminalPowerIn(Vtemp,Itemp,FnPhases).re/Speed) / (Mmass);
-//**** dTheta := Speed ;
-
- // Trapezoidal method
- with StorageVars do
- begin
-//**** Speed := SpeedHistory + 0.5*h*dSpeed;
-//**** Theta := ThetaHistory + 0.5*h*dTheta;
- end;
-
- // Write Dynamics Trace Record
+ // Write Dynamics Trace Record
if DebugTrace then
begin
FSWrite(TraceFile, Format('t=%-.5g ', [Dynavars.t]));
@@ -3567,43 +2698,12 @@ procedure TStorage2Obj.IntegrateStates();
FSWriteln(TraceFile);
FSFlush(TraceFile);
end;
-
end;
-
end;
-
-//----------------------------------------------------------------------------
-function TStorage2Obj.InterpretState(const S: String): Integer;
-begin
- case LowerCase(S)[1] of
- 'c':
- Result := STORE_CHARGING;
- 'd':
- Result := STORE_DISCHARGING;
- else
- Result := STORE_IDLING;
- end;
-end;
-
-{ apparently for debugging only
-//----------------------------------------------------------------------------
-Function TStorage2Obj.StateToStr:String;
-Begin
- CASE FState of
- STORE_CHARGING: Result := 'Charging';
- STORE_IDLING: Result := 'Idling';
- STORE_DISCHARGING: Result := 'Discharging';
- END;
-End;
-}
-
-//----------------------------------------------------------------------------
function TStorage2Obj.Get_Variable(i: Integer): Double;
-{Return variables one at a time}
-
+// Return variables one at a time
var
N, k: Integer;
-
begin
Result := -9999.99; // error return value; no state vars
if i < 1 then
@@ -3630,11 +2730,11 @@ function TStorage2Obj.Get_Variable(i: Integer): Double;
6:
Result := DCkW;
7:
- Result := kWTotalLosses; {Present kW charge or discharge loss incl idle losses}
+ Result := kWTotalLosses; // Present kW charge or discharge loss incl idle losses
8:
- Result := kWInverterLosses; {Inverter Losses}
+ Result := kWInverterLosses; // Inverter Losses
9:
- Result := kWIdlingLosses; {Present kW Idling Loss}
+ Result := kWIdlingLosses; // Present kW Idling Loss
10:
Result := kWChDchLosses; // Charge/Discharge Losses
11:
@@ -3645,7 +2745,7 @@ function TStorage2Obj.Get_Variable(i: Integer): Double;
Result := EffFactor; //Old: Result := Get_EfficiencyFactor;
end;
13:
- if (FInverterON) then
+ if (InverterON) then
Result := 1.0
else
Result := 0.0;
@@ -3679,7 +2779,6 @@ function TStorage2Obj.Get_Variable(i: Integer): Double;
Result := 1.0
else
Result := 0.0;
-
else
begin
if UserModel.Exists then // Checks for existence and Selects
@@ -3706,11 +2805,9 @@ function TStorage2Obj.Get_Variable(i: Integer): Double;
end;
end;
-//----------------------------------------------------------------------------
procedure TStorage2Obj.Set_Variable(i: Integer; Value: Double);
var
N, k: Integer;
-
begin
if i < 1 then
Exit; // No variables to set
@@ -3721,7 +2818,7 @@ procedure TStorage2Obj.Set_Variable(i: Integer; Value: Double);
kWhStored := Value;
2:
Fstate := Trunc(Value);
- 3..13: ; {Do Nothing; read only}
+ 3..13: ; // Do Nothing; read only
14:
Vreg := Value;
15:
@@ -3738,59 +2835,53 @@ procedure TStorage2Obj.Set_Variable(i: Integer; Value: Double);
WPOperation := Value;
21:
WVOperation := Value;
- 22..25: ; {Do Nothing; read only}
+ 22..25: ; // Do Nothing; read only
else
- begin
- if UserModel.Exists then // Checks for existence and Selects
begin
- N := UserModel.FNumVars;
- k := (i - NumStorage2Variables);
- if k <= N then
+ if UserModel.Exists then // Checks for existence and Selects
begin
- UserModel.FSetVariable(k, Value);
- Exit;
+ N := UserModel.FNumVars;
+ k := (i - NumStorage2Variables);
+ if k <= N then
+ begin
+ UserModel.FSetVariable(k, Value);
+ Exit;
+ end;
end;
- end;
- if DynaModel.Exists then // Checks for existence and Selects
- begin
- N := DynaModel.FNumVars;
- k := (i - NumStorage2Variables);
- if k <= N then
+ if DynaModel.Exists then // Checks for existence and Selects
begin
- DynaModel.FSetVariable(k, Value);
- Exit;
+ N := DynaModel.FNumVars;
+ k := (i - NumStorage2Variables);
+ if k <= N then
+ begin
+ DynaModel.FSetVariable(k, Value);
+ Exit;
+ end;
end;
end;
end;
- end;
-
end;
-//----------------------------------------------------------------------------
procedure TStorage2Obj.GetAllVariables(States: pDoubleArray);
-
var
- i{, N}: Integer;
+ i: Integer;
begin
for i := 1 to NumStorage2Variables do
States^[i] := Variable[i];
if UserModel.Exists then
begin // Checks for existence and Selects
- {N := UserModel.FNumVars;}
+ // N := UserModel.FNumVars;
UserModel.FGetAllVars(pDoubleArray(@States^[NumStorage2Variables + 1]));
end;
if DynaModel.Exists then
begin // Checks for existence and Selects
- {N := UserModel.FNumVars;}
+ // N := UserModel.FNumVars;
DynaModel.FGetAllVars(pDoubleArray(@States^[NumStorage2Variables + 1]));
end;
-
end;
-//----------------------------------------------------------------------------
-
function TStorage2Obj.NumVariables: Integer;
begin
Result := NumStorage2Variables;
@@ -3802,10 +2893,7 @@ function TStorage2Obj.NumVariables: Integer;
Result := Result + DynaModel.FNumVars;
end;
-//----------------------------------------------------------------------------
-
function TStorage2Obj.VariableName(i: Integer): String;
-
const
BuffSize = 255;
var
@@ -3825,8 +2913,6 @@ function TStorage2Obj.VariableName(i: Integer): String;
Result := 'kWh';
2:
Result := 'State';
-// 3:Result := 'Pnom';
-// 4:Result := 'Qnom';
3:
Result := 'kWOut';
4:
@@ -3873,111 +2959,91 @@ function TStorage2Obj.VariableName(i: Integer): String;
Result := 'Limit kWOut Function';
25:
Result := 'kVA Exceeded';
-
else
- begin
- if UserModel.Exists then // Checks for existence and Selects
begin
- pName := PAnsiChar(@Buff);
- n := UserModel.FNumVars;
- i2 := i - NumStorage2Variables;
- if i2 <= n then
+ if UserModel.Exists then // Checks for existence and Selects
begin
- UserModel.FGetVarName(i2, pName, BuffSize);
- Result := String(pName);
- Exit;
+ pName := PAnsiChar(@Buff);
+ n := UserModel.FNumVars;
+ i2 := i - NumStorage2Variables;
+ if i2 <= n then
+ begin
+ UserModel.FGetVarName(i2, pName, BuffSize);
+ Result := String(pName);
+ Exit;
+ end;
end;
- end;
- if DynaModel.Exists then // Checks for existence and Selects
- begin
- pName := PAnsiChar(@Buff);
- n := DynaModel.FNumVars;
- i2 := i - NumStorage2Variables; // Relative index
- if i2 <= n then
+ if DynaModel.Exists then // Checks for existence and Selects
begin
- DynaModel.FGetVarName(i2, pName, BuffSize);
- Result := String(pName);
- Exit;
+ pName := PAnsiChar(@Buff);
+ n := DynaModel.FNumVars;
+ i2 := i - NumStorage2Variables; // Relative index
+ if i2 <= n then
+ begin
+ DynaModel.FGetVarName(i2, pName, BuffSize);
+ Result := String(pName);
+ Exit;
+ end;
end;
end;
end;
- end;
-
end;
-//----------------------------------------------------------------------------
-
procedure TStorage2Obj.MakePosSequence();
-
var
- S: String;
- V: Double;
-
+ newkW, newPF, V: Double;
+ oldPhases, changes: Integer;
begin
-
- S := 'Phases=1 conn=wye';
-
- // Make sure voltage is line-neutral
+ // Make sure voltage is line-neutral
if (Fnphases > 1) or (connection <> 0) then
V := StorageVars.kVStorageBase / SQRT3
else
V := StorageVars.kVStorageBase;
- S := S + Format(' kV=%-.5g', [V]);
-
- if Fnphases > 1 then
+ oldPhases := FnPhases;
+ changes := 3;
+ if oldPhases > 1 then
begin
- S := S + Format(' kWrating=%-.5g PF=%-.5g', [StorageVars.kWrating / Fnphases, PFNominal]);
+ newkW := StorageVars.kWrating / Fnphases;
+ newPF := PFNominal;
+ changes := changes + 2;
end;
-
- Parser.CmdString := S;
- Edit();
+ SetInteger(ord(TProp.Phases), 1);
+ SetInteger(ord(TProp.conn), 0);
+ SetDouble(ord(TProp.kV), V);
+ if oldPhases > 1 then
+ begin
+ SetDouble(ord(TProp.kWrated), newkW);
+ SetDouble(ord(TProp.PF), newPF);
+ end;
+ EndEdit(changes);
inherited; // write out other properties
end;
-//----------------------------------------------------------------------------
-
procedure TStorage2Obj.Set_ConductorClosed(Index: Integer; Value: Boolean);
begin
inherited;
- // Just turn Storage element on or off;
-
+ // Just turn Storage element on or off;
if Value then
Storage2ObjSwitchOpen := FALSE
else
Storage2ObjSwitchOpen := TRUE;
-
-end;
-
-//----------------------------------------------------------------------------
-
-function TStorage2Obj.Get_InverterON: Boolean;
-begin
- if FInverterON then
- Result := TRUE
- else
- Result := FALSE;
-
end;
-//----------------------------------------------------------------------------
-
procedure TStorage2Obj.kWOut_Calc;
var
limitkWpct: Double;
-
begin
with StorageVars do
begin
-
FVWStateRequested := FALSE;
if FState = STORE_DISCHARGING then
- limitkWpct := kWrating * FpctkWrated
+ limitkWpct := kWrating * pctkWrated
else
- limitkWpct := kWrating * FpctkWrated * -1;
+ limitkWpct := kWrating * pctkWrated * -1;
// if VWmode and (FState = STORE_DISCHARGING) then if (abs(kwRequested) < abs(limitkWpct)) then limitkWpct := kwRequested * sign(kW_Out);
// VW works only if element is not in idling state.
@@ -3985,7 +3051,6 @@ procedure TStorage2Obj.kWOut_Calc;
// When in 'requesting' region, it will be negative.
if VWmode and not (FState = STORE_IDLING) then
begin
-
if (kWRequested >= 0.0) and (abs(kwRequested) < abs(limitkWpct)) then // Apply VW limit
begin
if FState = STORE_DISCHARGING then
@@ -4029,12 +3094,10 @@ procedure TStorage2Obj.kWOut_Calc;
// Update limitkWpct because state might have been changed
if FState = STORE_DISCHARGING then
- limitkWpct := kWrating * FpctkWrated
+ limitkWpct := kWrating * pctkWrated
else
- limitkWpct := kWrating * FpctkWrated * -1;
-
+ limitkWpct := kWrating * pctkWrated * -1;
end;
-
end;
if (limitkWpct > 0) and (kW_Out > limitkWpct) then
@@ -4042,122 +3105,21 @@ procedure TStorage2Obj.kWOut_Calc;
else
if (limitkWpct < 0) and (kW_Out < limitkWpct) then
kW_Out := limitkWpct;
-
end;
end;
-//----------------------------------------------------------------------------
-
-function TStorage2Obj.Get_Varmode: Integer;
-begin
- Result := FvarMode;
-end;
-
-//----------------------------------------------------------------------------
-
-function TStorage2Obj.Get_VWmode: Boolean;
-begin
- if FVWmode then
- Result := TRUE
- else
- Result := FALSE; // TRUE if volt-watt mode
- // engaged from InvControl (not ExpControl)
-end;
-
-// ============================================================Get_WPmode===============================
-function TStorage2Obj.Get_WPmode: Boolean;
-begin
- if FWPmode then
- Result := TRUE
- else
- Result := FALSE; // engaged from InvControl (not ExpControl)
-end;
-
-// ============================================================Get_WVmode===============================
-function TStorage2Obj.Get_WVmode: Boolean;
-begin
- if FWVmode then
- Result := TRUE
- else
- Result := FALSE; // engaged from InvControl (not ExpControl)
-end;
-
-//----------------------------------------------------------------------------
-
-function TStorage2Obj.Get_VVmode: Boolean;
-begin
- if FVVmode then
- Result := TRUE
- else
- Result := FALSE;
-end;
-
-
-//----------------------------------------------------------------------------
-
-function TStorage2Obj.Get_DRCmode: Boolean;
-begin
- if FDRCmode then
- Result := TRUE
- else
- Result := FALSE;
-end;
-
-//----------------------------------------------------------------------------
-
-function TStorage2Obj.Get_CutOutkWAC: Double;
-begin
- Result := FCutOutkWAC;
-end;
-
-//----------------------------------------------------------------------------
-
-function TStorage2Obj.Get_CutInkWAC: Double;
-begin
- Result := FCutInkWAC;
-end;
-
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.Set_pctkWOut(const Value: Double);
-begin
- FpctkWOut := Value;
-end;
-
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.Set_pctkWIn(const Value: Double);
-begin
- FpctkWIn := Value;
-end;
-
-//----------------------------------------------------------------------------
-
-// ===========================================================================================
-procedure TStorage2Obj.Set_WVmode(const Value: Boolean);
-begin
- FWVmode := Value;
-end;
-
-
-// ===========================================================================================
-procedure TStorage2Obj.Set_WPmode(const Value: Boolean);
-begin
- FWPmode := Value;
-end;
-
procedure TStorage2Obj.Set_kW(const Value: Double);
begin
if Value > 0 then
begin
FState := STORE_DISCHARGING;
- FpctkWOut := Value / StorageVars.kWRating * 100.0;
+ pctkWOut := Value / StorageVars.kWRating * 100.0;
end
else
if Value < 0 then
begin
FState := STORE_CHARGING;
- FpctkWIn := abs(Value) / StorageVars.kWRating * 100.0;
+ pctkWIn := abs(Value) / StorageVars.kWRating * 100.0;
end
else
begin
@@ -4165,103 +3127,32 @@ procedure TStorage2Obj.Set_kW(const Value: Double);
end;
end;
-//----------------------------------------------------------------------------
-
procedure TStorage2Obj.Set_Maxkvar(const Value: Double);
begin
StorageVars.Fkvarlimit := Value;
- PropertyValue[propkvarLimit] := Format('%-g', [StorageVars.Fkvarlimit]);
+ SetAsNextSeq(ord(TProp.kvarMax));
end;
-//----------------------------------------------------------------------------
-
procedure TStorage2Obj.Set_Maxkvarneg(const Value: Double);
begin
StorageVars.Fkvarlimitneg := Value;
- PropertyValue[propkvarLimitneg] := Format('%-g', [StorageVars.Fkvarlimitneg]);
+ SetAsNextSeq(ord(TProp.kvarMaxAbs));
end;
-//----------------------------------------------------------------------------
-
procedure TStorage2Obj.Set_kVARating(const Value: Double);
begin
StorageVars.FkVARating := Value;
- PropertyValue[propKVA] := Format('%-g', [StorageVars.FkVArating]);
+ SetAsNextSeq(ord(TProp.kVA));
end;
-//----------------------------------------------------------------------------
-
procedure TStorage2Obj.Set_PowerFactor(const Value: Double);
begin
PFNominal := Value;
varMode := VARMODEPF;
end;
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.Set_Varmode(const Value: Integer);
-begin
- FvarMode := Value;
-end;
-
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.Set_VWmode(const Value: Boolean);
-begin
- FVWmode := Value;
-end;
-
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.Set_VVmode(const Value: Boolean);
-begin
- FVVmode := Value;
-end;
-
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.Set_DRCmode(const Value: Boolean);
-begin
- FDRCmode := Value;
-end;
-
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.Set_PresentkV(const Value: Double);
-begin
- StorageVars.kVStorageBase := Value;
- case FNphases of
- 2, 3:
- VBase := StorageVars.kVStorageBase * InvSQRT3x1000;
- else
- VBase := StorageVars.kVStorageBase * 1000.0;
- end;
-end;
-
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.Set_kvarRequested(const Value: Double);
-begin
- FkvarRequested := Value;
-end;
-
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.Set_pf_wp_nominal(const Value: Double);
-begin
- Fpf_wp_nominal := Value;
-end;
-
-procedure TStorage2Obj.Set_kWRequested(const Value: Double);
-begin
- FkWRequested := Value;
-end;
-
-//----------------------------------------------------------------------------
-
function TStorage2Obj.Get_kW: Double;
begin
-
case Fstate of
STORE_CHARGING:
Result := -pctkWIn * StorageVars.kWRating / 100.0;
@@ -4269,58 +3160,11 @@ function TStorage2Obj.Get_kW: Double;
Result := pctkWOut * StorageVars.kWRating / 100.0;
STORE_IDLING:
Result := -kWOutIdling;
- end;
-
-end;
-
-//----------------------------------------------------------------------------
-
-function TStorage2Obj.Get_kWRequested: Double;
-begin
- Result := FkWRequested;
-end;
-
-//----------------------------------------------------------------------------
-
-function TStorage2Obj.Get_kvarRequested: Double;
-begin
- Result := FkvarRequested;
-end;
-
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.Set_VarFollowInverter(const Value: Boolean);
-begin
- FVarFollowInverter := Value;
-end;
-
-//----------------------------------------------------------------------------
-
-function TStorage2Obj.Get_VarFollowInverter: Boolean;
-begin
- if FVarFollowInverter then
- Result := TRUE
else
- Result := FALSE;
-
-end;
-
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.Set_pctkWrated(const Value: Double);
-begin
- StorageVars.FpctkWrated := Value;
-end;
-
-//----------------------------------------------------------------------------
-
-procedure TStorage2Obj.Set_InverterON(const Value: Boolean);
-begin
- FInverterON := Value;
+ Result := 0.0;
+ end;
end;
-//----------------------------------------------------------------------------
-
procedure TStorage2Obj.Set_StorageState(const Value: Integer);
var
SavedState: Integer;
@@ -4356,7 +3200,6 @@ procedure TStorage2Obj.Set_StorageState(const Value: Integer);
//---DEBUG--- WriteDLLDebugFile(Format('t=%.8g, ---State Set To %s', [ActiveCircuit.Solution.dblHour, StateToStr ]));
end;
-//----------------------------------------------------------------------------
procedure TStorage2Obj.SetDragHandRegister(Reg: Integer; const Value: Double);
begin
@@ -4364,9 +3207,12 @@ procedure TStorage2Obj.SetDragHandRegister(Reg: Integer; const Value: Double);
Registers[Reg] := Value;
end;
-//----------------------------------------------------------------------------
-initialization
-
- CDOUBLEONE := CMPLX(1.0, 1.0);
+function TStorage2Obj.UsingCIMDynamics(): Boolean;
+begin
+ Result := VWMode or VVMode or WVMode or AVRMode or DRCMode; // FWPMode not in CIM Dynamics
+end;
+finalization
+ StateEnum.Free;
+ DispatchModeEnum.Free;
end.
diff --git a/src/PCElements/Storage2Vars.pas b/src/PCElements/Storage2Vars.pas
deleted file mode 100644
index 08b2acffd..000000000
--- a/src/PCElements/Storage2Vars.pas
+++ /dev/null
@@ -1,77 +0,0 @@
-unit Storage2Vars;
-
-{
- ----------------------------------------------------------
- Copyright (c) 2009-2015, Electric Power Research Institute, Inc.
- All rights reserved.
- ----------------------------------------------------------
-
- Definition of Storage Public Data Record for passing to DLLs and other object
-}
-
-interface
-
-uses
- Ucomplex;
-
-type
-
-{Struct to pass basic data to user-written DLLs}
- TStorage2Vars = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}packed{$ENDIF} record
-
- kWrating: Double;
- kWhRating: Double;
- kWhStored: Double;
- kWhReserve: Double;
-
- ChargeEff: Double;
- DisChargeEff: Double;
- kVStorageBase: Double;
- RThev: Double;
- XThev: Double;
-
- // Inverter Related Properties
- FkVArating: Double;
- Fkvarlimit: Double;
- Fkvarlimitneg: Double;
- P_Priority: Boolean;
- PF_Priority: Boolean;
- FpctkWrated: Double;
- EffFactor: Double;
-
-
- // Interaction with InvControl
- Vreg: Double;
- Vavg: Double;
- VVOperation: Double;
- VWOperation: Double;
- DRCOperation: Double;
- VVDRCOperation: Double;
- WPOperation: Double;
- WVOperation: Double;
-// kW_out_desired :Double;
-
-
- // Dynamics variables
- Vthev: Complex; {Thevenin equivalent voltage (complex) for dynamic model}
- ZThev: Complex;
- Vthevharm: Double; {Thevenin equivalent voltage mag and angle reference for Harmonic model}
- Thetaharm: Double; {Thevenin equivalent voltage mag and angle reference for Harmonic model}
- VthevMag: Double; {Thevenin equivalent voltage for dynamic model}
- Theta: Double; {Power angle between voltage and current}
- w_grid: Double; {Grid frequency}
- TotalLosses: Double;
- IdlingLosses: Double;
-
- {32-bit integers}
- NumPhases, {Number of phases}
- NumConductors, {Total Number of conductors (wye-connected will have 4)}
- Conn: Integer; // 0 = wye; 1 = Delta
-
-
- end;
-
-
-implementation
-
-end.
diff --git a/src/PCElements/StorageVars.pas b/src/PCElements/StorageVars.pas
deleted file mode 100644
index 0da9a1f6a..000000000
--- a/src/PCElements/StorageVars.pas
+++ /dev/null
@@ -1,55 +0,0 @@
-unit StorageVars;
-
-{
- ----------------------------------------------------------
- Copyright (c) 2009-2015, Electric Power Research Institute, Inc.
- All rights reserved.
- ----------------------------------------------------------
-
- Definition of Storage Public Data Record for passing to DLLs and other object
-}
-
-interface
-
-uses
- Ucomplex;
-
-type
-
-{Struct to pass basic data to user-written DLLs}
- TStorageVars = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}packed{$ENDIF} record
-
- kWrating: Double;
- kWhRating: Double;
- kWhStored: Double;
- kWhReserve: Double;
- ChargeEff: Double;
- DisChargeEff: Double;
- kVArating: Double;
- kVStorageBase: Double;
- kvarRequested: Double;
- RThev: Double;
- XThev: Double;
- // Dynamics variables
- Vthev: Complex; {Thevenin equivalent voltage (complex) for dynamic model}
- ZThev: Complex;
- Vthevharm: Double; {Thevenin equivalent voltage mag and angle reference for Harmonic model}
- Thetaharm: Double; {Thevenin equivalent voltage mag and angle reference for Harmonic model}
- VthevMag: Double; {Thevenin equivalent voltage for dynamic model}
- Theta: Double; {Power angle between voltage and current}
- w_grid: Double; {Grid frequency}
- TotalLosses: Double;
- IdlingLosses: Double;
-
- {32-bit integers}
- NumPhases, {Number of phases}
- NumConductors, {Total Number of conductors (wye-connected will have 4)}
- Conn: Integer; // 0 = wye; 1 = Delta
-
-
- end;
-
-
-implementation
-
-end.
diff --git a/src/PCElements/StoreUserModel.pas b/src/PCElements/StoreUserModel.pas
index 40e7642f1..66afafa44 100644
--- a/src/PCElements/StoreUserModel.pas
+++ b/src/PCElements/StoreUserModel.pas
@@ -1,6 +1,5 @@
unit StoreUserModel;
-{$M+}
{
----------------------------------------------------------
Copyright (c) 2009-2015, Electric Power Research Institute, Inc.
@@ -15,11 +14,10 @@
interface
-USES StorageVars, Dynamics, DSSCallBackRoutines, ucomplex, Arraydef, DSSClass;
+USES Dynamics, DSSCallBackRoutines, UComplex, DSSUcomplex, Arraydef, DSSClass;
TYPE
-
- // Interface for Dynamics-only user-written model
+ // Interface for Dynamics-only user-written model
TStoreDynaModel = Class (TObject)
private
FHandle : NativeUInt; // Handle to DLL containing user model
@@ -28,7 +26,7 @@ interface
FuncError : Boolean;
- {These functions should only be called by the object itself}
+ // These functions should only be called by the object itself
FNew: Function( Var DynaData:TDynamicsRec; Var CallBacks:TDSSCallBacks): Integer; Stdcall;// Make a new instance
FDelete: Procedure(var x:Integer); Stdcall; // deletes specified instance
FSelect: Function (var x:Integer):Integer; Stdcall; // Select active instance
@@ -47,7 +45,7 @@ interface
FIntegrate: Procedure; stdcall; // Integrates any state vars
FUpdateModel: Procedure; Stdcall; // Called when props of generator updated
- {Monitoring functions}
+ // Monitoring functions
FNumVars: Function:Integer;Stdcall;
FGetAllVars: Procedure(Vars:pDoubleArray);StdCall; // Get all vars
FGetVariable: Function(var I:Integer):Double;StdCall;// Get a particular var
@@ -65,9 +63,6 @@ interface
constructor Create(dssContext: TDSSContext);
destructor Destroy; override;
-
- Published
-
End;
@@ -92,8 +87,6 @@ TStoreUserModel = class(TObject)
procedure Set_Edit(const Value: String);
function Get_Exists: Boolean;
- protected
-
public
DSS: TDSSContext;
@@ -125,14 +118,11 @@ TStoreUserModel = class(TObject)
constructor Create(dssContext: TDSSContext);
destructor Destroy; override;
-
- published
-
end;
implementation
-Uses Storage, DSSGlobals, {$IFDEF FPC}dynlibs{$ELSE}Windows{$ENDIF}, Sysutils,
+Uses Storage, DSSGlobals, dynlibs, Sysutils,
DSSHelper;
{ TStoreUserModel }
@@ -141,7 +131,7 @@ function TStoreUserModel.CheckFuncError(Addr: Pointer; FuncName: String): Point
begin
If Addr=nil then
Begin
- DoSimpleMsg(DSS, 'Storage User Model DLL Does Not Have Required Function: ' + FuncName, 1569);
+ DoSimpleMsg(DSS, 'Storage User Model DLL Does Not Have Required Function: %s', [FuncName], 1569);
FuncError := True;
End;
Result := Addr;
@@ -153,7 +143,6 @@ constructor TStoreUserModel.Create(dssContext: TDSSContext);
FID := 0;
Fhandle := 0;
FName := '';
-
end;
destructor TStoreUserModel.Destroy;
@@ -165,7 +154,6 @@ destructor TStoreUserModel.Destroy;
FDelete(FID); // Clean up all memory associated with this instance
FreeLibrary(FHandle);
End;
-
end;
function TStoreUserModel.Get_Exists: Boolean;
@@ -198,7 +186,6 @@ procedure TStoreUserModel.Set_Edit(const Value: String);
procedure TStoreUserModel.Set_Name(const Value:String);
begin
-
{If Model already points to something, then free it}
IF FHandle <> 0 Then Begin
@@ -222,10 +209,9 @@ procedure TStoreUserModel.Set_Name(const Value:String);
End;
If FHandle = 0 Then
- DoSimpleMsg(DSS, 'Storage User Model ' + Value + ' Not Loaded. DSS Directory = '+DSSDirectory, 1570)
+ DoSimpleMsg(DSS, 'Storage User Model %s Not Loaded. DSS Directory = %s', [Value, DSSDirectory], 1570)
Else
- Begin
-
+ begin
FName := Value;
// Now set up all the procedure variables
@@ -267,7 +253,7 @@ function TStoreDynaModel.CheckFuncError(Addr: Pointer;
begin
If Addr=nil then
Begin
- DoSimpleMsg(DSS, 'Storage User Dynamic DLL Does Not Have Required Function: ' + FuncName, 1569);
+ DoSimpleMsg(DSS, 'Storage User Dynamic DLL Does Not Have Required Function: %s', [FuncName], 1569);
FuncError := True;
End;
Result := Addr;
@@ -279,13 +265,11 @@ constructor TStoreDynaModel.Create(dssContext: TDSSContext);
FID := 0;
Fhandle := 0;
FName := '';
-
end;
destructor TStoreDynaModel.Destroy;
begin
-
If FID <> 0 Then
Begin
FDelete(FID); // Clean up all memory associated with this instance
@@ -293,7 +277,6 @@ destructor TStoreDynaModel.Destroy;
End;
inherited;
-
end;
function TStoreDynaModel.Get_Exists: Boolean;
@@ -325,7 +308,6 @@ procedure TStoreDynaModel.Set_Edit(const Value: String);
procedure TStoreDynaModel.Set_Name(const Value:String);
begin
-
{If Model already points to something, then free it}
IF FHandle <> 0 Then Begin
@@ -349,10 +331,9 @@ procedure TStoreDynaModel.Set_Name(const Value:String);
End;
If FHandle = 0 Then
- DoSimpleMsg(DSS, 'Storage User-written Dynamics Model ' + Value + ' Not Loaded. DSS Directory = '+DSSDirectory, 1570)
+ DoSimpleMsg(DSS, 'Storage User-written Dynamics Model "%s" Not Loaded. DSS Directory = "%s"', [Value, DSSDirectory], 1570)
Else
- Begin
-
+ begin
FName := Value;
// Now set up all the procedure variables
@@ -383,5 +364,4 @@ procedure TStoreDynaModel.Set_Name(const Value:String);
End;
end;
-
end.
diff --git a/src/PCElements/UPFC.pas b/src/PCElements/UPFC.pas
index 6e10660e4..1560aab7d 100644
--- a/src/PCElements/UPFC.pas
+++ b/src/PCElements/UPFC.pas
@@ -7,11 +7,6 @@
----------------------------------------------------------
}
-{
- 7-6-2015 Created from VSOURCE
-
-}
-
interface
uses
@@ -20,28 +15,45 @@ interface
PCClass,
PCElement,
ucmatrix,
- ucomplex,
+ UComplex, DSSUcomplex,
Spectrum,
Arraydef,
Loadshape,
XYCurve;
type
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+{$SCOPEDENUMS ON}
+ TUPFCProp = (
+ INVALID = 0,
+ bus1 = 1,
+ bus2 = 2,
+ refkv = 3,
+ pf = 4,
+ frequency = 5,
+ phases = 6,
+ Xs = 7,
+ Tol1 = 8,
+ Mode = 9,
+ VpqMax = 10,
+ LossCurve = 11,
+ VHLimit = 12,
+ VLLimit = 13,
+ CLimit = 14,
+ refkv2 = 15,
+ kvarLimit = 16
+ );
+{$SCOPEDENUMS OFF}
TUPFC = class(TPCClass)
PROTECTED
- procedure DefineProperties;
- function MakeLike(const OtherSource: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TUPFCObj = class(TPCElement)
PRIVATE
VRef: Double; //Expected vooltage in the output (only magnitude)
@@ -75,7 +87,6 @@ TUPFCObj = class(TPCElement)
SyncFlag: Boolean; // Flag used to synchronize controllers in Dual mode
SF2: Boolean; // Flag used to Synch control modes 4 and 5
- LossCurve: String; //Losses curve name
UPFCLossCurveObj: TXYCurveObj; //Losses curve reference
function GetinputCurr(Cond: Integer): Complex;
@@ -85,12 +96,10 @@ TUPFCObj = class(TPCElement)
procedure GetInjCurrents(Curr: pComplexArray);
PROTECTED
-
function Get_Variable(i: Integer): Double; OVERRIDE;
procedure Set_Variable(i: Integer; Value: Double); OVERRIDE;
PUBLIC
-
InCurr, OutCurr: Array of Complex; // for storing the input and output currents
Z: TCmatrix; // Base Frequency Series Z matrix
@@ -99,6 +108,8 @@ TUPFCObj = class(TPCElement)
constructor Create(ParClass: TDSSClass; const SourceName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
@@ -110,11 +121,9 @@ TUPFCObj = class(TPCElement)
procedure UploadCurrents;
function CheckStatus: Boolean;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
function NumVariables: Integer; OVERRIDE;
procedure GetAllVariables(States: pDoubleArray); OVERRIDE;
@@ -123,12 +132,9 @@ TUPFCObj = class(TPCElement)
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
implementation
-
uses
- ParserDel,
Circuit,
UPFCControl,
DSSClassDefs,
@@ -143,275 +149,158 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TUPFCObj;
+ TProp = TUPFCProp;
const
- propLossCurve = 11;
- NumPropsThisClass = 16;
+ NumPropsThisClass = Ord(High(TProp));
NumUPFCVariables = 14;
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TUPFC.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TUPFC.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'UPFC';
- DSSClassType := PC_ELEMENT + UPFC_ELEMENT; // UPFC is PC Element
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- ActiveElement := 0;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, UPFC_ELEMENT, 'UPFC');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TUPFC.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TUPFC.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
- PropertyName[1] := 'bus1';
- PropertyName[2] := 'bus2';
- PropertyName[3] := 'refkv';
- PropertyName[4] := 'pf';
- PropertyName[5] := 'frequency';
- PropertyName[6] := 'phases';
- PropertyName[7] := 'Xs';
- PropertyName[8] := 'Tol1';
- PropertyName[9] := 'Mode';
- PropertyName[10] := 'VpqMax';
- PropertyName[11] := 'LossCurve';
- PropertyName[12] := 'VHLimit';
- PropertyName[13] := 'VLLimit';
- PropertyName[14] := 'CLimit';
- PropertyName[15] := 'refkv2';
- PropertyName[16] := 'kvarLimit';
-
- // define Property help values
- PropertyHelp[1] := 'Name of bus to which the input terminal (1) is connected.' + CRLF + 'bus1=busname.1.3' + CRLF + 'bus1=busname.1.2.3';
- ;
- PropertyHelp[2] := 'Name of bus to which the output terminal (2) is connected.' + CRLF + 'bus2=busname.1.2' + CRLF + 'bus2=busname.1.2.3';
- PropertyHelp[3] := 'Base Voltage expected at the output of the UPFC' + CRLF + CRLF +
- '"refkv=0.24"';
- PropertyHelp[4] := 'Power factor target at the input terminal.';
- PropertyHelp[5] := 'UPFC working frequency. Defaults to system default base frequency.';
- PropertyHelp[6] := 'Number of phases. Defaults to 1 phase (2 terminals, 1 conductor per terminal).';
- PropertyHelp[7] := 'Reactance of the series transformer of the UPFC, ohms (default=0.7540 ... 2 mH)';
- PropertyHelp[8] := 'Tolerance in pu for the series PI controller' + CRLF +
- 'Tol1=0.02 is the format used to define 2% tolerance (Default=2%)';
- PropertyHelp[9] := 'Integer used to define the control mode of the UPFC: '+ CRLF + CRLF +
- '0 = Off, ' + CRLF +
- '1 = Voltage regulator, ' + CRLF +
- '2 = Phase angle regulator, ' + CRLF +
- '3 = Dual mode' + CRLF +
- '4 = It is a control mode where the user can set two different set points to create a secure GAP,' +
- ' these references must be defined in the parameters RefkV and RefkV2. The only restriction when ' +
- 'setting these values is that RefkV must be higher than RefkV2. ' + CRLF +
- '5 = In this mode the user can define the same GAP using two set points as in control mode 4. The ' +
- 'only difference between mode 5 and mode 4 is that in mode 5, the UPFC controller performs dual control' +
- ' actions just as in control mode 3';
- PropertyHelp[10] := 'Maximum voltage (in volts) delivered by the series voltage source (Default = 24 V)';
- PropertyHelp[11] := 'Name of the XYCurve for describing the losses behavior as a function of the voltage at the input of the UPFC';
- PropertyHelp[12] := 'High limit for the voltage at the input of the UPFC, if the voltage is above this value the UPFC turns off. This value is specified in Volts (default 300 V)';
- PropertyHelp[13] := 'low limit for the voltage at the input of the UPFC, if voltage is below this value the UPFC turns off. This value is specified in Volts (default 125 V)';
- PropertyHelp[14] := 'Current Limit for the UPFC, if the current passing through the UPFC is higher than this value the UPFC turns off. This value is specified in Amps (Default 265 A)';
- PropertyHelp[15] := 'Base Voltage expected at the output of the UPFC for control modes 4 and 5.' + CRLF + CRLF +
- 'This reference must be lower than refkv, see control modes 4 and 5 for details';
- PropertyHelp[16] := 'Maximum amount of reactive power (kvar) that can be absorved by the UPFC (Default = 5)';
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- // Override help string
- PropertyHelp[NumPropsThisClass + 1] := 'Name of harmonic spectrum for this source. Default is "defaultUPFC", which is defined when the DSS starts.';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyType[ord(TProp.bus2)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+ PropertyOffset[ord(TProp.bus2)] := 2;
+
+ // object properties
+ PropertyType[ord(TProp.LossCurve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.LossCurve)] := ptruint(@obj.UPFCLossCurveObj);
+ PropertyOffset2[ord(TProp.LossCurve)] := ptruint(DSS.XYCurveClass);
+
+ // integer properties
+ PropertyType[ord(TProp.Mode)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Mode)] := ptruint(@obj.ModeUPFC);
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.refkv)] := ptruint(@obj.VRef);
+ PropertyOffset[ord(TProp.pf)] := ptruint(@obj.pf);
+ PropertyOffset[ord(TProp.frequency)] := ptruint(@obj.Freq);
+ PropertyOffset[ord(TProp.Xs)] := ptruint(@obj.Xs);
+ PropertyOffset[ord(TProp.Tol1)] := ptruint(@obj.Tol1);
+ PropertyOffset[ord(TProp.VpqMax)] := ptruint(@obj.VpqMax);
+ PropertyOffset[ord(TProp.VHLimit)] := ptruint(@obj.VHLimit);
+ PropertyOffset[ord(TProp.VLLimit)] := ptruint(@obj.VLLimit);
+ PropertyOffset[ord(TProp.CLimit)] := ptruint(@obj.CLimit);
+ PropertyOffset[ord(TProp.refkv2)] := ptruint(@obj.VRef2);
+ PropertyOffset[ord(TProp.kvarLimit)] := ptruint(@obj.kvarLim);
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
end;
+function TUPFC.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
+begin
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
+end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TUPFC.NewObject(const ObjName: String): Integer;
+procedure TUPFCObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- // Make a new voltage source and add it to UPFC class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TUPFCObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
+ case Idx of
+ ord(TProp.phases):
+ if Fnphases <> previousIntVal then
+ begin
+ NConds := Fnphases; // Force Reallocation of terminal info
+ SetLength(OutCurr, FNphases + 1);
+ SetLength(InCurr, FNphases + 1);
+ end;
end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TUPFC.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName,
- Param: String;
+function TUPFC.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
begin
- // continue parsing with contents of Parser
- DSS.ActiveUPFCObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveUPFCObj;
-
- Result := 0;
-
- with DSS.ActiveUPFCObj do
+ with TObj(ptr) do
begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "UPFC.' + Name + '"', 320);
- 1:
- SetBus(1, param); // special handling of Bus 1
- 2:
- SetBus(2, param); // special handling of Bus 2
- 3:
- VRef := Parser.DblValue; // kv Output reference
- 4:
- pf := Parser.DblValue; // power factor
- 5:
- Freq := Parser.DblValue; // Freq
- 6:
- begin
- Nphases := Parser.Intvalue; // num phases
- NConds := Fnphases; // Force Reallocation of terminal info
- SetLength(OutCurr, Nphases + 1);
- SetLength(InCurr, Nphases + 1);
- end;
- 7:
- Xs := Parser.DblValue; // Xs
- 8:
- Tol1 := Parser.DblValue; // Tolerance Ctrl 2
- 9:
- ModeUPFC := Parser.IntValue;
- 10:
- VpqMax := Parser.DblValue;
- propLossCurve:
- LossCurve := Param;
- 12:
- VHLimit := Parser.DblValue;
- 13:
- VLLimit := Parser.DblValue;
- 14:
- CLimit := Parser.DblValue;
- 15:
- VRef2 := Parser.DblValue;
- 16:
- kvarLim := Parser.DblValue;
-
- else
- ClassEdit(DSS.ActiveUPFCObj, ParamPointer - NumPropsThisClass)
- end;
-
- case ParamPointer of
- propLossCurve:
- UPFCLossCurveObj := DSS.XYCurveClass.Find(LossCurve);
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
-
RecalcElementData;
YPrimInvalid := TRUE;
+ Exclude(Flags, Flg.EditionActive);
end;
-
+ Result := True;
end;
-//----------------------------------------------------------------------------
-function TUPFC.MakeLike(const OtherSource: String): Integer;
+procedure TUPFCObj.MakeLike(OtherPtr: Pointer);
var
- OtherUPFC: TUPFCObj;
- i: Integer;
-
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this line name in the present collection}
- OtherUPFC := Find(OtherSource);
- if OtherUPFC <> NIL then
- with DSS.ActiveUPFCObj do
- begin
-
- if Fnphases <> OtherUPFC.Fnphases then
- begin
- Nphases := OtherUPFC.Fnphases;
- NConds := Fnphases; // Forces reallocation of terminal stuff
+ inherited MakeLike(OtherPtr);
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
+ Other := TObj(OtherPtr);
+ if Fnphases <> Other.Fnphases then
+ begin
+ FNphases := Other.Fnphases;
+ NConds := Fnphases; // Forces reallocation of terminal stuff
- if Z <> NIL then
- Z.Free;
- if Zinv <> NIL then
- Zinv.Free;
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
- Z := TCmatrix.CreateMatrix(Fnphases);
- Zinv := TCMatrix.CreateMatrix(Fnphases);
- end;
+ if Z <> NIL then
+ Z.Free;
+ if Zinv <> NIL then
+ Zinv.Free;
- Z.CopyFrom(OtherUPFC.Z);
- VRef := OtherUPFC.VRef;
- pf := OtherUPFC.pf;
- Xs := OtherUPFC.Xs;
- Tol1 := OtherUPFC.Tol1;
- ZBase := OtherUPFC.ZBase;
- Freq := OtherUPFC.Freq;
- ModeUPFC := OtherUPFC.ModeUPFC;
- VpqMax := OtherUPFC.VpqMax;
- LossCurve := OtherUPFC.LossCurve;
- UPFCLossCurveObj := UPFCLossCurveObj;
- VHLimit := OtherUPFC.VHLimit;
- VLLimit := OtherUPFC.VLLimit;
- CLimit := OtherUPFC.CLimit;
- VRef2 := OtherUPFC.VRef2;
- kvarLim := OtherUPFC.kvarLim;
-
- ClassMakeLike(OtherUPFC);
-
- for i := 1 to ParentClass.NumProperties do
- FPropertyValue[i] := OtherUPFC.FPropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in UPFC MakeLike: "' + OtherSource + '" Not Found.', 322);
+ Z := TCmatrix.CreateMatrix(Fnphases);
+ Zinv := TCMatrix.CreateMatrix(Fnphases);
+ end;
+ Z.CopyFrom(Other.Z);
+ VRef := Other.VRef;
+ pf := Other.pf;
+ Xs := Other.Xs;
+ Tol1 := Other.Tol1;
+ ZBase := Other.ZBase;
+ Freq := Other.Freq;
+ ModeUPFC := Other.ModeUPFC;
+ VpqMax := Other.VpqMax;
+ UPFCLossCurveObj := UPFCLossCurveObj;
+ VHLimit := Other.VHLimit;
+ VLLimit := Other.VLLimit;
+ CLimit := Other.CLimit;
+ VRef2 := Other.VRef2;
+ kvarLim := Other.kvarLim;
end;
-//=============================================================================
constructor TUPFCObj.Create(ParClass: TDSSClass; const SourceName: String);
var
i: Integer;
ctrl: TUPFCControlObj;
begin
inherited create(ParClass);
- Name := LowerCase(SourceName);
+ Name := AnsiLowerCase(SourceName);
DSSObjType := ParClass.DSSClassType; //SOURCE + NON_PCPD_ELEM; // Don't want this in PC Element List
- Nphases := 1;
+ FNphases := 1;
Fnconds := 1; // number conductors per terminal
Nterms := 2; // A 2-terminal device
@@ -425,7 +314,6 @@ constructor TUPFCObj.Create(ParClass: TDSSClass; const SourceName: String);
enabled := TRUE;
ModeUPFC := 1;
VpqMax := 24.0; // From the data provided
- LossCurve := '';
UPFCLossCurveObj := NIL;
VHLimit := 300.0;
VLLimit := 125.0;
@@ -441,38 +329,33 @@ constructor TUPFCObj.Create(ParClass: TDSSClass; const SourceName: String);
// Initialize shift registers
Reallocmem(SR0, SizeOf(Sr0^[1]) * Fnphases);
Reallocmem(SR1, SizeOf(Sr1^[1]) * Fnphases);
- for i := 1 to Nphases do
+ for i := 1 to FNphases do
Sr0^[i] := CZERO; //For multiphase model
- for i := 1 to Nphases do
+ for i := 1 to FNphases do
Sr1^[i] := CZERO; //For multiphase model
- for i := 1 to Nphases do
+ for i := 1 to FNphases do
ERR0[i] := 0; //For multiphase model
- InitPropertyValues(0);
-
- SetLength(OutCurr, Nphases + 1);
- SetLength(InCurr, Nphases + 1);
- for i := 0 to Nphases do
+ SetLength(OutCurr, FNphases + 1);
+ SetLength(InCurr, FNphases + 1);
+ for i := 0 to FNphases do
begin
OutCurr[i] := CZERO; //For multiphase model
InCurr[i] := CZERO; //For multiphase model
end;
// If there is a controller, sets the flag for it to consider de new UPFC
- if DSS.UPFCControlClass.ElementCount > 0 then
+ if ParentClass.ElementCount > 0 then
begin
- ctrl := DSS.UPFCControlClass.ElementList.Get(1);
+ ctrl := ParentClass.ElementList.Get(1);
ctrl.UPFCList.Clear;
ctrl.ListSize := 0;
end;
Yorder := Fnterms * Fnconds;
RecalcElementData;
-
end;
-
-//=============================================================================
destructor TUPFCObj.Destroy;
begin
Z.Free;
@@ -484,14 +367,11 @@ destructor TUPFCObj.Destroy;
inherited Destroy;
end;
-//=============================================================================
procedure TUPFCObj.RecalcElementData;
var
Z1: Complex;
Value: Complex;
i: Integer;
-
-
begin
if Z <> NIL then
Z.Free;
@@ -504,8 +384,8 @@ procedure TUPFCObj.RecalcElementData;
Qideal := 0.0;
- {Update property Value array}
- { Don't change a specified value; only computed ones}
+ // Update property Value array
+ // Don't change a specified value; only computed ones
Z1 := Cmplx(0, Xs);
// Diagonals (all the same)
@@ -517,11 +397,9 @@ procedure TUPFCObj.RecalcElementData;
Reallocmem(SR1, SizeOf(Sr1^[1]) * Fnphases);
Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
-
end;
-//=============================================================================
procedure TUPFCObj.CalcYPrim;
var
@@ -530,9 +408,8 @@ procedure TUPFCObj.CalcYPrim;
FreqMultiplier: Double;
begin
-
-// Calc UPFC Losses
- // Build only YPrim Series
+ // Calc UPFC Losses
+ // Build only YPrim Series
if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Series = NIL) {YPrimInvalid} then
begin
if YPrim_Series <> NIL then
@@ -551,22 +428,23 @@ procedure TUPFCObj.CalcYPrim;
FYprimFreq := ActiveCircuit.Solution.Frequency;
FreqMultiplier := FYprimFreq / BaseFrequency;
- { Put in Series RL Adjusted for frequency }
+ // Put in Series RL Adjusted for frequency
for i := 1 to Fnphases do
begin
for j := 1 to Fnphases do
begin
Value := Z.GetElement(i, j);
- Value.im := Value.im * FreqMultiplier; {Modify from base freq}
+ Value.im := Value.im * FreqMultiplier; // Modify from base freq
Zinv.SetElement(i, j, value);
end;
end;
- Zinv.Invert; {Invert in place}
+ Zinv.Invert; // Invert in place
if Zinv.InvertError > 0 then
- begin {If error, put in Large series conductance}
- DoErrorMsg('TUPFCObj.CalcYPrim', 'Matrix Inversion Error for UPFC "' + Name + '"',
- 'Invalid impedance specified. Replaced with small resistance.', 325);
+ begin // If error, put in Large series conductance
+ DoErrorMsg('TUPFCObj.CalcYPrim',
+ Format(_('Matrix Inversion Error for UPFC "%s"'), [Name]),
+ _('Invalid impedance specified. Replaced with small resistance.'), 325);
Zinv.Clear;
for i := 1 to Fnphases do
Zinv.SetElement(i, i, Cmplx(1.0 / EPSILON, 0.0));
@@ -581,66 +459,50 @@ procedure TUPFCObj.CalcYPrim;
Value := Zinv.GetElement(i, j);
YPrim_series.SetElement(i, j, Value);
YPrim_series.SetElement(i + FNPhases, j + FNPhases, Value);
- //YPrim_series.SetElemsym(i + FNPhases, j, CNegate(Value))
- YPrim_series.SetElement(i, j + Fnphases, Cnegate(Value));
- YPrim_series.SetElement(i + Fnphases, j, Cnegate(Value));
+ //YPrim_series.SetElemsym(i + FNPhases, j, -Value)
+ YPrim_series.SetElement(i, j + Fnphases, -Value);
+ YPrim_series.SetElement(i + Fnphases, j, -Value);
end;
end;
YPrim.CopyFrom(YPrim_Series);
- {Now Account for Open Conductors}
- {For any conductor that is open, zero out row and column}
+ // Now Account for Open Conductors
+ // For any conductor that is open, zero out row and column
inherited CalcYPrim;
YPrimInvalid := FALSE;
-
end;
-//=============================================================================
-
function TUPFCObj.CalcUPFCLosses(Vpu: Double): Double;
begin
-
-// Calculates the Active power losses at the input of the device
-// By using the Load powers, the approach is based in the data provided
-
+ // Calculates the Active power losses at the input of the device
+ // By using the Load powers, the approach is based in the data provided
Result := UPFCLossCurveObj.GetYValue(Vpu);
end;
-
-//===========================================================================
-
function TUPFCObj.InjCurrents: Integer;
-
begin
-
GetInjCurrents(InjCurrent);
-
-{This is source injection}
-
+ // This is source injection
Result := inherited InjCurrents; // Add into system array
-
end;
-//===========================================================================
//Taken from ISources due to the kind of model
-//===========================================================================
+
//Calculates the output current for the UPFC device
-{
- Vbin Xs Vbout
- <---*--=======--*--->
- | |
- I input ^ ^ I output
- | |
-
- 4 modes of operation:
- mode 0: UPFC Off
- mode 1: UPFC in voltage regulation mode
- mode 2: UPFC in reactive power compensation mode
- mode 3: Mode 1 and 2 working together
-}
+// Vbin Xs Vbout
+// <---*--=======--*--->
+// | |
+// I input ^ ^ I output
+// | |
+//
+// 4 modes of operation:
+// mode 0: UPFC Off
+// mode 1: UPFC in voltage regulation mode
+// mode 2: UPFC in reactive power compensation mode
+// mode 3: Mode 1 and 2 working together
function TUPFCObj.GetoutputCurr(Cond: Integer): Complex;
var
@@ -655,7 +517,6 @@ function TUPFCObj.GetoutputCurr(Cond: Integer): Complex;
begin
-
try
with ActiveCircuit.Solution do
UPFCON := TRUE;
@@ -676,7 +537,7 @@ function TUPFCObj.GetoutputCurr(Cond: Integer): Complex;
Error := abs(1 - abs(Vpolar.mag / (VRef * 1000)));
if Error > Tol1 then
begin
- Vtemp := csub(Vbout, Vbin);
+ Vtemp := Vbout - Vbin;
Vpolar := ctopolar(Vbin);
TError := (VRef * 1000) - Vpolar.mag;
if TError > VpqMax then
@@ -685,8 +546,8 @@ function TUPFCObj.GetoutputCurr(Cond: Integer): Complex;
if TError < -VpqMax then
TError := -VpqMax;
Vpolar := topolar(TError, Vpolar.ang);
- VTemp := csub(ptocomplex(Vpolar), VTemp); //Calculates Vpq
- CurrOut := cadd(SR0^[Cond], cdiv(VTemp, cmplx(0, Xs)));
+ VTemp := ptocomplex(Vpolar) - VTemp; //Calculates Vpq
+ CurrOut := SR0^[Cond] + VTemp / cmplx(0, Xs);
SR0^[Cond] := CurrOut;
end
else
@@ -702,7 +563,7 @@ function TUPFCObj.GetoutputCurr(Cond: Integer): Complex;
Error := abs(1 - abs(Vpolar.mag / (VRef * 1000)));
if Error > Tol1 then
begin
- Vtemp := csub(Vbout, Vbin);
+ Vtemp := Vbout - Vbin;
Vpolar := ctopolar(Vbin);
TError := (VRef * 1000) - Vpolar.mag;
if TError > VpqMax then
@@ -711,8 +572,8 @@ function TUPFCObj.GetoutputCurr(Cond: Integer): Complex;
if TError < -VpqMax then
TError := -VpqMax;
Vpolar := topolar(TError, Vpolar.ang);
- VTemp := csub(ptocomplex(Vpolar), VTemp); //Calculates Vpq
- CurrOut := cadd(SR0^[Cond], cdiv(VTemp, cmplx(0, Xs)));
+ VTemp := ptocomplex(Vpolar) - VTemp; //Calculates Vpq
+ CurrOut := SR0^[Cond] + VTemp / cmplx(0, Xs);
SR0^[Cond] := CurrOut;
SyncFlag := FALSE;
end
@@ -725,23 +586,23 @@ function TUPFCObj.GetoutputCurr(Cond: Integer): Complex;
4:
begin // Double reference control mode (only voltage control)
Vpolar := ctopolar(Vbin); // Takes the input voltage to verify the operation
- // Verifies if the Voltage at the input is out of the gap defined with VRef and VRef2
+ // Verifies if the Voltage at the input is out of the gap defined with VRef and VRef2
RefH := (VRef * 1000) + (VRef * 1000 * Tol1);
RefL := (VRef2 * 1000) - (VRef2 * 1000 * Tol1);
if (Vpolar.mag > RefH) or (Vpolar.mag < RefL) then
begin
- // Sets the New reference by considering the value at the input of the device
+ // Sets the New reference by considering the value at the input of the device
if (Vpolar.mag > RefH) then
VRefD := VRef
else
if (Vpolar.mag < RefL) then
VRefD := VRef2;
- // Starts the control routine for voltage control only
+ // Starts the control routine for voltage control only
Vpolar := ctopolar(Vbout);
Error := abs(1 - abs(Vpolar.mag / (VRefD * 1000)));
if Error > Tol1 then
begin
- Vtemp := csub(Vbout, Vbin);
+ Vtemp := Vbout - Vbin;
Vpolar := ctopolar(Vbin);
TError := (VRefD * 1000) - Vpolar.mag;
if TError > VpqMax then
@@ -750,8 +611,8 @@ function TUPFCObj.GetoutputCurr(Cond: Integer): Complex;
if TError < -VpqMax then
TError := -VpqMax;
Vpolar := topolar(TError, Vpolar.ang);
- VTemp := csub(ptocomplex(Vpolar), VTemp); //Calculates Vpq
- CurrOut := cadd(SR0^[Cond], cdiv(VTemp, cmplx(0, Xs)));
+ VTemp := ptocomplex(Vpolar) - VTemp; //Calculates Vpq
+ CurrOut := SR0^[Cond] + VTemp / cmplx(0, Xs);
SR0^[Cond] := CurrOut;
end
else
@@ -770,23 +631,23 @@ function TUPFCObj.GetoutputCurr(Cond: Integer): Complex;
5:
begin // Double reference control mode (Dual mode)
Vpolar := ctopolar(Vbin); // Takes the input voltage to verify the operation
- // Verifies if the Voltage at the input is out of the gap defined with VRef and VRef2
+ // Verifies if the Voltage at the input is out of the gap defined with VRef and VRef2
RefH := (VRef * 1000) + (VRef * 1000 * Tol1);
RefL := (VRef2 * 1000) - (VRef2 * 1000 * Tol1);
if (Vpolar.mag > RefH) or (Vpolar.mag < RefL) then
begin
- // Sets the New reference by considering the value at the input of the device
+ // Sets the New reference by considering the value at the input of the device
if (Vpolar.mag > RefH) then
VRefD := VRef
else
if (Vpolar.mag < RefL) then
VRefD := VRef2;
- // Starts standard control (the same as Dual control mode)
+ // Starts standard control (the same as Dual control mode)
Vpolar := ctopolar(Vbout);
Error := abs(1 - abs(Vpolar.mag / (VRefD * 1000)));
if Error > Tol1 then
begin
- Vtemp := csub(Vbout, Vbin);
+ Vtemp := Vbout - Vbin;
Vpolar := ctopolar(Vbin);
TError := (VRefD * 1000) - Vpolar.mag;
if TError > VpqMax then
@@ -795,8 +656,8 @@ function TUPFCObj.GetoutputCurr(Cond: Integer): Complex;
if TError < -VpqMax then
TError := -VpqMax;
Vpolar := topolar(TError, Vpolar.ang);
- VTemp := csub(ptocomplex(Vpolar), VTemp); //Calculates Vpq
- CurrOut := cadd(SR0^[Cond], cdiv(VTemp, cmplx(0, Xs)));
+ VTemp := ptocomplex(Vpolar) - VTemp; //Calculates Vpq
+ CurrOut := SR0^[Cond] + VTemp / cmplx(0, Xs);
SR0^[Cond] := CurrOut;
SyncFlag := FALSE;
end
@@ -816,56 +677,49 @@ function TUPFCObj.GetoutputCurr(Cond: Integer): Complex;
end;
end
else
- DoSimpleMsg('Control mode not regognized for UPFC', 790);
+ DoSimpleMsg(_('Control mode not recognized for UPFC'), 790);
end;
end;
Result := CurrOut;
except
- DoSimpleMsg('Error computing current for Isource.' + Name + '. Check specification. Aborting.', 334);
+ DoSimpleMsg('Error computing current for "%s". Check specification. Aborting.', [FullName], 334);
if DSS.In_Redirect then
DSS.Redirect_Abort := TRUE;
end;
end;
-//============================================================================
function TUPFCObj.CalcUPFCPowers(ModeUP, Cond: Integer): Complex;
begin
case ModeUP of
1:
begin //Dual mode
- IUPFC := cdiv(csub(Vbout, Vbin), cmplx(0, Xs));
-// SOut=cmul(Vbout,conjg(cadd(IUPFC,SR0[Cond]))) // Just if you want to know the power at the output
- Result := cnegate(cmul(Vbin, conjg(cadd(IUPFC, SR1^[Cond]))));
+ IUPFC := (Vbout - Vbin) / cmplx(0, Xs);
+// SOut=cmul(Vbout,cong(cadd(IUPFC,SR0[Cond]))) // Just if you want to know the power at the output
+ Result := -Vbin * cong(IUPFC + SR1^[Cond]);
end;
2:
begin //StatCOM
- IUPFC := cdiv(csub(Vbin, Vbout), cmplx(0, Xs));
- Result := cmul(Vbin, conjg(IUPFC));
+ IUPFC := (Vbin - Vbout) / cmplx(0, Xs);
+ Result := Vbin * cong(IUPFC);
end;
end;
end;
-
-//============================================================================
//Calculates the input current to absorb reactive power from UPFC
-{
- Vbin Xs Vbout
- <---*--=======--*--->
- | |
- I input ^ ^ I output
- | |
-}
-
+//
+// Vbin Xs Vbout
+// <---*--=======--*--->
+// | |
+// I input ^ ^ I output
+// | |
function TUPFCObj.GetinputCurr(Cond: Integer): Complex;
var
- CurrIn{, Ctemp}: complex;
+ CurrIn: complex;
S: Double;
-
begin
-
try
with ActiveCircuit.Solution do
- {Get first Phase Current}
+ // Get first Phase Current
if UPFCON then
begin
case ModeUPFC of
@@ -877,10 +731,10 @@ function TUPFCObj.GetinputCurr(Cond: Integer): Complex;
1:
begin // Voltage regulation mode
CurrIn := CZERO;
- // Ctemp := conjg(cmul(cdiv(Vbout, Vbin), conjg(SR0^[Cond]))); //Balancing powers
+ // Ctemp := cong(cmul(cdiv(Vbout, Vbin), cong(SR0^[Cond]))); //Balancing powers
Losses := CalcUPFCLosses(Cabs(Vbin) / (VRef * 1000));
- // CurrIn := cnegate(cmplx((Ctemp.re * Losses), SR0^[Cond].im));
- CurrIn := cnegate(cmplx(Losses * SR0^[Cond].re, SR0^[Cond].im));
+ // CurrIn := -cmplx((Ctemp.re * Losses), SR0^[Cond].im);
+ CurrIn := -cmplx(Losses * SR0^[Cond].re, SR0^[Cond].im);
SR1^[Cond] := CurrIn;
end;
2:
@@ -890,15 +744,15 @@ function TUPFCObj.GetinputCurr(Cond: Integer): Complex;
QIdeal := UPFC_Power.im - sqrt(1 - pf * pf) * S; //This is the expected compensating reactive power
if (QIdeal > (kvarLim * 1000)) then
QIdeal := kvarLim * 1000;
- CurrIn := conjg(cdiv(cmplx(0, QIdeal), Vbin)); //Q in terms of current *** conjg
+ CurrIn := cong(cmplx(0, QIdeal) / Vbin); //Q in terms of current *** cong
end;
3:
begin // Dual mode
CurrIn := CZERO;
- // Ctemp := conjg(cmul(cdiv(Vbout, Vbin), conjg(SR0^[Cond]))); //Balancing powers
+ // Ctemp := cong(cmul(cdiv(Vbout, Vbin), cong(SR0^[Cond]))); //Balancing powers
Losses := CalcUPFCLosses(Cabs(Vbin) / (VRef * 1000));
- // CurrIn := cnegate(cmplx((Ctemp.re * Losses), SR0^[Cond].im));
- CurrIn := cnegate(cmplx(Losses * SR0^[Cond].re, SR0^[Cond].im));
+ // CurrIn := -cmplx((Ctemp.re * Losses), SR0^[Cond].im);
+ CurrIn := -cmplx(Losses * SR0^[Cond].re, SR0^[Cond].im);
SR1^[Cond] := CurrIn;
if SyncFlag then
begin
@@ -908,7 +762,7 @@ function TUPFCObj.GetinputCurr(Cond: Integer): Complex;
QIdeal := UPFC_Power.im - sqrt(1 - pf * pf) * S; //This is the expected compensating reactive power
if (QIdeal > (kvarLim * 1000)) then
QIdeal := kvarLim * 1000;
- CurrIn := cadd(conjg(cdiv(cmplx(0, QIdeal), Vbin)), SR1^[Cond]); //Q in terms of current *** conjg
+ CurrIn := cong(cmplx(0, QIdeal) / Vbin) + SR1^[Cond]; //Q in terms of current *** cong
// This partial result is added to the one obtained previously to balance the control loop
end;
end;
@@ -917,10 +771,10 @@ function TUPFCObj.GetinputCurr(Cond: Integer): Complex;
if SF2 then
begin // Normal control routine considering the dynamic reference
CurrIn := CZERO;
- // Ctemp := conjg(cmul(cdiv(Vbout, Vbin), conjg(SR0^[Cond]))); //Balancing powers
+ // Ctemp := cong(cmul(cdiv(Vbout, Vbin), cong(SR0^[Cond]))); //Balancing powers
Losses := CalcUPFCLosses(Cabs(Vbin) / (VRefD * 1000));
- // CurrIn := cnegate(cmplx((Ctemp.re * Losses), SR0^[Cond].im));
- CurrIn := cnegate(cmplx(Losses * SR0^[Cond].re, SR0^[Cond].im));
+ // CurrIn := -cmplx((Ctemp.re * Losses), SR0^[Cond].im);
+ CurrIn := -cmplx(Losses * SR0^[Cond].re, SR0^[Cond].im);
SR1^[Cond] := CurrIn;
end
else
@@ -935,10 +789,10 @@ function TUPFCObj.GetinputCurr(Cond: Integer): Complex;
if SF2 then
begin
CurrIn := CZERO;
- // Ctemp := conjg(cmul(cdiv(Vbout, Vbin), conjg(SR0^[Cond]))); //Balancing powers
+ // Ctemp := cong(cmul(cdiv(Vbout, Vbin), cong(SR0^[Cond]))); //Balancing powers
Losses := CalcUPFCLosses(Cabs(Vbin) / (VRefD * 1000));
- // CurrIn := cnegate(cmplx((Ctemp.re * Losses), SR0^[Cond].im));
- CurrIn := cnegate(cmplx(Losses * SR0^[Cond].re, SR0^[Cond].im));
+ // CurrIn := -cmplx((Ctemp.re * Losses), SR0^[Cond].im);
+ CurrIn := -cmplx(Losses * SR0^[Cond].re, SR0^[Cond].im);
SR1^[Cond] := CurrIn;
end
else
@@ -947,17 +801,17 @@ function TUPFCObj.GetinputCurr(Cond: Integer): Complex;
SR1^[Cond] := CurrIn;
UPFC_Power := CZERO;
end;
- //Always corrects PF
+ //Always corrects PF
if SyncFlag then
begin
- // Starts Power Calculations to compensate the reactive power
+ // Starts Power Calculations to compensate the reactive power
UPFC_Power := CalcUPFCPowers(1, Cond);
S := abs(UPFC_Power.re) / pf;
QIdeal := UPFC_Power.im - sqrt(1 - pf * pf) * S; //This is the expected compensating reactive power
if (QIdeal > (kvarLim * 1000)) then
QIdeal := kvarLim * 1000;
- CurrIn := cadd(conjg(cdiv(cmplx(0, QIdeal), Vbin)), SR1^[Cond]); //Q in terms of current *** conjg
- // This partial result is added to the one obtained previously to balance the control loop
+ CurrIn := cong(cmplx(0, QIdeal) / Vbin) + SR1^[Cond]; //Q in terms of current *** cong
+ // This partial result is added to the one obtained previously to balance the control loop
end;
end;
end;
@@ -966,21 +820,17 @@ function TUPFCObj.GetinputCurr(Cond: Integer): Complex;
CurrIn := cmplx(0, 0);
Result := CurrIn;
except
- DoSimpleMsg('Error computing current for Isource.' + Name + '. Check specification. Aborting.', 334);
+ DoSimpleMsg('Error computing current for "%s". Check specification. Aborting.', [FullName], 334);
if DSS.In_Redirect then
DSS.Redirect_Abort := TRUE;
end;
-
end;
-//===========================================================================
-procedure TUPFCObj.GetInjCurrents(Curr: pComplexArray);
-
-{Fill Up an array of injection currents}
+procedure TUPFCObj.GetInjCurrents(Curr: pComplexArray);
+// Fill Up an array of injection currents
var
i: Integer;
begin
-
with ActiveCircuit.solution do
begin
for i := 1 to fnphases do
@@ -992,14 +842,11 @@ procedure TUPFCObj.GetInjCurrents(Curr: pComplexArray);
// (Different from VSource)
Curr^[i + fnphases] := OutCurr[i];
Curr^[i] := InCurr[i];
-
end;
end;
end;
-//===========================================================================
-//| Checks if the UPFC control needs an update, returns true if so |
-//===========================================================================
+// Checks if the UPFC control needs an update, returns true if so
function TUPFCObj.CheckStatus: Boolean;
var
Error,
@@ -1090,10 +937,7 @@ function TUPFCObj.CheckStatus: Boolean;
end;
end;
-
-//===========================================================================
-//| Uploads the calculated currents into memeory for further use |
-//===========================================================================
+// Uploads the calculated currents into memeory for further use
procedure TUPFCObj.UploadCurrents;
var
i: Integer;
@@ -1105,42 +949,33 @@ procedure TUPFCObj.UploadCurrents;
end;
end;
-//===========================================================================
-
procedure TUPFCObj.GetCurrents(Curr: pComplexArray);
-
var
i: Integer;
-
begin
try
- with ActiveCircuit.Solution do
+ with ActiveCircuit.Solution do
begin
ComputeVTerminal;
YPrim.MVMult(Curr, Vterminal); // Current from Elements in System Y
GetInjCurrents(ComplexBuffer); // Get present value of inj currents
-// Add Together with yprim currents
+ // Add Together with yprim currents
for i := 1 to Yorder do
- Curr^[i] := Csub(Curr^[i], ComplexBuffer^[i]);
- end; {With}
+ Curr^[i] := Curr^[i] - ComplexBuffer^[i];
+ end;
except
On E: Exception do
- DoErrorMsg(('GetCurrents for Element: ' + Name + '.'), E.Message,
- 'Inadequate storage allotted for circuit element.', 327);
+ DoErrorMsg(Format(_('GetCurrents for Element: %s.'), [FullName]), E.Message,
+ _('Inadequate storage allotted for circuit element.'), 327);
end;
-
end;
-
-//=============================================================================
-procedure TUPFCObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
+procedure TUPFCObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i, j: Integer;
c: Complex;
-
begin
inherited DumpProperties(F, Complete);
@@ -1166,88 +1001,15 @@ procedure TUPFCObj.DumpProperties(F: TFileStream; Complete: Boolean);
FSWriteln(F);
end;
end;
-
end;
-
-//=============================================================================
-procedure TUPFCObj.InitPropertyValues(ArrayOffset: Integer);
+procedure TUPFCObj.MakePosSequence();
begin
-
- {PropertyValue Allocated in DSSObject.Create}
- PropertyValue[1] := GetBus(1);
- PropertyValue[2] := GetBus(2);
- PropertyValue[3] := '0.24';
- PropertyValue[4] := '1';
- PropertyValue[5] := Format('%d', [Round(ActiveCircuit.Fundamental)]);
- PropertyValue[6] := '3';
- PropertyValue[7] := '0.7540'; // 2mH inductance
- PropertyValue[8] := '0.02';
- PropertyValue[9] := '1';
- PropertyValue[10] := '24';
- PropertyValue[11] := '';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
end;
-//=============================================================================
-function TUPFCObj.GetPropertyValue(Index: Integer): String;
-begin
- case Index of
- 1:
- Result := GetBus(1);
- 2:
- Result := GetBus(2);
- 3:
- Result := Format('%-.5g', [VRef]);
- 4:
- Result := Format('%-.5g', [pf]);
- 5:
- Result := Format('%-.5g', [Freq]);
- 7:
- Result := Format('%-.5g', [Xs]);
- 8:
- Result := Format('%-.5g', [Tol1]);
- 9:
- Result := Format('%d', [ModeUPFC]);
- 10:
- Result := Format('%-.5g', [VpqMax]);
- propLossCurve:
- Result := LossCurve;
-
- else
- Result := inherited GetPropertyValue(Index);
- end;
-end;
-
-
-procedure TUPFCObj.MakePosSequence;
-
-{Var
- S:String;
-}
-begin
- {
- S :='Phases=1 ';
- S := S + Format('BasekV=%-.5g ', [kVbase/SQRT3]);
- S := S + Format('R1=%-.5g ', [R1]);
- S := S + Format('X1=%-.5g ', [X1]);
-
- Parser.CmdString := S;
- Edit;
-
- inherited;
- }
-end;
-
-// ======================== BEGIN STATE VARIABLES ===============================
-
function TUPFCObj.NumVariables: Integer;
begin
-
Result := NumUPFCVariables;
-
end;
procedure TUPFCObj.Set_Variable(i: Integer; Value: Double);
@@ -1275,7 +1037,6 @@ procedure TUPFCObj.Set_Variable(i: Integer; Value: Double);
14:
Sr1^[1].im := Value;
end;
-
end;
function TUPFCObj.Get_Variable(i: Integer): Double;
@@ -1310,23 +1071,18 @@ function TUPFCObj.Get_Variable(i: Integer): Double;
Result := SR1^[1].re;
14:
Result := SR1^[1].im;
- else
end;
-
end;
procedure TUPFCObj.GetAllVariables(States: pDoubleArray);
var
i: Integer;
begin
-
for i := 1 to NumUPFCVariables do
States^[i] := Variable[i];
-
end;
function TUPFCObj.VariableName(i: Integer): String;
-
begin
if i < 1 then
Exit; // Someone goofed
@@ -1360,11 +1116,7 @@ function TUPFCObj.VariableName(i: Integer): String;
Result := 'Re{Sr1^[1]}';
14:
Result := 'Im{Sr1^[1]}';
- else
-
end;
-
end;
-// ======================== END STATE VARIABLES ===============================
-end.
+end.
\ No newline at end of file
diff --git a/src/PCElements/VSConverter.pas b/src/PCElements/VSConverter.pas
index b50a5d341..bf303d6ed 100644
--- a/src/PCElements/VSConverter.pas
+++ b/src/PCElements/VSConverter.pas
@@ -16,23 +16,44 @@ interface
Circuit,
PCElement,
UcMatrix,
- Ucomplex,
+ UComplex, DSSUcomplex,
ArrayDef,
XYCurve;
type
+{$SCOPEDENUMS ON}
+ TVSConverterProp = (
+ INVALID = 0,
+ phases = 1,
+ Bus1 = 2,
+ kVac = 3,
+ kVdc = 4,
+ kW = 5,
+ Ndc = 6,
+ Rac = 7,
+ Xac = 8,
+ m0 = 9,
+ d0 = 10,
+ Mmin = 11,
+ Mmax = 12,
+ Iacmax = 13,
+ Idcmax = 14,
+ Vacref = 15,
+ Pacref = 16,
+ Qacref = 17,
+ Vdcref = 18,
+ VscMode = 19
+ );
+{$SCOPEDENUMS OFF}
+
TVSConverter = class(TPCClass)
- PRIVATE
- procedure VscSetBus1(const S: String);
PROTECTED
- procedure DefineProperties;
- function MakeLike(const VSCName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
- end;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
+ end;
TVSConverterObj = class(TPCElement)
PRIVATE
@@ -52,32 +73,29 @@ TVSConverterObj = class(TPCElement)
FMaxIac: Double;
FMaxIdc: Double;
Fmode: Integer;
- FNdc: Integer;
+ Ndc: Integer;
LastCurrents: pComplexArray; // state memory for GetInjCurrents
PUBLIC
constructor Create(ParClass: TDSSClass; const FaultName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
- // these three functions make it a PCElement
+ // these three functions make it a PCElement
function InjCurrents: Integer; OVERRIDE;
procedure GetInjCurrents(Curr: pComplexArray);
procedure GetCurrents(Curr: pComplexArray); OVERRIDE;
- procedure MakePosSequence; OVERRIDE;
-
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure MakePosSequence(); OVERRIDE;
end;
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Dynamics,
@@ -89,27 +107,31 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TVSConverterObj;
+ TProp = TVSConverterProp;
const
- NumPropsthisclass = 19;
+ NumPropsThisClass = Ord(High(TProp));
VSC_FIXED = 0;
VSC_PACVAC = 1;
VSC_PACQAC = 2;
VSC_VDCVAC = 3;
VSC_VDCQAC = 4;
-
-// =====================================================
-// Class Methods
-// =====================================================
+var
+ PropInfo: Pointer = NIL;
+ ModeEnum: TDSSEnum;
constructor TVSConverter.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'VSConverter';
- DSSClassType := VS_CONVERTER + PC_ELEMENT;
- ActiveElement := 0;
- DefineProperties;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ ModeEnum := TDSSEnum.Create('VSConverter: Control Mode', True, 1, 4,
+ ['Fixed', 'PacVac', 'PacQac', 'VdcVac', 'VdcQac'],
+ [0, 1, 2, 3, 4]);
+ ModeEnum.DefaultValue := VSC_FIXED;
+ end;
+ inherited Create(dssContext, VS_CONVERTER, 'VSConverter');
end;
destructor TVSConverter.Destroy;
@@ -118,256 +140,141 @@ destructor TVSConverter.Destroy;
end;
procedure TVSConverter.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
Numproperties := NumPropsThisClass;
- CountProperties;
- AllocatePropertyArrays;
-
- PropertyName^[1] := 'phases';
- PropertyName^[2] := 'Bus1';
- PropertyName^[3] := 'kVac';
- PropertyName^[4] := 'kVdc';
- PropertyName^[5] := 'kW';
- PropertyName^[6] := 'Ndc';
- PropertyName^[7] := 'Rac';
- PropertyName^[8] := 'Xac';
- PropertyName^[9] := 'm0';
- PropertyName^[10] := 'd0';
- PropertyName^[11] := 'Mmin';
- PropertyName^[12] := 'Mmax';
- PropertyName^[13] := 'Iacmax';
- PropertyName^[14] := 'Idcmax';
- PropertyName^[15] := 'Vacref';
- PropertyName^[16] := 'Pacref';
- PropertyName^[17] := 'Qacref';
- PropertyName^[18] := 'Vdcref';
- PropertyName^[19] := 'VscMode';
-
- PropertyHelp[1] := 'Number of AC plus DC conductors. Default is 4. AC phases numbered before DC conductors.';
- PropertyHelp[2] := 'Name of converter bus, containing both AC and DC conductors. Bus2 is always ground.';
- PropertyHelp[3] := 'Nominal AC line-neutral voltage in kV. Must be specified > 0.';
- PropertyHelp[4] := 'Nominal DC voltage in kV. Must be specified > 0.';
- PropertyHelp[5] := 'Nominal converter power in kW. Must be specified > 0.';
- PropertyHelp[6] := 'Number of DC conductors. Default is 1. DC conductors numbered after AC phases.';
- PropertyHelp[7] := 'AC resistance (ohms) for the converter transformer, plus any series reactors. Default is 0.' + CRLF +
- 'Must be 0 for Vac control mode.';
- PropertyHelp[8] := 'AC reactance (ohms) for the converter transformer, plus any series reactors. Default is 0.' + CRLF +
- 'Must be 0 for Vac control mode. Must be >0 for PacVac, PacQac or VacVdc control mode.';
- PropertyHelp[9] := 'Fixed or initial value of the modulation index. Default is 0.5.';
- PropertyHelp[10] := 'Fixed or initial value of the power angle in degrees. Default is 0.';
- PropertyHelp[11] := 'Minimum value of modulation index. Default is 0.1.';
- PropertyHelp[12] := 'Maximum value of modulation index. Default is 0.9.';
- PropertyHelp[13] := 'Maximum value of AC line current, per-unit of nominal. Default is 2.';
- PropertyHelp[14] := 'Maximum value of DC current, per-unit of nominal. Default is 2.';
- PropertyHelp[15] := 'Reference AC line-to-neutral voltage, RMS Volts. Default is 0.' + CRLF +
- 'Applies to PacVac and VdcVac control modes, influencing m.';
- PropertyHelp[16] := 'Reference total AC real power, Watts. Default is 0.' + CRLF +
- 'Applies to PacVac and PacQac control modes, influencing d.';
- PropertyHelp[17] := 'Reference total AC reactive power, Vars. Default is 0.' + CRLF +
- 'Applies to PacQac and VdcQac control modes, influencing m.';
- PropertyHelp[18] := 'Reference DC voltage, Volts. Default is 0.' + CRLF +
- 'Applies to VdcVac control mode, influencing d.';
- PropertyHelp[19] := 'Control Mode (Fixed|PacVac|PacQac|VdcVac|VdcQac). Default is Fixed.';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enum properties
+ PropertyType[ord(TProp.VscMode)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.VscMode)] := ptruint(@obj.Fmode);
+ PropertyOffset2[ord(TProp.VscMode)] := PtrInt(ModeEnum);
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+
+ // integer properties
+ PropertyType[ord(TProp.Ndc)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Ndc)] := ptruint(@obj.Ndc);
+
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.kVac)] := ptruint(@obj.FkVac);
+ PropertyOffset[ord(TProp.kVdc)] := ptruint(@obj.FkVdc);
+ PropertyOffset[ord(TProp.kW)] := ptruint(@obj.FkW);
+ PropertyOffset[ord(TProp.Rac)] := ptruint(@obj.FRac);
+ PropertyOffset[ord(TProp.Xac)] := ptruint(@obj.FXac);
+ PropertyOffset[ord(TProp.m0)] := ptruint(@obj.Fm);
+ PropertyOffset[ord(TProp.d0)] := ptruint(@obj.Fd);
+ PropertyOffset[ord(TProp.Mmin)] := ptruint(@obj.FMinM);
+ PropertyOffset[ord(TProp.Mmax)] := ptruint(@obj.FMaxM);
+ PropertyOffset[ord(TProp.Iacmax)] := ptruint(@obj.FMaxIac);
+ PropertyOffset[ord(TProp.Idcmax)] := ptruint(@obj.FMaxIdc);
+ PropertyOffset[ord(TProp.Vacref)] := ptruint(@obj.FRefVac);
+ PropertyOffset[ord(TProp.Pacref)] := ptruint(@obj.FRefPac);
+ PropertyOffset[ord(TProp.Qacref)] := ptruint(@obj.FRefQac);
+ PropertyOffset[ord(TProp.Vdcref)] := ptruint(@obj.FRefVdc);
ActiveProperty := NumPropsThisClass;
inherited DefineProperties;
end;
-function TVSConverter.NewObject(const ObjName: String): Integer;
-begin
- with ActiveCircuit do
- begin
- ActiveCktElement := TVSConverterObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
-end;
-
-procedure TVSConverter.VscSetBus1(const S: String);
+function TVSConverter.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- s2: String;
- i, dotpos: Integer;
+ Obj: TObj;
begin
- with DSS.ActiveVSconverterObj do
- begin
- SetBus(1, S);
- dotpos := Pos('.', S);
- if dotpos > 0 then
- S2 := Copy(S, 1, dotpos - 1)
- else
- S2 := Copy(S, 1, Length(S));
- for i := 1 to Fnphases do
- S2 := S2 + '.0';
- SetBus(2, S2); // default setting for Bus2=Bus1.0.0.0.0
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-function TVSConverter.Edit: Integer;
+procedure TVSConverterObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
- Tok: String;
+ S, S2: String;
+ i, dotpos: Integer;
begin
- Result := 0;
- DSS.ActiveVSConverterObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveVSConverterObj; // use property to set this value
-
- with DSS.ActiveVSConverterObj do
- begin
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 350);
- 1:
- if Fnphases <> Parser.IntValue then
- begin
- Nphases := Parser.IntValue;
- NConds := Fnphases;
- ActiveCircuit.BusNameRedefined := TRUE;
- end;
- 2:
- VscSetBus1(param);
- 3:
- FkVac := Parser.DblValue;
- 4:
- FkVdc := Parser.DblValue;
- 5:
- FkW := Parser.DblValue;
- 6:
- FNdc := Parser.IntValue;
- 7:
- FRac := Parser.DblValue;
- 8:
- FXac := Parser.DblValue;
- 9:
- Fm := Parser.DblValue;
- 10:
- Fd := Parser.DblValue;
- 11:
- FMinM := Parser.DblValue;
- 12:
- FMaxM := Parser.DblValue;
- 13:
- FMaxIac := Parser.DblValue;
- 14:
- FMaxIdc := Parser.DblValue;
- 15:
- FRefVac := Parser.DblValue;
- 16:
- FRefPac := Parser.DblValue;
- 17:
- FRefQac := Parser.DblValue;
- 18:
- FRefVdc := Parser.DblValue;
- 19:
- begin
- Tok := Uppercase(LeftStr(param, 4));
- if CompareStr(LeftStr(Tok, 1), 'F') = 0 then
- Fmode := VSC_FIXED
- else
- if CompareStr(Tok, 'PACV') = 0 then
- Fmode := VSC_PACVAC
- else
- if CompareStr(Tok, 'PACQ') = 0 then
- Fmode := VSC_PACQAC
- else
- if CompareStr(Tok, 'VDCV') = 0 then
- Fmode := VSC_VDCVAC
- else
- if CompareStr(Tok, 'VDCQ') = 0 then
- Fmode := VSC_VDCQAC
- else
- Fmode := VSC_FIXED
- end;
- else
- ClassEdit(DSS.ActiveVSConverterObj, ParamPointer - NumPropsThisClass)
+ case Idx of
+ ord(TProp.phases):
+ if Fnphases <> previousIntVal then
+ begin
+ NConds := Fnphases;
+ ActiveCircuit.BusNameRedefined := TRUE;
end;
-
- case ParamPointer of
- 1..16:
- YprimInvalid := TRUE;
+ ord(TProp.bus1):
+ begin
+ S := GetBus(1);
+ dotpos := Pos('.', S);
+ if dotpos > 0 then
+ S2 := Copy(S, 1, dotpos - 1)
else
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ S2 := Copy(S, 1, Length(S));
+ for i := 1 to Fnphases do
+ S2 := S2 + '.0';
+ SetBus(2, S2); // default setting for Bus2=Bus1.0.0.0.0
end;
- RecalcElementData;
end;
+ case Idx of
+ 1..16:
+ YprimInvalid := TRUE;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-function TVSConverter.MakeLike(const VSCName: String): Integer;
+procedure TVSConverterObj.MakeLike(OtherPtr: Pointer);
var
- OtherVSC: TVSConverterObj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- OtherVSC := Find(VSCName);
- if OtherVSC <> NIL then
- with DSS.ActiveVSConverterObj do
- begin
- if Fnphases <> OtherVSC.Fnphases then
- begin
- Fnphases := OtherVSC.Fnphases;
- FnTerms := OtherVSC.FnTerms;
- NConds := Fnphases;
- FNdc := OtherVSC.FNdc;
- Yorder := FnConds * FnTerms;
- YPrimInvalid := TRUE;
- FkVac := OtherVSC.FkVac;
- FkVdc := OtherVSC.FkVdc;
- FkW := OtherVSC.FkW;
- FRac := OtherVSC.FRac;
- FXac := OtherVSC.FXac;
- Fm := OtherVSC.Fm;
- Fd := OtherVSC.Fd;
- FMinM := OtherVSC.FMinM;
- FMaxM := OtherVSC.FMaxM;
- FMaxIac := OtherVSC.FMaxIac;
- FMaxIdc := OtherVSC.FMaxIdc;
- FRefVac := OtherVSC.FRefVac;
- FRefPac := OtherVSC.FRefPac;
- FRefQac := OtherVSC.FRefQac;
- FRefVdc := OtherVSC.FRefVdc;
- Fmode := OtherVSC.Fmode;
- end;
- BaseFrequency := OtherVSC.BaseFrequency;
- ClassMakeLike(OtherVSC);
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherVSC.PropertyValue[i];
- Result := 1;
- end // with
- else
- DoSimpleMsg('Error in VSConverter MakeLike: "' + VSCName + '" Not Found.', 351);
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ if Fnphases <> Other.Fnphases then
+ begin
+ Fnphases := Other.Fnphases;
+ FnTerms := Other.FnTerms;
+ NConds := Fnphases;
+ end;
+ Ndc := Other.Ndc;
+ Yorder := FnConds * FnTerms;
+ YPrimInvalid := TRUE;
+ FkVac := Other.FkVac;
+ FkVdc := Other.FkVdc;
+ FkW := Other.FkW;
+ FRac := Other.FRac;
+ FXac := Other.FXac;
+ Fm := Other.Fm;
+ Fd := Other.Fd;
+ FMinM := Other.FMinM;
+ FMaxM := Other.FMaxM;
+ FMaxIac := Other.FMaxIac;
+ FMaxIdc := Other.FMaxIdc;
+ FRefVac := Other.FRefVac;
+ FRefPac := Other.FRefPac;
+ FRefQac := Other.FRefQac;
+ FRefVdc := Other.FRefVdc;
+ Fmode := Other.Fmode;
+ BaseFrequency := Other.BaseFrequency;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// Object Methods
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TVSConverterObj.Create(ParClass: TDSSClass; const FaultName: String);
begin
inherited Create(ParClass);
DSSObjType := ParClass.DSSClassType;
- Name := LowerCase(FaultName);
+ Name := AnsiLowerCase(FaultName);
LastCurrents := NIL;
- // typically the first 3 "phases" are AC, and the last one is DC
- NPhases := 4;
+ // typically the first 3 "phases" are AC, and the last one is DC
+ FNPhases := 4;
Fnconds := 4;
Nterms := 2; // two-terminal device, like the voltage source
- FNdc := 1;
+ Ndc := 1;
FkVac := 1.0;
FkVdc := 1.0;
@@ -387,7 +294,6 @@ constructor TVSConverterObj.Create(ParClass: TDSSClass; const FaultName: String)
FmaxIac := 2.0;
FmaxIdc := 2.0;
- InitPropertyValues(0);
Yorder := Fnterms * Fnconds;
RecalcElementData;
end;
@@ -416,7 +322,7 @@ procedure TVSConverterObj.CalcYPrim;
FreqMultiplier: Double;
i: Integer;
begin
-// build YPrim_Series non-zero for just the AC phases, and it will be diagonal
+ // build YPrim_Series non-zero for just the AC phases, and it will be diagonal
if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Series = NIL) {YPrimInvalid} then
begin
if YPrim_Series <> NIL then
@@ -432,17 +338,17 @@ procedure TVSConverterObj.CalcYPrim;
Yprim.Clear;
end;
- // calculate the AC voltage source admittance
+ // calculate the AC voltage source admittance
FYprimFreq := ActiveCircuit.Solution.Frequency;
FreqMultiplier := FYprimFreq / BaseFrequency;
Value.re := FRac;
Value.im := FXac * FreqMultiplier;
Value := cinv(Value);
- Value2 := cnegate(Value);
+ Value2 := -Value;
with YPrim_Series do
begin
- for i := 1 to (Fnphases - FNdc) do
+ for i := 1 to (Fnphases - Ndc) do
begin
SetElement(i, i, Value);
SetElement(i + Fnphases, i + Fnphases, Value);
@@ -468,20 +374,20 @@ procedure TVSConverterObj.GetCurrents(Curr: pComplexArray);
with ActiveCircuit.Solution do
begin
ComputeVTerminal;
- // add the injection currents from both AC and DC nodes, to the
- // currents from Yprim elements, which should be zero at the DC nodes
+ // add the injection currents from both AC and DC nodes, to the
+ // currents from Yprim elements, which should be zero at the DC nodes
YPrim.MVMult(Curr, Vterminal);
GetInjCurrents(ComplexBuffer);
for i := 1 to Yorder do
begin
- Curr^[i] := Csub(Curr^[i], ComplexBuffer^[i]);
+ Curr^[i] := Curr^[i] - ComplexBuffer^[i];
LastCurrents^[i] := Curr^[i];
end;
end;
except
on E: Exception do
- DoErrorMsg(('GetCurrents for Element: ' + Name + '.'), E.Message,
- 'Inadequate storage allotted for circuit element.', 327);
+ DoErrorMsg(Format(_('GetCurrents for Element: %s.'), [Name]),
+ E.Message, _('Inadequate storage allotted for circuit element.'), 327);
end;
end;
@@ -492,16 +398,15 @@ procedure TVSConverterObj.GetInjCurrents(Curr: pComplexArray);
Pac, Deg, Idc, Idclim{, Iaclim, Itmag}: Double;
i, Nac: Integer;
begin
-
- { AC Voltage source injection currents given by this formula:
- _ _ _ _
- |Iinj1| |Vsource |
- | | = [Yprim] | |
- |Iinj2| | 0 |
- _ _ _ _
- }
-
- Nac := FNphases - FNdc;
+ // AC Voltage source injection currents given by this formula:
+ // _ _ _ _
+ // |Iinj1| |Vsource |
+ // | | = [Yprim] | |
+ // |Iinj2| | 0 |
+ // _ _ _ _
+
+
+ Nac := FNphases - Ndc;
Idclim := FMaxIdc * Fkw / FkVdc;
// Iaclim := FMaxIac * Fkw / FkVac / Nac;
@@ -509,20 +414,12 @@ procedure TVSConverterObj.GetInjCurrents(Curr: pComplexArray);
ComputeVterminal;
ITerminalUpdated := FALSE;
GetTerminalCurrents(ITerminal);
-// for i := 1 to Nac do begin
-// Itmag := cabs(Iterminal^[i]);
-// if Itmag > Iaclim then begin
-// Itmag := Iaclim / Itmag;
-// Iterminal^[i].re := Iterminal^[i].re * Itmag;
-// Iterminal^[i].im := Iterminal^[i].im * Itmag;
-// end;
-// end;
// do the AC voltage source injection - dependent voltage sources kept in ComplexBuffer
Vdc := Vterminal^[FNphases];
if (Vdc.re = 0.0) and (Vdc.im = 0.0) then
Vdc.re := 1000.0 * FkVdc;
- Vmag := CMulReal(Vdc, 0.353553 * Fm);
+ Vmag := Vdc * (0.353553 * Fm);
RotatePhasorDeg(Vmag, 1.0, Fd);
ComplexBuffer^[1] := Vmag;
Deg := -360.0 / Nac;
@@ -539,9 +436,9 @@ procedure TVSConverterObj.GetInjCurrents(Curr: pComplexArray);
Stotal.im := 0.0;
for i := 1 to Nac do
begin
-// Sphase := Cmul (ComplexBuffer^[i], Conjg(LastCurrents^[i]));
- Sphase := Cmul(ComplexBuffer^[i], Conjg(Iterminal^[i]));
- Stotal := Cadd(Stotal, Sphase);
+// Sphase := ComplexBuffer^[i] * cong(LastCurrents^[i]);
+ Sphase := ComplexBuffer^[i] * cong(Iterminal^[i]);
+ Stotal := Stotal + Sphase;
end;
Pac := Stotal.re;
// Qac := Stotal.im;
@@ -560,142 +457,16 @@ procedure TVSConverterObj.GetInjCurrents(Curr: pComplexArray);
ITerminalUpdated := FALSE;
end;
-procedure TVSConverterObj.DumpProperties(F: TFileStream; Complete: Boolean);
-var
- i: Integer;
-begin
- inherited DumpProperties(F, complete);
- with ParentClass do
- begin
- FSWriteln(F, Format('~ %s=%d', [PropertyName^[1], Fnphases]));
- FSWriteln(F, Format('~ %s=%s', [PropertyName^[2], firstbus]));
- FSWriteln(F, Format('~ %s=%8.1f', [PropertyName^[3], FkVac]));
- FSWriteln(F, Format('~ %s=%8.1f', [PropertyName^[4], FkVdc]));
- FSWriteln(F, Format('~ %s=%8.1f', [PropertyName^[5], FkW]));
- FSWriteln(F, Format('~ %s=%d', [PropertyName^[6], FNdc]));
- FSWriteln(F, Format('~ %s=%.4f', [PropertyName^[7], FRac]));
- FSWriteln(F, Format('~ %s=%.4f', [PropertyName^[8], FXac]));
- FSWriteln(F, Format('~ %s=%.4f', [PropertyName^[9], Fm]));
- FSWriteln(F, Format('~ %s=%.4f', [PropertyName^[10], Fd]));
- FSWriteln(F, Format('~ %s=%.4f', [PropertyName^[11], FMinM]));
- FSWriteln(F, Format('~ %s=%.4f', [PropertyName^[12], FMaxM]));
- FSWriteln(F, Format('~ %s=%.4f', [PropertyName^[13], FMaxIac]));
- FSWriteln(F, Format('~ %s=%.4f', [PropertyName^[14], FMaxIdc]));
- FSWriteln(F, Format('~ %s=%.4f', [PropertyName^[15], FRefVac]));
- FSWriteln(F, Format('~ %s=%.4f', [PropertyName^[16], FRefPac]));
- FSWriteln(F, Format('~ %s=%.4f', [PropertyName^[17], FRefQac]));
- FSWriteln(F, Format('~ %s=%.4f', [PropertyName^[18], FRefVdc]));
- case Fmode of
- VSC_FIXED:
- FSWriteln(F, '~ ' + PropertyName^[19] + '= Fixed');
- VSC_PACVAC:
- FSWriteln(F, '~ ' + PropertyName^[19] + '= PacVac');
- VSC_PACQAC:
- FSWriteln(F, '~ ' + PropertyName^[19] + '= PacQac');
- VSC_VDCVAC:
- FSWriteln(F, '~ ' + PropertyName^[19] + '= VdcVac');
- VSC_VDCQAC:
- FSWriteln(F, '~ ' + PropertyName^[19] + '= VdcQac');
- end;
- for i := NumPropsthisClass + 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
- end;
-end;
-
-procedure TVSConverterObj.InitPropertyValues(ArrayOffset: Integer);
-begin
- PropertyValue[1] := '4';
- PropertyValue[2] := getbus(1);
- PropertyValue[3] := '1';
- PropertyValue[4] := '1';
- PropertyValue[5] := '1';
- PropertyValue[6] := '1';
- PropertyValue[7] := '0';
- PropertyValue[8] := '0';
- PropertyValue[9] := '0.5';
- PropertyValue[10] := '0';
- PropertyValue[11] := '0.1';
- PropertyValue[12] := '0.9';
- PropertyValue[13] := '0';
- PropertyValue[14] := '0';
- PropertyValue[15] := '0';
- PropertyValue[16] := '0';
- PropertyValue[17] := '0';
- PropertyValue[18] := '0';
- PropertyValue[19] := 'FIXED';
-
- inherited InitPropertyValues(NumPropsThisClass);
-end;
-
-function TVSConverterObj.GetPropertyValue(Index: Integer): String;
-begin
- case Index of
- 1:
- Result := Format('%d', [Nphases]);
- 2:
- Result := GetBus(1);
- 3:
- Result := Format('%.8g', [FkVac]);
- 4:
- Result := Format('%.8g', [FkVdc]);
- 5:
- Result := Format('%.8g', [FkW]);
- 6:
- Result := Format('%d', [FNdc]);
- 7:
- Result := Format('%.8g', [FRac]);
- 8:
- Result := Format('%.8g', [FXac]);
- 9:
- Result := Format('%.8g', [Fm]);
- 10:
- Result := Format('%.8g', [Fd]);
- 11:
- Result := Format('%.8g', [FMinM]);
- 12:
- Result := Format('%.8g', [FMaxM]);
- 13:
- Result := Format('%.8g', [FMaxIac]);
- 14:
- Result := Format('%.8g', [FMaxIdc]);
- 15:
- Result := Format('%.8g', [FRefVac]);
- 16:
- Result := Format('%.8g', [FRefPac]);
- 17:
- Result := Format('%.8g', [FRefQac]);
- 18:
- Result := Format('%.8g', [FRefVdc]);
- 19:
- case Fmode of
- VSC_FIXED:
- Result := 'Fixed';
- VSC_PACVAC:
- Result := 'PacVac';
- VSC_PACQAC:
- Result := 'PacQac';
- VSC_VDCVAC:
- Result := 'VdcVac';
- VSC_VDCQAC:
- Result := 'VdcQac';
- end
- else
- Result := inherited GetPropertyValue(Index);
- end;
-end;
-
-procedure TVSConverterObj.MakePosSequence;
+procedure TVSConverterObj.MakePosSequence();
begin
if FnPhases <> 2 then
begin
- Parser.CmdString := 'Phases=2';
- Edit;
- Parser.CmdString := 'Ndc=1';
- Edit;
+ //TODO: why two edits?
+ SetInteger(ord(TProp.Phases), 2);
+ SetInteger(ord(TProp.Ndc), 1);
end;
inherited;
end;
+finalization ModeEnum.Free;
end.
diff --git a/src/PCElements/VSource.pas b/src/PCElements/VSource.pas
index b8dedaad3..78783eb7c 100644
--- a/src/PCElements/VSource.pas
+++ b/src/PCElements/VSource.pas
@@ -2,21 +2,11 @@
{
----------------------------------------------------------
- Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
+ Copyright (c) 2008-2022, Electric Power Research Institute, Inc.
All rights reserved.
----------------------------------------------------------
}
-{
- 2-17-00 Change Log
- Added Angle to VMag calculation
-
- 6-18-00 Added ability to do specify impedance in ohms or short circuit current
- 5-17-01 Moved Spectrum to Base class
- 2-10-09 Converted to 2-terminal voltage source
-
-}
-
interface
uses
@@ -25,27 +15,58 @@ interface
PCClass,
PCElement,
ucmatrix,
- ucomplex,
+ UComplex, DSSUcomplex,
Spectrum,
Loadshape;
type
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+{$SCOPEDENUMS ON}
+ TVsourceProp = (
+ INVALID = 0,
+ bus1 = 1,
+ basekv = 2,
+ pu = 3,
+ angle = 4,
+ frequency = 5,
+ phases = 6,
+ MVAsc3 = 7,
+ MVAsc1 = 8,
+ x1r1 = 9,
+ x0r0 = 10,
+ Isc3 = 11,
+ Isc1 = 12,
+ R1 = 13,
+ X1 = 14,
+ R0 = 15,
+ X0 = 16,
+ ScanType = 17,
+ Sequence = 18,
+ bus2 = 19,
+ Z1 = 20,
+ Z0 = 21,
+ Z2 = 22,
+ puZ1 = 23,
+ puZ0 = 24,
+ puZ2 = 25,
+ baseMVA = 26,
+ Yearly = 27,
+ Daily = 28,
+ Duty = 29,
+ Model = 30,
+ puZideal = 31
+ );
+{$SCOPEDENUMS OFF}
TVsource = class(TPCClass)
- PRIVATE
- procedure VsourceSetBus1(const S: String);
PROTECTED
- procedure DefineProperties;
- function MakeLike(const OtherSource: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TVsourceObj = class(TPCElement)
PRIVATE
MVAsc3: Double;
@@ -70,7 +91,7 @@ TVsourceObj = class(TPCElement)
puZ2Specified: Boolean;
Z2Specified: Boolean;
Z0Specified: Boolean;
- IsQuasiIdeal: Boolean; // Use puZideal for power flow
+ IsQuasiIdeal: LongBool; // Use puZideal for power flow
ScanType: Integer;
SequenceType: Integer;
@@ -83,11 +104,9 @@ TVsourceObj = class(TPCElement)
procedure CalcDutyMult(Hr: Double);
procedure CalcYearlyMult(Hr: Double);
- function InterpretSourceModel(const s: String): Boolean;
procedure GetInjCurrents(Curr: pComplexArray);
PUBLIC
-
Z: TCmatrix; // Base Frequency Series Z matrix
Zinv: TCMatrix;
VMag: Double;
@@ -97,16 +116,14 @@ TVsourceObj = class(TPCElement)
Angle: Double;
SrcFrequency: Double;
- DailyShape: String; // Daily (24 HR) load shape
- DailyShapeObj: TLoadShapeObj; // Daily load Shape FOR this load
- DutyShape: String; // Duty cycle load shape FOR changes typically less than one hour
- DutyShapeObj: TLoadShapeObj; // Shape for this load
- YearlyShape: String; // ='fixed' means no variation exempt from variation
- YearlyShapeObj: TLoadShapeObj; // Shape for this load
-
+ DailyShapeObj: TLoadShapeObj; // Daily (24 HR) load shape
+ DutyShapeObj: TLoadShapeObj; // Duty cycle load shape FOR changes typically less than one hour
+ YearlyShapeObj: TLoadShapeObj;
constructor Create(ParClass: TDSSClass; const SourceName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
@@ -114,20 +131,14 @@ TVsourceObj = class(TPCElement)
function InjCurrents: Integer; OVERRIDE;
procedure GetCurrents(Curr: pComplexArray); OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
implementation
-
uses
- ParserDel,
Circuit,
DSSClassDefs,
DSSGlobals,
@@ -139,539 +150,335 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TVsourceObj;
+ TProp = TVsourceProp;
const
- NumPropsThisClass = 31;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
+ ModelEnum: TDSSEnum;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TVsource.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TVsource.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'Vsource';
- DSSClassType := SOURCE + NON_PCPD_ELEM; // Don't want this in PC Element List
-
- ActiveElement := 0;
-
- DefineProperties;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ ModelEnum := TDSSEnum.Create('VSource: Model', True, 1, 1, ['Thevenin', 'Ideal'], [0, Integer(True)]);
+ end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, SOURCE or NON_PCPD_ELEM, 'Vsource');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TVsource.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TVSource.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
- PropertyName[1] := 'bus1';
- PropertyName[2] := 'basekv';
- PropertyName[3] := 'pu';
- PropertyName[4] := 'angle';
- PropertyName[5] := 'frequency';
- PropertyName[6] := 'phases';
- PropertyName[7] := 'MVAsc3';
- PropertyName[8] := 'MVAsc1';
- PropertyName[9] := 'x1r1';
- PropertyName[10] := 'x0r0';
- PropertyName[11] := 'Isc3';
- PropertyName[12] := 'Isc1';
- PropertyName[13] := 'R1';
- PropertyName[14] := 'X1';
- PropertyName[15] := 'R0';
- PropertyName[16] := 'X0';
- PropertyName[17] := 'ScanType';
- PropertyName[18] := 'Sequence';
- PropertyName[19] := 'bus2';
- PropertyName[20] := 'Z1';
- PropertyName[21] := 'Z0';
- PropertyName[22] := 'Z2';
- PropertyName[23] := 'puZ1';
- PropertyName[24] := 'puZ0';
- PropertyName[25] := 'puZ2';
- PropertyName[26] := 'baseMVA';
- PropertyName[27] := 'Yearly';
- PropertyName[28] := 'Daily';
- PropertyName[29] := 'Duty';
- PropertyName[30] := 'Model';
- PropertyName[31] := 'puZideal';
-
-
- // define Property help values
- PropertyHelp[1] := 'Name of bus to which the main terminal (1) is connected.' + CRLF + 'bus1=busname' + CRLF + 'bus1=busname.1.2.3' + CRLF + CRLF +
- 'The VSOURCE object is a two-terminal voltage source (thevenin equivalent). ' +
- 'Bus2 defaults to Bus1 with all phases connected to ground (node 0) unless previously specified. This is a Yg connection. ' +
- 'If you want something different, define the Bus2 property ezplicitly.';
- PropertyHelp[2] := 'Base Source kV, usually phase-phase (L-L) unless you are making a positive-sequence model or 1-phase model' +
- 'in which case, it will be phase-neutral (L-N) kV.';
- PropertyHelp[3] := 'Per unit of the base voltage that the source is actually operating at.' + CRLF +
- '"pu=1.05"';
- PropertyHelp[4] := 'Phase angle in degrees of first phase: e.g.,Angle=10.3';
- PropertyHelp[5] := 'Source frequency. Defaults to system default base frequency.';
- PropertyHelp[6] := 'Number of phases. Defaults to 3.';
- PropertyHelp[7] := 'MVA Short circuit, 3-phase fault. Default = 2000. ' +
- 'Z1 is determined by squaring the base kv and dividing by this value. ' +
- 'For single-phase source, this value is not used.';
- PropertyHelp[8] := 'MVA Short Circuit, 1-phase fault. Default = 2100. ' +
- 'The "single-phase impedance", Zs, is determined by squaring the base kV and dividing by this value. ' +
- 'Then Z0 is determined by Z0 = 3Zs - 2Z1. For 1-phase sources, Zs is used directly. ' +
- 'Use X0R0 to define X/R ratio for 1-phase source.';
- PropertyHelp[9] := 'Positive-sequence X/R ratio. Default = 4.';
- PropertyHelp[10] := 'Zero-sequence X/R ratio.Default = 3.';
- PropertyHelp[11] := 'Alternate method of defining the source impedance. ' + CRLF +
- '3-phase short circuit current, amps. Default is 10000.';
- PropertyHelp[12] := 'Alternate method of defining the source impedance. ' + CRLF +
- 'single-phase short circuit current, amps. Default is 10500.';
- PropertyHelp[13] := 'Alternate method of defining the source impedance. ' + CRLF +
- 'Positive-sequence resistance, ohms. Default is 1.65.';
- PropertyHelp[14] := 'Alternate method of defining the source impedance. ' + CRLF +
- 'Positive-sequence reactance, ohms. Default is 6.6.';
- PropertyHelp[15] := 'Alternate method of defining the source impedance. ' + CRLF +
- 'Zero-sequence resistance, ohms. Default is 1.9.';
- PropertyHelp[16] := 'Alternate method of defining the source impedance. ' + CRLF +
- 'Zero-sequence reactance, ohms. Default is 5.7.';
- PropertyHelp[17] := '{pos*| zero | none} Maintain specified sequence for harmonic solution. Default is positive sequence. ' +
- 'Otherwise, angle between phases rotates with harmonic.';
- PropertyHelp[18] := '{pos*| neg | zero} Set the phase angles for the specified symmetrical component sequence for non-harmonic solution modes. ' +
- 'Default is positive sequence. ';
- PropertyHelp[19] := 'Name of bus to which 2nd terminal is connected.' + CRLF + 'bus2=busname' + CRLF + 'bus2=busname.1.2.3' +
- CRLF + CRLF +
- 'Default is Bus1.0.0.0 (grounded wye connection)';
- PropertyHelp[20] := 'Positive-sequence equivalent source impedance, ohms, as a 2-element array representing a complex number. Example: ' + CRLF + CRLF +
- 'Z1=[1, 2] ! represents 1 + j2 ' + CRLF + CRLF +
- 'If defined, Z1, Z2, and Z0 are used to define the impedance matrix of the VSOURCE. ' +
- 'Z1 MUST BE DEFINED TO USE THIS OPTION FOR DEFINING THE MATRIX.' + CRLF + CRLF +
- 'Side Effect: Sets Z2 and Z0 to same values unless they were previously defined.';
- PropertyHelp[21] := 'Zero-sequence equivalent source impedance, ohms, as a 2-element array representing a complex number. Example: ' + CRLF + CRLF +
- 'Z0=[3, 4] ! represents 3 + j4 ' + CRLF + CRLF +
- 'Used to define the impedance matrix of the VSOURCE if Z1 is also specified. ' + CRLF + CRLF +
- 'Note: Z0 defaults to Z1 if it is not specifically defined. ';
- PropertyHelp[22] := 'Negative-sequence equivalent source impedance, ohms, as a 2-element array representing a complex number. Example: ' + CRLF + CRLF +
- 'Z2=[1, 2] ! represents 1 + j2 ' + CRLF + CRLF +
- 'Used to define the impedance matrix of the VSOURCE if Z1 is also specified. ' + CRLF + CRLF +
- 'Note: Z2 defaults to Z1 if it is not specifically defined. If Z2 is not equal to Z1, the impedance matrix is asymmetrical.';
- PropertyHelp[23] := '2-element array: e.g., [1 2]. An alternate way to specify Z1. See Z1 property. Per-unit positive-sequence impedance on base of Vsource BasekV and BaseMVA.';
- PropertyHelp[24] := '2-element array: e.g., [1 2]. An alternate way to specify Z0. See Z0 property. Per-unit zero-sequence impedance on base of Vsource BasekV and BaseMVA.';
- PropertyHelp[25] := '2-element array: e.g., [1 2]. An alternate way to specify Z2. See Z2 property. Per-unit negative-sequence impedance on base of Vsource BasekV and BaseMVA.';
- PropertyHelp[26] := 'Default value is 100. Base used to convert values specifiied with puZ1, puZ0, and puZ2 properties to ohms on kV base specified by BasekV property.';
- PropertyHelp[27] := 'LOADSHAPE object to use for the per-unit voltage for YEARLY-mode simulations. Set the Mult property of the LOADSHAPE ' +
- 'to the pu curve. Qmult is not used. If UseActual=Yes then the Mult curve should be actual L-N kV.' + CRLF + CRLF +
- 'Must be previously defined as a LOADSHAPE object. ' + CRLF + CRLF +
- 'Is set to the Daily load shape when Daily is defined. The daily load shape is repeated in this case. ' +
- 'Set to NONE to reset to no loadahape for Yearly mode. ' +
- 'The default is no variation.';
- PropertyHelp[28] := 'LOADSHAPE object to use for the per-unit voltage for DAILY-mode simulations. Set the Mult property of the LOADSHAPE ' +
- 'to the pu curve. Qmult is not used. If UseActual=Yes then the Mult curve should be actual L-N kV.' + CRLF + CRLF +
- 'Must be previously defined as a LOADSHAPE object. ' + CRLF + CRLF +
- 'Sets Yearly curve if it is not already defined. ' +
- 'Set to NONE to reset to no loadahape for Yearly mode. ' +
- 'The default is no variation.';
- PropertyHelp[29] := 'LOADSHAPE object to use for the per-unit voltage for DUTYCYCLE-mode simulations. Set the Mult property of the LOADSHAPE ' +
- 'to the pu curve. Qmult is not used. If UseActual=Yes then the Mult curve should be actual L-N kV.' + CRLF + CRLF +
- 'Must be previously defined as a LOADSHAPE object. ' + CRLF + CRLF +
- 'Defaults to Daily load shape when Daily is defined. ' +
- 'Set to NONE to reset to no loadahape for Yearly mode. ' +
- 'The default is no variation.';
- PropertyHelp[30] := '{Thevenin* | Ideal} Specifies whether the Vsource is to be considered a Thevenin short circuit model or a quasi-ideal voltage source. If Thevenin, the Vsource uses the impedances defined for all calculations. ' +
- 'If "Ideal", the model uses a small impedance on the diagonal of the impedance matrix for the fundamental base frequency power flow only. Then switches to actual Thevenin model for other frequencies. ';
- PropertyHelp[31] := '2-element array: e.g., [1 2]. The pu impedance to use for the quasi-ideal voltage source model. Should be a very small impedances. Default is [1e-6, 0.001]. Per-unit impedance on base of Vsource BasekV and BaseMVA. ' +
- 'If too small, solution may not work. Be sure to check the voltage values and powers.';
+ CountPropertiesAndAllocate();
+
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyType[ord(TProp.bus2)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+ PropertyOffset[ord(TProp.bus2)] := 2;
+
+ // enum properties
+ PropertyType[ord(TProp.ScanType)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.ScanType)] := ptruint(@obj.ScanType);
+ PropertyOffset2[ord(TProp.ScanType)] := PtrInt(DSS.ScanTypeEnum);
+
+ PropertyType[ord(TProp.Sequence)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.Sequence)] := ptruint(@obj.Sequencetype);
+ PropertyOffset2[ord(TProp.Sequence)] := PtrInt(DSS.SequenceEnum);
+
+ PropertyType[ord(TProp.Model)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.Model)] := ptruint(@obj.IsQuasiIdeal); // LongBool as Integer
+ PropertyOffset2[ord(TProp.Model)] := PtrInt(ModelEnum);
+
+ // object properties
+ PropertyType[ord(TProp.yearly)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.daily)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.duty)] := TPropertyType.DSSObjectReferenceProperty;
+
+ PropertyOffset[ord(TProp.yearly)] := ptruint(@obj.YearlyShapeObj);
+ PropertyOffset[ord(TProp.daily)] := ptruint(@obj.DailyShapeObj);
+ PropertyOffset[ord(TProp.duty)] := ptruint(@obj.DutyShapeObj);
+
+ PropertyOffset2[ord(TProp.yearly)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.daily)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.duty)] := ptruint(DSS.LoadShapeClass);
+
+ // integer
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNphases);
+
+ // complex properties
+ PropertyType[ord(TProp.puZ0)] := TPropertyType.ComplexProperty;
+ PropertyType[ord(TProp.puZ1)] := TPropertyType.ComplexProperty;
+ PropertyType[ord(TProp.puZ2)] := TPropertyType.ComplexProperty;
+ PropertyType[ord(TProp.puZideal)] := TPropertyType.ComplexProperty;
+ PropertyOffset[ord(TProp.puZ0)] := ptruint(@obj.puZ0);
+ PropertyOffset[ord(TProp.puZ1)] := ptruint(@obj.puZ1);
+ PropertyOffset[ord(TProp.puZ2)] := ptruint(@obj.puZ2);
+ PropertyOffset[ord(TProp.puZideal)] := ptruint(@obj.puZideal);
+
+ // "complex parts" properties
+ PropertyType[ord(TProp.Z0)] := TPropertyType.ComplexPartsProperty;
+ PropertyType[ord(TProp.Z1)] := TPropertyType.ComplexPartsProperty;
+ PropertyType[ord(TProp.Z2)] := TPropertyType.ComplexPartsProperty;
+ PropertyOffset[ord(TProp.Z0)] := ptruint(@obj.R0); PropertyOffset2[ord(TProp.Z0)] := ptruint(@obj.X0);
+ PropertyOffset[ord(TProp.Z1)] := ptruint(@obj.R1); PropertyOffset2[ord(TProp.Z1)] := ptruint(@obj.X1);
+ PropertyOffset[ord(TProp.Z2)] := ptruint(@obj.R2); PropertyOffset2[ord(TProp.Z2)] := ptruint(@obj.X2);
+
+ // double properties
+ PropertyOffset[ord(TProp.basekv)] := ptruint(@obj.kVBase);
+ PropertyOffset[ord(TProp.pu)] := ptruint(@obj.PerUnit);
+ PropertyOffset[ord(TProp.angle)] := ptruint(@obj.Angle);
+ PropertyOffset[ord(TProp.frequency)] := ptruint(@obj.SrcFrequency);
+ PropertyOffset[ord(TProp.MVAsc3)] := ptruint(@obj.MVAsc3);
+ PropertyOffset[ord(TProp.MVAsc1)] := ptruint(@obj.MVAsc1);
+ PropertyOffset[ord(TProp.x1r1)] := ptruint(@obj.X1R1);
+ PropertyOffset[ord(TProp.x0r0)] := ptruint(@obj.X0R0);
+ PropertyOffset[ord(TProp.Isc3)] := ptruint(@obj.Isc3);
+ PropertyOffset[ord(TProp.Isc1)] := ptruint(@obj.Isc1);
+
+ PropertyOffset[ord(TProp.R1)] := ptruint(@obj.R1);
+ PropertyOffset[ord(TProp.X1)] := ptruint(@obj.X1);
+ PropertyOffset[ord(TProp.R0)] := ptruint(@obj.R0);
+ PropertyOffset[ord(TProp.X0)] := ptruint(@obj.X0);
+ PropertyFlags[ord(TProp.R1)] := [TPropertyFlag.Redundant];
+ PropertyFlags[ord(TProp.X1)] := [TPropertyFlag.Redundant];
+ PropertyFlags[ord(TProp.R0)] := [TPropertyFlag.Redundant];
+ PropertyFlags[ord(TProp.X0)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.R1)] := ord(TProp.Z1);
+ PropertyRedundantWith[ord(TProp.X1)] := ord(TProp.Z1);
+ PropertyRedundantWith[ord(TProp.R0)] := ord(TProp.Z0);
+ PropertyRedundantWith[ord(TProp.X0)] := ord(TProp.Z0);
+
+ PropertyOffset[ord(TProp.baseMVA)] := ptruint(@obj.BaseMVA);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- // Override help string
- PropertyHelp[NumPropsThisClass + 1] := 'Name of harmonic spectrum for this source. Default is "defaultvsource", which is defined when the DSS starts.';
-
+ inherited DefineProperties;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TVsource.NewObject(const ObjName: String): Integer;
+function TVsource.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new voltage source and add it to Vsource class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TVsourceObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TVsource.VsourceSetBus1(const S: String);
+procedure TVsourceObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- s2: String;
+ S, S2: String;
i, dotpos: Integer;
-
- // Special handling for Bus 1
- // Set Bus2 = Bus1.0.0.0
-
begin
- with DSS.ActiveVSourceObj do
- begin
- SetBus(1, S);
-
- if not Bus2Defined then // Default Bus2 to zero node of Bus1. (Grounded-Y connection)
+ case Idx of
+ ord(TProp.bus1):
begin
- // Strip node designations from S
- dotpos := Pos('.', S);
- if dotpos > 0 then
- S2 := Copy(S, 1, dotpos - 1)
- else
- S2 := Copy(S, 1, Length(S)); // copy up to Dot
- for i := 1 to Fnphases do
- S2 := S2 + '.0'; // append series of ".0"'s
+ // Special handling for Bus 1
+ // Set Bus2 = Bus1.0.0.0
+ if not Bus2Defined then // Default Bus2 to zero node of Bus1. (Grounded-Y connection)
+ begin
+ // Strip node designations from S
+ S := GetBus(1);
+ dotpos := Pos('.', S);
+ if dotpos > 0 then
+ S2 := Copy(S, 1, dotpos - 1)
+ else
+ S2 := Copy(S, 1, Length(S)); // copy up to Dot
+ for i := 1 to Fnphases do
+ S2 := S2 + '.0'; // append series of ".0"'s
- SetBus(2, S2); // default setting for Bus2
+ SetBus(2, S2); // default setting for Bus2
+ end;
end;
- end;
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TVsource.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName,
- Param: String;
- ZTemp: Complex;
-
-begin
- // continue parsing with contents of Parser
- DSS.ActiveVSourceObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveVSourceObj;
-
- Result := 0;
-
- with DSS.ActiveVSourceObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
+ 6:
+ NConds := Fnphases; // Force Reallocation of terminal info
+ 13:
+ R2 := R1;
+ 14:
+ X2 := X1;
+ 20:
begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "VSource.' + Name + '"', 320);
- 1:
- VSourceSetBus1(param); // special handling of Bus 1
- 2:
- kVBase := Parser.DblValue; // basekv
- 3:
- PerUnit := Parser.DblValue; // pu
- 4:
- Angle := Parser.DblValue; // Ang
- 5:
- SrcFrequency := Parser.DblValue; // freq
- 6:
- begin
- Nphases := Parser.Intvalue; // num phases
- NConds := Fnphases; // Force Reallocation of terminal info
- end;
- 7:
- MVAsc3 := Parser.DblValue; // MVAsc3
- 8:
- MVAsc1 := Parser.DblValue; // MVAsc1
- 9:
- X1R1 := Parser.DblValue; // X1/R1
- 10:
- X0R0 := Parser.DblValue; // X0/R0
- 11:
- Isc3 := Parser.DblValue;
- 12:
- Isc1 := Parser.DblValue;
- 13:
- R1 := Parser.DblValue;
- 14:
- X1 := Parser.DblValue;
- 15:
- R0 := Parser.DblValue;
- 16:
- X0 := Parser.DblValue;
- 17:
- case Uppercase(Param)[1] of
- 'P':
- ScanType := 1;
- 'Z':
- ScanType := 0;
- 'N':
- ScanType := -1;
- else
- DoSimpleMsg('Unknown Scan Type for "' + Class_Name + '.' + Name + '": ' + Param, 321);
- end;
- 18:
- case Uppercase(Param)[1] of
- 'P':
- Sequencetype := 1;
- 'Z':
- Sequencetype := 0;
- 'N':
- Sequencetype := -1;
- else
- DoSimpleMsg('Unknown Sequence Type for "' + Class_Name + '.' + Name + '": ' + Param, 321);
- end;
- 19:
- SetBus(2, param);
- 20:
- Ztemp := InterpretComplex(Param);
- 21:
- Ztemp := InterpretComplex(Param);
- 22:
- Ztemp := InterpretComplex(Param);
- 23:
- puZ1 := InterpretComplex(Param);
- 24:
- puZ0 := InterpretComplex(Param);
- 25:
- puZ2 := InterpretComplex(Param);
- 26:
- BaseMVA := Parser.DblValue;
-
- 27:
- YearlyShape := Param;
- 28:
- DailyShape := Param;
- 29:
- DutyShape := Param;
- 30:
- IsQuasiIdeal := InterpretSourceModel(Param);
- 31:
- puZideal := InterpretComplex(Param);
- else
- ClassEdit(DSS.ActiveVsourceObj, ParamPointer - NumPropsThisClass)
+ Z1Specified := TRUE;
+ // default values for Z2, Z0
+ if not Z2Specified then
+ begin
+ R2 := R1;
+ X2 := X1;
end;
-
- case ParamPointer of
- 20:
- begin
- R1 := ZTemp.re;
- X1 := Ztemp.im;
- Z1Specified := TRUE;
- // default values for Z2, Z0
- if not Z2Specified then
- begin
- R2 := R1;
- X2 := X1;
- end;
- if not Z0Specified then
- begin
- R0 := R1;
- X0 := X1;
- end;
- end;
- 21:
- begin
- R0 := ZTemp.re;
- X0 := Ztemp.im;
- Z0Specified := TRUE;
- end;
- 22:
- begin
- R2 := ZTemp.re;
- X2 := Ztemp.im;
- Z2Specified := TRUE;
- end;
- 23:
- begin
- puZ1Specified := TRUE;
- // default values for Z2, Z0
- if not puZ2Specified then
- begin
- puZ2 := puZ1;
- end;
- if not puZ0Specified then
- begin
- puZ0 := puZ1;
- end;
- end;
- 24:
- puZ0Specified := TRUE;
- 25:
- puZ2Specified := TRUE;
- {Set shape objects; returns nil if not valid}
- {Sets the kW and kvar properties to match the peak kW demand from the Loadshape}
- 27:
- YearlyShapeObj := DSS.LoadShapeClass.Find(YearlyShape);
- 28:
- begin
- DailyShapeObj := DSS.LoadShapeClass.Find(DailyShape);
- {If Yearly load shape is not yet defined, make it the same as Daily}
- if YearlyShapeObj = NIL then
- YearlyShapeObj := DailyShapeObj;
- end;
- 29:
- DutyShapeObj := DSS.LoadShapeClass.Find(DutyShape);
+ if not Z0Specified then
+ begin
+ R0 := R1;
+ X0 := X1;
end;
-
- case ParamPointer of
- 13:
- R2 := R1;
- 14:
- X2 := X1;
+ end;
+ 21:
+ Z0Specified := TRUE;
+ 22:
+ Z2Specified := TRUE;
+ 23:
+ begin
+ puZ1Specified := TRUE;
+ // default values for Z2, Z0
+ if not puZ2Specified then
+ begin
+ puZ2 := puZ1;
end;
- // Set the Z spec type switch depending on which was specified.
- case ParamPointer of
- 7, 8:
- ZSpecType := 1; // MVAsc
- 11, 12:
- ZSpecType := 2; // Isc
-
- 13 .. 16:
- ZSpecType := 3; // Specified in Ohms
- 19:
- Bus2Defined := TRUE;
- 20..25:
- Zspectype := 3;
+ if not puZ0Specified then
+ begin
+ puZ0 := puZ1;
end;
+ end;
+ 24:
+ puZ0Specified := TRUE;
+ 25:
+ puZ2Specified := TRUE;
+ // Set shape objects; returns nil if not valid
+ // Sets the kW and kvar properties to match the peak kW demand from the Loadshape
+ 28:
+ begin
+ // If Yearly load shape is not yet defined, make it the same as Daily
+ if YearlyShapeObj = NIL then
+ YearlyShapeObj := DailyShapeObj;
+ end;
+ end;
- case ParamPointer of
- 2:
- ZBase := SQR(kvBase) / BaseMVA;
- 23:
- begin
- Z1Specified := TRUE;
- puZ1Specified := TRUE;
- end;
- 24:
- puZ0Specified := TRUE;
- 25:
- puZ2Specified := TRUE;
- 26:
- ZBase := SQR(kvBase) / BaseMVA;
- end;
+ // Set the Z spec type switch depending on which was specified.
+ case Idx of
+ 7, 8:
+ ZSpecType := 1; // MVAsc
+ 11, 12:
+ ZSpecType := 2; // Isc
+ 13 .. 16:
+ ZSpecType := 3; // Specified in Ohms
+ 19:
+ Bus2Defined := TRUE;
+ 20..25:
+ Zspectype := 3;
+ end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ case Idx of
+ 2:
+ ZBase := SQR(kvBase) / BaseMVA;
+ 23:
+ begin
+ Z1Specified := TRUE;
+ puZ1Specified := TRUE;
end;
+ 24:
+ puZ0Specified := TRUE;
+ 25:
+ puZ2Specified := TRUE;
+ 26:
+ ZBase := SQR(kvBase) / BaseMVA;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
+function TVsource.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
+ begin
RecalcElementData;
YPrimInvalid := TRUE;
+ Exclude(Flags, Flg.EditionActive);
end;
-
+ Result := True;
end;
-//----------------------------------------------------------------------------
-function TVsource.MakeLike(const OtherSource: String): Integer;
+procedure TVsourceObj.MakeLike(OtherPtr: Pointer);
var
- OtherVSource: TVSourceObj;
- i: Integer;
-
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this line name in the present collection}
- OtherVSource := Find(OtherSource);
- if OtherVSource <> NIL then
- with DSS.ActiveVsourceObj do
- begin
-
- if Fnphases <> OtherVSource.Fnphases then
- begin
- Nphases := OtherVSource.Fnphases;
- NConds := Fnphases; // Forces reallocation of terminal stuff
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ if Fnphases <> Other.Fnphases then
+ begin
+ FNphases := Other.Fnphases;
+ NConds := Fnphases; // Forces reallocation of terminal stuff
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
- if Z <> NIL then
- Z.Free;
- if Zinv <> NIL then
- Zinv.Free;
+ if Z <> NIL then
+ Z.Free;
+ if Zinv <> NIL then
+ Zinv.Free;
- Z := TCmatrix.CreateMatrix(Fnphases);
- Zinv := TCMatrix.CreateMatrix(Fnphases);
- end;
-
- Z.CopyFrom(OtherVSource.Z);
- // Zinv.CopyFrom(OtherLine.Zinv);
- VMag := OtherVsource.Vmag;
- kVBase := OtherVsource.kVBase;
- BaseMVA := OtherVsource.BaseMVA;
- PerUnit := OtherVsource.PerUnit;
- Angle := OtherVsource.Angle;
- MVAsc3 := OtherVsource.MVAsc3;
- MVAsc1 := OtherVsource.MVAsc1;
-
- Scantype := OtherVsource.Scantype;
- Sequencetype := OtherVsource.Sequencetype;
- SrcFrequency := OtherVsource.SrcFrequency;
-
- ZSpecType := OtherVsource.ZSpecType;
- R1 := OtherVsource.R1;
- X1 := OtherVsource.X1;
- R2 := OtherVsource.R2;
- X2 := OtherVsource.X2;
- R0 := OtherVsource.R0;
- X0 := OtherVsource.X0;
- X1R1 := OtherVsource.X1R1;
- X0R0 := OtherVsource.X0R0;
- BaseMVA := OtherVsource.BaseMVA;
- puZ1 := OtherVsource.puZ1;
- puZ0 := OtherVsource.puZ0;
- puZ2 := OtherVsource.puZ2;
- ZBase := OtherVsource.ZBase;
- Bus2Defined := OtherVsource.Bus2Defined;
- Z1Specified := OtherVsource.Z1Specified;
- Z2Specified := OtherVsource.Z2Specified;
- Z0Specified := OtherVsource.Z0Specified;
- puZ0Specified := OtherVsource.puZ0Specified;
- puZ1Specified := OtherVsource.puZ1Specified;
- puZ2Specified := OtherVsource.puZ2Specified;
- IsQuasiIdeal := OtherVsource.IsQuasiIdeal;
- puZideal := OtherVsource.puZideal;
-
- {Loadshape stuff}
- ShapeIsActual := OtherVsource.ShapeIsActual;
- DailyShape := OtherVsource.DailyShape;
- DailyShapeObj := OtherVsource.DailyShapeObj;
- DutyShape := OtherVsource.DutyShape;
- DutyShapeObj := OtherVsource.DutyShapeObj;
- YearlyShape := OtherVsource.YearlyShape;
- YearlyShapeObj := OtherVsource.YearlyShapeObj;
-
- ClassMakeLike(OtherVSource);
-
- for i := 1 to ParentClass.NumProperties do
- FPropertyValue[i] := OtherVsource.FPropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Vsource MakeLike: "' + OtherSource + '" Not Found.', 322);
+ Z := TCmatrix.CreateMatrix(Fnphases);
+ Zinv := TCMatrix.CreateMatrix(Fnphases);
+ end;
+ Z.CopyFrom(Other.Z);
+ // Zinv.CopyFrom(OtherLine.Zinv);
+ VMag := Other.Vmag;
+ kVBase := Other.kVBase;
+ BaseMVA := Other.BaseMVA;
+ PerUnit := Other.PerUnit;
+ Angle := Other.Angle;
+ MVAsc3 := Other.MVAsc3;
+ MVAsc1 := Other.MVAsc1;
+
+ Scantype := Other.Scantype;
+ Sequencetype := Other.Sequencetype;
+ SrcFrequency := Other.SrcFrequency;
+
+ ZSpecType := Other.ZSpecType;
+ R1 := Other.R1;
+ X1 := Other.X1;
+ R2 := Other.R2;
+ X2 := Other.X2;
+ R0 := Other.R0;
+ X0 := Other.X0;
+ X1R1 := Other.X1R1;
+ X0R0 := Other.X0R0;
+ BaseMVA := Other.BaseMVA;
+ puZ1 := Other.puZ1;
+ puZ0 := Other.puZ0;
+ puZ2 := Other.puZ2;
+ ZBase := Other.ZBase;
+ Bus2Defined := Other.Bus2Defined;
+ Z1Specified := Other.Z1Specified;
+ Z2Specified := Other.Z2Specified;
+ Z0Specified := Other.Z0Specified;
+ puZ0Specified := Other.puZ0Specified;
+ puZ1Specified := Other.puZ1Specified;
+ puZ2Specified := Other.puZ2Specified;
+ IsQuasiIdeal := Other.IsQuasiIdeal;
+ puZideal := Other.puZideal;
+
+ // Loadshape stuff
+ ShapeIsActual := Other.ShapeIsActual;
+ DailyShapeObj := Other.DailyShapeObj;
+ DutyShapeObj := Other.DutyShapeObj;
+ YearlyShapeObj := Other.YearlyShapeObj;
end;
-//=============================================================================
+
constructor TVsourceObj.Create(ParClass: TDSSClass; const SourceName: String);
begin
inherited create(ParClass);
- Name := LowerCase(SourceName);
+ Name := AnsiLowerCase(SourceName);
DSSObjType := ParClass.DSSClassType; //SOURCE + NON_PCPD_ELEM; // Don't want this in PC Element List
- Nphases := 3;
+ FNphases := 3;
Fnconds := 3;
Nterms := 2; // Now a 2-terminal device
Z := NIL;
Zinv := NIL;
- {Basefrequency := 60.0;} // set in base class
MVAsc3 := 2000.0;
MVAsc1 := 2100.0;
ZSpecType := 1; // default to MVAsc
@@ -707,26 +514,17 @@ constructor TVsourceObj.Create(ParClass: TDSSClass; const SourceName: String);
puZideal := Cmplx(1.0e-6, 0.001); // default ideal source pu impedance
- Spectrum := 'defaultvsource';
+ SpectrumObj := DSS.SpectrumClass.DefaultVSource;
ShapeIsActual := FALSE;
- YearlyShape := '';
- YearlyShapeObj := NIL; // IF YearlyShapeobj = nil THEN the Vsource alway stays nominal
- DailyShape := '';
- DailyShapeObj := NIL; // IF DaillyShapeobj = nil THEN the Vsource alway stays nominal
- DutyShape := '';
- DutyShapeObj := NIL; // IF DutyShapeobj = nil THEN the Vsource alway stays nominal
-
- InitPropertyValues(0);
-
+ YearlyShapeObj := NIL;
+ DailyShapeObj := NIL;
+ DutyShapeObj := NIL;
Yorder := Fnterms * Fnconds;
RecalcElementData;
-
end;
-
-//=============================================================================
destructor TVsourceObj.Destroy;
begin
Z.Free;
@@ -735,20 +533,6 @@ destructor TVsourceObj.Destroy;
inherited Destroy;
end;
-//----------------------------------------------------------------------------
-function TVsourceObj.InterpretSourceModel(const s: String): Boolean;
-
-// interpret Model type
-
-
-begin
- if CompareTextShortest(S, 'ideal') = 0 then
- Result := TRUE
- else
- Result := FALSE;
-end;
-
-//=============================================================================
procedure TVsourceObj.RecalcElementData;
var
Zs, Zm, Z1, Z2, Z0: Complex;
@@ -759,7 +543,6 @@ procedure TVsourceObj.RecalcElementData;
Factor: Double;
Rs, Xs, Rm, Xm: Double;
-
begin
if Z <> NIL then
Z.Free;
@@ -780,19 +563,19 @@ procedure TVsourceObj.RecalcElementData;
Xs := 0.1;
Xm := 0.0;
- {Calculate the short circuit impedance and make all other spec types agree}
+ // Calculate the short circuit impedance and make all other spec types agree
case ZSpecType of
1:
begin // MVAsc
X1 := Sqr(KvBase) / MVAsc3 / Sqrt(1.0 + 1.0 / Sqr(X1R1));
- // Xs := Sqr(KvBase) / MVAsc1/Sqrt(1.0 + 1.0/Sqr(X0R0)); // Approx
+ // Xs := Sqr(KvBase) / MVAsc1/Sqrt(1.0 + 1.0/Sqr(X0R0)); // Approx
R1 := X1 / X1R1;
R2 := R1; // default Z2 = Z1
X2 := X1;
Isc3 := MVAsc3 * 1000.0 / (SQRT3 * kVBase);
Isc1 := MVAsc1 * 1000.0 / (Factor * kVBase);
- // Compute R0, X0
+ // Compute R0, X0
R0 := QuadSolver((1.0 + SQR(X0R0)), (4.0 * (R1 + X1 * X0R0)), (4.0 * (R1 * R1 + X1 * X1) - SQR(3.0 * kVBase * 1000.0 / Factor / Isc1)));
X0 := R0 * X0R0;
@@ -806,14 +589,13 @@ procedure TVsourceObj.RecalcElementData;
2:
begin // Isc
-
MVAsc3 := SQRT3 * kVBase * Isc3 / 1000.0;
MVAsc1 := Factor * kVBase * Isc1 / 1000.0;
X1 := Sqr(KvBase) / MVAsc3 / Sqrt(1.0 + 1.0 / Sqr(X1R1));
R1 := X1 / X1R1;
R2 := R1; // default Z2 = Z1
X2 := X1;
- // Compute R0, X0
+ // Compute R0, X0
R0 := QuadSolver((1.0 + SQR(X0R0)), (4.0 * (R1 + X1 * X0R0)), (4.0 * (R1 * R1 + X1 * X1) - SQR(3.0 * kVBase * 1000.0 / Factor / Isc1)));
X0 := R0 * X0R0;
@@ -823,12 +605,10 @@ procedure TVsourceObj.RecalcElementData;
Rm := (R0 - R1) / 3.0;
Xm := (X0 - X1) / 3.0;
-
end;
3:
begin // Z1, Z2, Z0 Specified
-
// Compute Z1, Z2, Z0 in ohms if Z1 is specified in pu
if puZ1Specified then
begin
@@ -863,18 +643,14 @@ procedure TVsourceObj.RecalcElementData;
Rs := (2.0 * R1 + R0) / 3.0;
Rm := (R0 - R1) / 3.0;
-
end;
-
end;
- {Update property Value array}
- { Don't change a specified value; only computed ones}
-
-
+ // Update property Value array
+ // Don't change a specified value; only computed ones
if (R1 = R2) and (X1 = X2) then
begin
- // Symmetric Matrix Case
+ // Symmetric Matrix Case
Zs := cmplx(Rs, Xs);
Zm := cmplx(Rm, Xm);
@@ -889,40 +665,39 @@ procedure TVsourceObj.RecalcElementData;
end
else
begin
- // Asymmetric Matrix case where Z2 <> Z1
+ // Asymmetric Matrix case where Z2 <> Z1
Z1 := Cmplx(R1, X1);
Z2 := Cmplx(R2, X2);
Z0 := Cmplx(R0, X0);
- // Diagonals (all the same)
- Value := Cadd(Z2, Cadd(Z1, Z0)); // Z1 + Z2 + Z0
- Value := CdivReal(Value, 3.0);
+ // Diagonals (all the same)
+ Value := Z2 + Z1 + Z0;
+ Value := Value / 3.0;
for i := 1 to Fnphases do
Z.SetElement(i, i, Value);
- // Off-Diagonals
+ // Off-Diagonals
if FnPhases = 3 then // otherwise undefined
begin
-
// There are two possible off-diagonal elements if Z1 <> Z2
// Calpha is defined as 1 /_ -120 instead of 1 /_ 120
- Calpha1 := Conjg(Calpha); // Change Calpha to agree with textbooks
- Calpha2 := Cmul(Calpha1, Calpha1); // Alpha squared = 1 /_ 240 = 1/_-120
+ Calpha1 := cong(Calpha); // Change Calpha to agree with textbooks
+ Calpha2 := Calpha1 * Calpha1; // Alpha squared = 1 /_ 240 = 1/_-120
//(Z0 + aZ1 + a2 Z2)/3
- Value2 := Cadd(Cmul(Calpha2, Z2), Cadd(Cmul(Calpha1, Z1), Z0));
+ Value2 := Calpha2 * Z2 + Calpha1 * Z1 + Z0;
//(Z0 + a2 Z1 + aZ2)/3
- Value1 := Cadd(Cmul(Calpha2, Z1), Cadd(Cmul(Calpha1, Z2), Z0));
+ Value1 := Calpha2 * Z1 + Calpha1 * Z2 + Z0;
// Apply 1/3 ...
- Value1 := CdivReal(Value1, 3.0);
- Value2 := CdivReal(Value2, 3.0);
+ Value1 := Value1 / 3.0;
+ Value2 := Value2 / 3.0;
with Z do
begin
- //Lower Triangle
+ //Lower Triangle
SetElement(2, 1, Value1);
SetElement(3, 1, Value2);
SetElement(3, 2, Value1);
- //Upper Triangle
+ //Upper Triangle
SetElement(1, 2, Value2);
SetElement(1, 3, Value1);
SetElement(2, 3, Value2);
@@ -932,7 +707,7 @@ procedure TVsourceObj.RecalcElementData;
end;
- // if not specified, compute a value for for puZ1 for display in formedit
+ // if not specified, compute a value for for puZ1 for display in formedit
if not (puZ1Specified or puZ0Specified or puZ2Specified) and (Zbase > 0.0) then
begin
puZ1.re := R1 / Zbase;
@@ -950,53 +725,16 @@ procedure TVsourceObj.RecalcElementData;
Vmag := kVBase * PerUnit * 1000.0 / 2.0 / Sin((180.0 / Fnphases) * PI / 180.0);
end;
- SpectrumObj := DSS.SpectrumClass.Find(Spectrum);
- if SpectrumObj = NIL then
- begin
- DoSimpleMsg('Spectrum Object "' + Spectrum + '" for Device Vsource.' + Name + ' Not Found.', 324);
- end;
-
- {Now check for errors. If any of these came out nil and the string was not nil, give warning}
- if CompareText(YearlyShape, 'none') = 0 then
- YearlyShape := '';
- if CompareText(DailyShape, 'none') = 0 then
- DailyShape := '';
- if CompareText(DutyShape, 'none') = 0 then
- DutyShape := '';
- if YearlyShapeObj = NIL then
- if Length(YearlyShape) > 0 then
- DoSimpleMsg('WARNING! Vsource Yearly load shape: "' + YearlyShape + '" Not Found.', 34583);
- if DailyShapeObj = NIL then
- if Length(DailyShape) > 0 then
- DoSimpleMsg('WARNING! Vsource Daily load shape: "' + DailyShape + '" Not Found.', 34584);
- if DutyShapeObj = NIL then
- if Length(DutyShape) > 0 then
- DoSimpleMsg('WARNING! Vsource Duty load shape: "' + DutyShape + '" Not Found.', 34585);
-
Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
-
end;
-//=============================================================================
procedure TVsourceObj.CalcYPrim;
-
var
Value: Complex;
i, j: Integer;
FreqMultiplier: Double;
-
- function TorF(Value: Boolean): String;
- begin
- if Value then
- Result := 'T'
- else
- Result := 'F';
- end;
-
-
begin
-
- // Build only YPrim Series
+ // Build only YPrim Series
if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Series = NIL) {YPrimInvalid} then
begin
if YPrim_Series <> NIL then
@@ -1017,45 +755,46 @@ procedure TVsourceObj.CalcYPrim;
FYprimFreq := Frequency;
FreqMultiplier := FYprimFreq / BaseFrequency;
- {**** Quasi Ideal Source for fundamental power flow****}
+ // **** Quasi Ideal Source for fundamental power flow****
if ((FreqMultiplier - 1.0) < Epsilon) and IsQuasiIdeal and (not IsHarmonicModel) then
begin
// Ideal Source approximation -- impedance matrix is diagonal matrix only
Zinv.Clear;
- Value := CmulReal(puZIdeal, Zbase); // convert to ohms
+ Value := puZIdeal * Zbase; // convert to ohms
for i := 1 to Fnphases do
Zinv.SetElement(i, i, value);
end
else
- {**** Normal Thevenin Source ****}
- { Put in Series RL Adjusted for frequency -- Use actual values }
+ // **** Normal Thevenin Source ****
+ // Put in Series RL Adjusted for frequency -- Use actual values
for i := 1 to Fnphases do
begin
for j := 1 to Fnphases do
begin
Value := Z.GetElement(i, j);
- Value.im := Value.im * FreqMultiplier; {Modify from base freq}
+ Value.im := Value.im * FreqMultiplier; // Modify from base freq
Zinv.SetElement(i, j, value);
end;
end;
end;
- Zinv.Invert; {Invert in place}
+ Zinv.Invert; // Invert in place
if Zinv.InvertError > 0 then
- begin {If error, put in Large series conductance}
- DoErrorMsg('TVsourceObj.CalcYPrim', 'Matrix Inversion Error for Vsource "' + Name + '"',
- 'Invalid impedance specified. Replaced with small resistance.', 325);
+ begin // If error, put in Large series conductance
+ DoErrorMsg('TVsourceObj.CalcYPrim',
+ Format(_('Matrix Inversion Error for Vsource "%s"'), [Name]),
+ _('Invalid impedance specified. Replaced with small resistance.'), 325);
Zinv.Clear;
for i := 1 to Fnphases do
Zinv.SetElement(i, i, Cmplx(1.0 / EPSILON, 0.0));
end;
- // YPrim_Series.CopyFrom(Zinv);
+ // YPrim_Series.CopyFrom(Zinv);
for i := 1 to FNPhases do
begin
@@ -1064,52 +803,41 @@ procedure TVsourceObj.CalcYPrim;
Value := Zinv.GetElement(i, j);
YPrim_series.SetElement(i, j, Value);
YPrim_series.SetElement(i + FNPhases, j + FNPhases, Value);
- //YPrim_series.SetElemsym(i + FNPhases, j, CNegate(Value))
- YPrim_series.SetElement(i, j + Fnphases, Cnegate(Value));
- YPrim_series.SetElement(i + Fnphases, j, Cnegate(Value));
+ //YPrim_series.SetElemsym(i + FNPhases, j, -Value)
+ YPrim_series.SetElement(i, j + Fnphases, -Value);
+ YPrim_series.SetElement(i + Fnphases, j, -Value);
end;
end;
YPrim.CopyFrom(YPrim_Series);
- {Now Account for Open Conductors}
- {For any conductor that is open, zero out row and column}
+ // Now Account for Open Conductors
+ // For any conductor that is open, zero out row and column
inherited CalcYPrim;
YPrimInvalid := FALSE;
-
end;
-//=============================================================================
procedure TVsourceObj.GetVterminalForSource;
-
var
i: Integer;
Vharm: Complex;
SrcHarmonic: Double;
-
begin
-
try
-
- {
- This formulation will theoretically handle voltage sources of
- any number of phases assuming they are
- equally displaced in time.
- }
-
-
+ // This formulation will theoretically handle voltage sources of
+ // any number of phases assuming they are
+ // equally displaced in time.
with ActiveCircuit.Solution do
begin
-
ShapeIsActual := FALSE;
- {Modify magnitude based on a LOADSHAPE if assigned}
+ // Modify magnitude based on a LOADSHAPE if assigned
case Mode of
- {Uses same logic as LOAD}
+ // Uses same logic as LOAD
TSolveMode.DAILYMODE:
begin
- CalcDailyMult(DynaVars.dblHour);
+ CalcDailyMult(DynaVars.dblHour); // set Shapefactor.re = Pmult(t) or PerUnit
end;
TSolveMode.YEARLYMODE:
begin
@@ -1119,15 +847,32 @@ procedure TVsourceObj.GetVterminalForSource;
begin
CalcDutyMult(DynaVars.dblHour);
end;
+ TSolveMode.DYNAMICMODE:
+ begin
+ // This mode allows use of one class of load shape in DYNAMIC mode
+ // Sets Shapefactor.re = pmult(t) or PerUnit value
+ case ActiveCircuit.ActiveLoadShapeClass of
+ USEDAILY:
+ CalcDailyMult(DynaVars.dblHour);
+ USEYEARLY:
+ CalcYearlyMult(DynaVars.dblHour);
+ USEDUTY:
+ CalcDutyMult(DynaVars.dblHour);
+ else
+ ShapeFactor := Cmplx(PerUnit, 0.0); // default to PerUnit + j0 if not known
+ end;
+ end;
end;
- if (Mode = TSolveMode.DAILYMODE) or {If a loadshape mode simulation}
+ if (Mode = TSolveMode.DAILYMODE) or // If a loadshape mode simulation
(Mode = TSolveMode.YEARLYMODE) or
- (Mode = TSolveMode.DUTYCYCLE) then
- begin {Loadshape cases}
+ (Mode = TSolveMode.DUTYCYCLE) or
+ (Mode = TSolveMode.DYNAMICMODE) then
+ begin // Loadshape cases
if ShapeIsActual then
Vmag := 1000.0 * ShapeFactor.re // assumes actual L-N voltage or voltage across source
else
+ // is pu value
case Fnphases of
1:
Vmag := kVBase * ShapeFactor.re * 1000.0;
@@ -1145,9 +890,8 @@ procedure TVsourceObj.GetVterminalForSource;
if IsHarmonicModel then
begin
-
SrcHarmonic := Frequency / SrcFrequency;
- Vharm := CMulReal(SpectrumObj.GetMult(SrcHarmonic), Vmag); // Base voltage for this harmonic
+ Vharm := SpectrumObj.GetMult(SrcHarmonic) * Vmag; // Base voltage for this harmonic
RotatePhasorDeg(Vharm, SrcHarmonic, Angle); // Rotate for phase 1 shift
for i := 1 to Fnphases do
begin
@@ -1168,10 +912,9 @@ procedure TVsourceObj.GetVterminalForSource;
end
else
begin // non-harmonic modes
-
if abs(Frequency - SrcFrequency) > EPSILON2 then
Vmag := 0.0; // Solution Frequency and Source Frequency don't match!
- {NOTE: RE-uses VTerminal space}
+ // NOTE: RE-uses VTerminal space
for i := 1 to Fnphases do
begin
case Sequencetype of
@@ -1191,86 +934,61 @@ procedure TVsourceObj.GetVterminalForSource;
end;
except
- DoSimpleMsg('Error computing Voltages for Vsource.' + Name + '. Check specification. Aborting.', 326);
+ DoSimpleMsg('Error computing Voltages for "%s". Check specification. Aborting.', [FullName], 326);
if DSS.In_Redirect then
DSS.Redirect_Abort := TRUE;
end;
-
end;
-//===========================================================================
-
function TVsourceObj.InjCurrents: Integer;
-
begin
-
GetInjCurrents(InjCurrent);
-
-{This is source injection}
-
+ // This is source injection
Result := inherited InjCurrents; // Add into system array
-
end;
-//===========================================================================
procedure TVsourceObj.GetCurrents(Curr: pComplexArray);
-
var
i: Integer;
-
begin
try
with ActiveCircuit.Solution do
begin
- //FOR i := 1 TO (Nterms * NConds) DO Vtemp^[i] := V^[NodeRef^[i]];
- // This is safer 12/7/99
- for i := 1 to Yorder do
+ for i := 1 to Yorder do
Vterminal^[i] := NodeV^[NodeRef^[i]];
YPrim.MVMult(Curr, Vterminal); // Current from Elements in System Y
GetInjCurrents(ComplexBuffer); // Get present value of inj currents
- // Add Together with yprim currents
+ // Add together with yprim currents
for i := 1 to Yorder do
- Curr^[i] := Csub(Curr^[i], ComplexBuffer^[i]);
-
- end; {With}
+ Curr^[i] := Curr^[i] - ComplexBuffer^[i];
+ end;
except
On E: Exception do
- DoErrorMsg(('GetCurrents for Element: ' + Name + '.'), E.Message,
- 'Inadequate storage allotted for circuit element.', 327);
+ DoErrorMsg(Format(_('GetCurrents for Element: %s.'), [Name]), E.Message,
+ _('Inadequate storage allotted for circuit element.'), 327);
end;
-
end;
-
-//=============================================================================
procedure TVsourceObj.GetInjCurrents(Curr: pComplexArray);
-
begin
-
- { source injection currents given by this formula:
- _ _ _ _
- |Iinj1| |Vsource |
- | | = [Yprim] | |
- |Iinj2| | 0 |
- _ _ _ _
- }
-
+ // source injection currents given by this formula:
+ // _ _ _ _
+ // |Iinj1| |Vsource |
+ // | | = [Yprim] | |
+ // |Iinj2| | 0 |
+ // _ _ _ _
GetVterminalForSource; // gets voltage vector above
YPrim.MVMult(Curr, Vterminal);
ITerminalUpdated := FALSE;
-
end;
-//=============================================================================
-procedure TVsourceObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
+procedure TVsourceObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i, j: Integer;
c: Complex;
-
begin
inherited DumpProperties(F, Complete);
@@ -1296,104 +1014,9 @@ procedure TVsourceObj.DumpProperties(F: TFileStream; Complete: Boolean);
FSWriteln(F);
end;
end;
-
end;
-
-//=============================================================================
-procedure TVsourceObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- {PropertyValue Allocated in DSSObject.Create}
- PropertyValue[1] := GetBus(1);
- PropertyValue[2] := '115';
- PropertyValue[3] := '1';
- PropertyValue[4] := '0';
- PropertyValue[5] := Format('%d', [Round(ActiveCircuit.Fundamental)]);
- PropertyValue[6] := '3';
- PropertyValue[7] := '2000';
- PropertyValue[8] := '2100';
- PropertyValue[9] := '4';
- PropertyValue[10] := '3';
- PropertyValue[11] := '10000';
- PropertyValue[12] := '10500';
- PropertyValue[13] := '1.65';
- PropertyValue[14] := '6.6';
- PropertyValue[15] := '1.9';
- PropertyValue[16] := '5.7';
- PropertyValue[17] := 'Pos';
- PropertyValue[18] := 'Pos';
- PropertyValue[19] := GetBus(2);
- PropertyValue[20] := '[ 0 0 ]';
- PropertyValue[21] := '[ 0 0 ]';
- PropertyValue[22] := '[ 0 0 ]';
- PropertyValue[23] := '[ 0 0 ]';
- PropertyValue[24] := '[ 0 0 ]';
- PropertyValue[25] := '[ 0 0 ]';
- PropertyValue[26] := '100';
- PropertyValue[27] := '';
- PropertyValue[28] := '';
- PropertyValue[29] := '';
- PropertyValue[30] := 'Thevenin';
- PropertyValue[31] := '[1.0e-6, 0.001]';
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-//=============================================================================
-function TVsourceObj.GetPropertyValue(Index: Integer): String;
-begin
- case Index of
- 1:
- Result := GetBus(1);
- 4:
- Result := Format('%-.5g',[Angle]);
- 7:
- Result := Format('%-.5g', [MVAsc3]);
- 8:
- Result := Format('%-.5g', [MVAsc1]);
- 11:
- Result := Format('%-.5g', [Isc3]);
- 12:
- Result := Format('%-.5g', [Isc1]);
- 13:
- Result := Format('%-.5g', [R1]);
- 14:
- Result := Format('%-.5g', [X1]);
- 15:
- Result := Format('%-.5g', [R0]);
- 16:
- Result := Format('%-.5g', [X0]);
- 19:
- Result := GetBus(2);
- 20:
- Result := Format('[%-.8g, %-.8g]', [R1, X1]);
- 21:
- Result := Format('[%-.8g, %-.8g]', [R0, X0]);
- 22:
- Result := Format('[%-.8g, %-.8g]', [R2, X2]);
- 23:
- Result := Format('[%-.8g, %-.8g]', [puZ1.re, puZ1.im]);
- 24:
- Result := Format('[%-.8g, %-.8g]', [puZ0.re, puZ0.im]);
- 25:
- Result := Format('[%-.8g, %-.8g]', [puZ2.re, puZ2.im]);
- 26:
- Result := Format('%-.5g', [BaseMVA]);
- 31:
- Result := Format('[%-.8g, %-.8g]', [puZideal.re, puZideal.im]);
-
- else
- Result := inherited GetPropertyValue(Index);
- end;
-end;
-
-
-//----------------------------------------------------------------------------
procedure TVSourceObj.CalcDailyMult(Hr: Double);
-
begin
if DailyShapeObj <> NIL then
begin
@@ -1401,13 +1024,10 @@ procedure TVSourceObj.CalcDailyMult(Hr: Double);
ShapeIsActual := DailyShapeObj.UseActual;
end
else
- ShapeFactor := cmplx(PerUnit, 0.0); // CDOUBLEONE; // Default to no daily variation
+ ShapeFactor := cmplx(PerUnit, 0.0); // Default to no daily variation
end;
-
-//----------------------------------------------------------------------------
procedure TVSourceObj.CalcDutyMult(Hr: Double);
-
begin
if DutyShapeObj <> NIL then
begin
@@ -1418,37 +1038,33 @@ procedure TVSourceObj.CalcDutyMult(Hr: Double);
CalcDailyMult(Hr); // Default to Daily Mult IF no duty curve specified
end;
-//----------------------------------------------------------------------------
procedure TVSourceObj.CalcYearlyMult(Hr: Double);
-
begin
-{Yearly curve is assumed to be hourly only}
+ // Yearly curve is assumed to be hourly only
if YearlyShapeObj <> NIL then
begin
ShapeFactor := YearlyShapeObj.GetMultAtHour(Hr);
ShapeIsActual := YearlyShapeObj.UseActual;
end
else
- ShapeFactor := cmplx(PerUnit, 0.0); // CDOUBLEONE; // Defaults to no variation
+ ShapeFactor := cmplx(PerUnit, 0.0); // Defaults to no variation
end;
-//=============================================================================
-procedure TVsourceObj.MakePosSequence;
-
+procedure TVsourceObj.MakePosSequence();
var
- S: String;
+ R1new, X1new, kVnew: Double;
begin
-
- S := 'Phases=1 ';
- S := S + Format('BasekV=%-.5g ', [kVbase / SQRT3]);
- S := S + Format('R1=%-.5g ', [R1]);
- S := S + Format('X1=%-.5g ', [X1]);
-
- Parser.CmdString := S;
- Edit;
-
+ R1new := R1;
+ X1new := X1;
+ kVnew := kVbase / SQRT3;
+ BeginEdit(True);
+ SetInteger(ord(TProp.Phases), 1);
+ SetDouble(ord(TProp.basekv), kVnew);
+ SetDouble(ord(TProp.R1), R1new);
+ SetDouble(ord(TProp.X1), X1new);
+ EndEdit(4);
inherited;
-
end;
+finalization ModelEnum.Free;
end.
diff --git a/src/PCElements/generator.pas b/src/PCElements/generator.pas
index 59cf74d03..af78c298a 100644
--- a/src/PCElements/generator.pas
+++ b/src/PCElements/generator.pas
@@ -7,80 +7,45 @@
----------------------------------------------------------
}
-{ Change Log
-
- 11/30/99 Added new properties to support conventional load flow
- Vset, Qmin, Qmax
- 12/1/99 Split out ComputeYsc(ibus)
- Added Code to estimate DQDV
- 12/2/99 Fixed bug in CalcYPrimMatrix - same bug as was in Load
- 12/6/99 revised 95% - 105% limits - same as Load
- 1-8-00 made voltage limites variable just like the Load. Added vminpu
- and vmaxpu properties and modified YEq95, etc.
- 2-2-00 Trapezoidal integration option
- 2-28-00 Corrected Errors in Take Sample function
- 8-23-00 Added FixedQ models; Added Price register and related dispatchmode
- 8-24-00 Fixed Pnominalperphase so that it is never 0.0 to avoid divide by zero error
- 9-20-00 Added InitStateVars Function for Dynamics mode
- 10-6-00 Fixed error in TakeSample for positive sequence model
- 10-25-00 Added Spectrum and code for Harmonic mode analysis
- 10-27-00 Deleted GetCurrents Override;
- 3-7-01 Fixed bug related to setting kvar= (Index wrong)
- 3-27-01 Added check to prevent divide by zero on calculation of PFNominal
- 5-17-01 moved spectrum editing back to base class
- 7-2-01 Corrected TakeSample to integrate only when GenON instead of S>0
- Also corrected kVA Max for Positive Seq only
- 8-14-01 Added price signal integration, which had been omitted
- Fixed TakeSample so it would integrate on Trapezoidal when not GenON
- 1-17/02 Fixed sign error for Type 5 model.
- 7/11/02 Added code to change Yprim when generator changes ON/OFF state
- 7/30/02 Fixed problem with propertyvalues and maxkvar
- 11/08/02 Added Dynamics model
- 11/11/02 Add user-written exciter and Shaft Models
- 3/6/03 Revised user-written dll interface.
- added control terminal code for PCELement override.
- 3-17-03 Revised user-written models and harmonic models
- 5-11-09 Added properties to support kW, kvar, PV, and kV through COM
- 8-28-13 Forced re-initializing solution if Model 3 generator added.
- 7-??-18 Corrected Generator Model 7 1-phase Model
-}
-{
- The generator is essentially a negative load that can be dispatched.
-
- If the dispatch value (DispValue) is 0, the generator always follows the
- appropriate dispatch curve, which are simply load curves. If DispValue>0 then
- the generator only comes on when the global circuit load multiplier exceeds
- DispValue. When the generator is on, it always follows the dispatch curve
- appropriate for the type of solution being performed.
-
- If you want to model a generator that is fully on whenever it is dispatched on,
- simply designate "Status=Fixed". The default is "Status=Variable" (i.e., it follows
- a dispatch curve. You could also define a dispatch curve that is always 1.0.
-
- Generators have their own energy meters that record:
- 1. Total kwh
- 2. Total kvarh
- 3. Max kW
- 4. Max kVA
- 5. Hours in operation
- 6. Price * kwH
-
- Generator meters reset with the circuit energy meters and take a sample with
- the circuit energy meters as well. The Energy meters also used trapezoidal integration
- so that they are compatible with Load-Duration simulations.
-
- Generator models are:
- 1. Constant P, Q (* dispatch curve, if appropriate).
- 2. Constant Z (For simple solution)
- 3. Constant P, |V| like a standard power flow
- 4. Constant P, Fixed Q (vars)
- 5. Constant P, Fixed Q (reactance)
- 6. User model
- 7. Approximate Inverter model
-
- Most of the time you will use #1 for planning studies.
+// Change Log
+// 8-28-13 Forced re-initializing solution if Model 3 generator added.
+// 7-??-18 Corrected Generator Model 7 1-phase Model
+
+// The generator is essentially a negative load that can be dispatched.
+//
+// If the dispatch value (DispValue) is 0, the generator always follows the
+// appropriate dispatch curve, which are simply load curves. If DispValue>0 then
+// the generator only comes on when the global circuit load multiplier exceeds
+// DispValue. When the generator is on, it always follows the dispatch curve
+// appropriate for the type of solution being performed.
+//
+// If you want to model a generator that is fully on whenever it is dispatched on,
+// simply designate "Status=Fixed". The default is "Status=Variable" (i.e., it follows
+// a dispatch curve. You could also define a dispatch curve that is always 1.0.
+//
+// Generators have their own energy meters that record:
+// 1. Total kwh
+// 2. Total kvarh
+// 3. Max kW
+// 4. Max kVA
+// 5. Hours in operation
+// 6. Price * kwH
+//
+// Generator meters reset with the circuit energy meters and take a sample with
+// the circuit energy meters as well. The Energy meters also used trapezoidal integration
+// so that they are compatible with Load-Duration simulations.
+//
+// Generator models are:
+// 1. Constant P, Q (* dispatch curve, if appropriate).
+// 2. Constant Z (For simple solution)
+// 3. Constant P, |V| like a standard power flow
+// 4. Constant P, Fixed Q (vars)
+// 5. Constant P, Fixed Q (reactance)
+// 6. User model
+// 7. Approximate Inverter model
+//
+// Most of the time you will use #1 for planning studies.
-}
// The Generator is assumed balanced over the no. of phases defined
@@ -93,15 +58,14 @@ interface
uses
Classes,
- GeneratorVars,
GenUserModel,
DSSClass,
PCClass,
PCElement,
ucmatrix,
- ucomplex,
+ UComplex, DSSUcomplex,
LoadShape,
- GrowthShape,
+ // GrowthShape,
Spectrum,
ArrayDef,
Dynamics;
@@ -111,34 +75,114 @@ interface
NumGenVariables = 6;
type
+{$SCOPEDENUMS ON}
+ TGeneratorProp = (
+ INVALID = 0,
+ phases,
+ bus1,
+ kv,
+ kW,
+ pf,
+ kvar, // 13
+ model, // 6
+ Vminpu, // 23
+ Vmaxpu, // 24
+ yearly, // 7
+ daily, // 8
+ duty, // 9
+ dispmode, // 10
+ dispvalue, // 11
+ conn, // 12
+ status, // 16
+ cls, // 17
+ Vpu, // 18
+ maxkvar, // 19
+ minkvar, // 20
+ pvfactor, // 21
+ forceon, // 25
+ kVA, // 26
+ MVA, // 27
+ Xd, // 28
+ Xdp, // 29
+ Xdpp, // 30
+ H, // 31
+ D, // 32
+ UserModel, // 33
+ UserData, // 34
+ ShaftModel, // 35
+ ShaftData, // 36
+ DutyStart, // 37
+ debugtrace, // 22
+ Balanced,
+ XRdp,
+ UseFuel,
+ FuelkWh,
+ pctFuel,
+ pctReserve,
+ Refuel
+ );
+{$SCOPEDENUMS OFF}
+ // Generator public data/state variable structure
+ TGeneratorVars = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}packed{$ENDIF} record
+
+ Theta, {Direct-Axis voltage magnitude & angle}
+ Pshaft,
+ Speed,
+ w0, {present Shaft Power and relative Speed, rad/sec, difference from Synchronous speed, w0}
+ {actual speed = Speed + w0}
+ Hmass, {Per unit mass constant}
+ Mmass, {Mass constant actual values (Joule-sec/rad}
+ D, Dpu, {Actual and per unit damping factors}
+ kVArating,
+ kVGeneratorBase,
+ Xd, Xdp, Xdpp, {machine Reactances, ohms}
+ puXd, puXdp, puXdpp, {machine Reactances, per unit}
+ dTheta,
+ dSpeed, {Derivatives of Theta and Speed}
+ ThetaHistory,
+ SpeedHistory, {history variables for integration}
+ Pnominalperphase,
+ Qnominalperphase {Target P and Q for power flow solution, watts, vars}: Double; { All Doubles }
+
+ {32-bit integers}
+ NumPhases, {Number of phases}
+ NumConductors, {Total Number of conductors (wye-connected will have 4)}
+ Conn: Integer; // 0 = wye; 1 = Delta
+
+ { Revisons (additions) to structure ...
+ Later additions are appended to end of the structure so that
+ previously compiled DLLs do not break
+ }
+
+ VthevMag: Double; {Thevinen equivalent voltage for dynamic model}
+ VThevHarm: Double; {Thevinen equivalent voltage mag reference for Harmonic model}
+ ThetaHarm: Double; {Thevinen equivalent voltage angle reference for Harmonic model}
+ VTarget: Double; // Target voltage for generator with voltage control
+ Zthev: Complex;
+ XRdp: Double; // Assumed X/R for Xd'
+ end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TGenerator = class(TPCClass)
- PRIVATE
-
- procedure InterpretConnection(const S: String);
- procedure SetNcondsForConnection;
PROTECTED
- procedure DefineProperties;
- function MakeLike(const OtherGeneratorName: String): Integer; OVERRIDE;
+ cBuffer: TCBuffer24; // Temp buffer for calcs 24-phase generator?
+
+ procedure DefineProperties; override;
PUBLIC
RegisterNames: array[1..NumGenregisters] of String;
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
procedure ResetRegistersAll;
procedure SampleAll;
-
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TGeneratorObj = class(TPCElement)
PRIVATE
-// Moved to GeneratorVars Zthev :Complex;
+ // Moved to GeneratorVars Zthev :Complex;
Yeq: Complex; // at nominal
Yeq95: Complex; // at 95%
Yeq105: Complex; // at 105%
@@ -147,15 +191,14 @@ TGeneratorObj = class(TPCElement)
PhaseCurrentLimit: Complex;
Model7MaxPhaseCurr: Double;
Model7LastAngle: Double;
- DebugTrace: Boolean;
+ DebugTrace: LongBool;
DeltaQMax: Double; // Max allowable var change on Model=3 per iteration
DispatchMode: Integer;
DispatchValue: Double;
DQDV: Double;
DQDVSaved: Double;
- FForcedON: Boolean;
FirstSampleAfterReset: Boolean;
- IsFixed: Boolean; // if Fixed, always at base value
+ IsFixed: LongBool; // if Fixed, always at base value
GeneratorSolutionCount: Integer;
GenFundamental: Double; {Thevinen equivalent voltage mag and angle reference for Harmonic model}
GenON: Boolean; {Indicates whether generator is currently on}
@@ -176,6 +219,7 @@ TGeneratorObj = class(TPCElement)
// moved to GeneratorVars Thetaharm :Double; {Thevinen equivalent voltage angle reference for Harmonic model}
TraceFile: TFileStream;
UserModel, ShaftModel: TGenUserModel; {User-Written Models}
+ UserModelNameStr, UserModelEditStr, ShaftModelNameStr, ShaftModelEditStr: String;
V_Avg: Double;
V_Remembered: Double;
var_Remembered: Double;
@@ -191,7 +235,7 @@ TGeneratorObj = class(TPCElement)
YPrimOpenCond: TCmatrix; // To handle cases where one conductor of load is open ; We revert to admittance for inj currents
YQFixed: Double; // Fixed value of y for type 7 load
ShapeIsActual: Boolean;
- ForceBalanced: Boolean;
+ ForceBalanced: LongBool;
procedure CalcDailyMult(Hr: Double);
procedure CalcDutyMult(Hr: Double); // now incorporates DutyStart offset
@@ -228,7 +272,6 @@ TGeneratorObj = class(TPCElement)
function Get_Presentkvar: Double;
function Get_PresentkV: Double;
procedure Set_PresentkV(const Value: Double);
- procedure Set_Presentkvar(const Value: Double);
procedure Set_PresentkW(const Value: Double);
procedure Set_PowerFactor(const Value: Double);
@@ -238,12 +281,10 @@ TGeneratorObj = class(TPCElement)
procedure GetTerminalCurrents(Curr: pComplexArray); OVERRIDE;
PUBLIC
-
Connection: Integer; {0 = line-neutral; 1=Delta}
- DailyDispShape: String; // Daily (24 HR) Generator shape
DailyDispShapeObj: TLoadShapeObj; // Daily Generator Shape for this load
- DutyShape: String; // Duty cycle load shape for changes typically less than one hour
DutyShapeObj: TLoadShapeObj; // Shape for this generator
+ YearlyShapeObj: TLoadShapeObj; // Shape for this Generator
DutyStart: Double; // starting time offset into the DutyShape [hrs] for this generator
GenClass: Integer;
GenModel: Integer; // Variation with voltage
@@ -257,21 +298,21 @@ TGeneratorObj = class(TPCElement)
Vmaxpu: Double;
Vminpu: Double;
+ ForcedON: LongBool;
+
// Fuel related variables
- GenActive,
- UseFuel: Boolean;
+ GenActive: Boolean;
+ UseFuel: LongBool;
FuelkWh,
pctFuel,
pctReserve: Double;
-// moved to GeneratorVars VTarget :Double; // Target voltage for generator with voltage control
- YearlyShape: String; // ='fixed' means no variation on all the time
- YearlyShapeObj: TLoadShapeObj; // Shape for this Generator
-
Registers, Derivatives: array[1..NumGenregisters] of Double;
constructor Create(ParClass: TDSSClass; const SourceName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
@@ -304,29 +345,24 @@ TGeneratorObj = class(TPCElement)
// Support for Harmonics Mode
procedure InitHarmonics; OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
property PresentkW: Double READ Get_PresentkW WRITE Set_PresentkW;
- property Presentkvar: Double READ Get_Presentkvar WRITE Set_Presentkvar;
- property ForcedON: Boolean READ FForcedON WRITE FForcedON;
- property PresentkV: Double READ Get_PresentkV WRITE Set_PresentkV;
property PowerFactor: Double READ PFNominal WRITE Set_PowerFactor;
+ //TODO: remove?
+ property PresentkV: Double READ Get_PresentkV WRITE Set_PresentkV;
+ property Presentkvar: Double READ Get_Presentkvar;
+
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
implementation
-
uses
- ParserDel,
+ BufStream,
Circuit,
Sysutils,
- Command,
+ // Command,
Math,
MathUtil,
DSSClassDefs,
@@ -336,195 +372,177 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TGeneratorObj;
+ TProp = TGeneratorProp;
const
- NumPropsThisClass = 44;
- // Dispatch modes
- DEFAULT = 0;
+ NumPropsThisClass = Ord(High(TProp));
+ // Dispatch modes
+ // DEFAULT = 0;
LOADMODE = 1;
PRICEMODE = 2;
-
var
- cBuffer: array[1..24] of Complex; // Temp buffer for calcs 24-phase generator?
- CDOUBLEONE: Complex;
-// TwoPI3:Double;
+ PropInfo: Pointer = NIL;
+ GenStatusEnum, GenDispModeEnum: TDSSEnum;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TGenerator.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TGenerator.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'Generator';
- DSSClassType := DSSClassType + GEN_ELEMENT; // In both PCelement and Genelement list
-
- ActiveElement := 0;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ GenDispModeEnum := TDSSEnum.Create('Generator: Dispatch Mode', True, 1, 1,
+ ['Default', 'LoadLevel', 'Price'], [0, 1, 2]
+ );
+ GenDispModeEnum.DefaultValue := 0;
+ GenStatusEnum := TDSSEnum.Create('Generator: Status', True, 1, 1,
+ ['Variable', 'Fixed'], [0, Integer(True)]);
+ GenStatusEnum.DefaultValue := 0;
+ end;
- // Set Register names
+ inherited Create(dssContext, GEN_ELEMENT, 'Generator');
+
+ // Set Register names
RegisterNames[1] := 'kWh';
RegisterNames[2] := 'kvarh';
RegisterNames[3] := 'Max kW';
RegisterNames[4] := 'Max kVA';
RegisterNames[5] := 'Hours';
RegisterNames[6] := '$';
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TGenerator.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
+end;
+procedure DoRefuel(Obj: TObj);
+begin
+ Obj.pctFuel := 100.0;
+ Obj.GenActive := True;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TGenerator.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays; {see DSSClass}
-
- // Define Property names
- AddProperty('phases', 1, 'Number of Phases, this Generator. Power is evenly divided among phases.');
- AddProperty('bus1', 2, 'Bus to which the Generator is connected. May include specific node specification.');
- AddProperty('kv', 3, 'Nominal rated (1.0 per unit) voltage, kV, for Generator. For 2- and 3-phase Generators, specify phase-phase kV. ' +
- 'Otherwise, for phases=1 or phases>3, specify actual kV across each branch of the Generator. ' +
- 'If wye (star), specify phase-neutral kV. ' +
- 'If delta or phase-phase connected, specify phase-phase kV.'); // line-neutral voltage// base voltage
- AddProperty('kW', 4, 'Total base kW for the Generator. A positive value denotes power coming OUT of the element, ' + CRLF +
- 'which is the opposite of a load. This value is modified depending on the dispatch mode. ' +
- 'Unaffected by the global load multiplier and growth curves. ' +
- 'If you want there to be more generation, you must add more generators or change this value.');
- AddProperty('pf', 5, 'Generator power factor. Default is 0.80. Enter negative for leading powerfactor ' +
- '(when kW and kvar have opposite signs.)' + CRLF +
- 'A positive power factor for a generator signifies that the generator produces vars ' + CRLF +
- 'as is typical for a synchronous generator. Induction machines would be ' + CRLF +
- 'specified with a negative power factor.');
- AddProperty('kvar', 13, 'Specify the base kvar. Alternative to specifying the power factor. Side effect: ' +
- ' the power factor value is altered to agree based on present value of kW.');
- AddProperty('model', 6, 'Integer code for the model to use for generation variation with voltage. ' +
- 'Valid values are:' + CRLF + CRLF +
- '1:Generator injects a constant kW at specified power factor.' + CRLF +
- '2:Generator is modeled as a constant admittance.' + CRLF +
- '3:Const kW, constant kV. Somewhat like a conventional transmission power flow P-V generator.' + CRLF +
- '4:Const kW, Fixed Q (Q never varies)' + CRLF +
- '5:Const kW, Fixed Q(as a constant reactance)' + CRLF +
- '6:Compute load injection from User-written Model.(see usage of Xd, Xdp)' + CRLF +
- '7:Constant kW, kvar, but current-limited below Vminpu. Approximates a simple inverter. See also Balanced.');
- AddProperty('Vminpu', 23, 'Default = 0.90. Minimum per unit voltage for which the Model is assumed to apply. ' +
- 'Below this value, the load model reverts to a constant impedance model. For model 7, the current is ' +
- 'limited to the value computed for constant power at Vminpu.');
- AddProperty('Vmaxpu', 24, 'Default = 1.10. Maximum per unit voltage for which the Model is assumed to apply. ' +
- 'Above this value, the load model reverts to a constant impedance model.');
- AddProperty('yearly', 7, 'Dispatch shape to use for yearly simulations. Must be previously defined ' +
- 'as a Loadshape object. If this is not specified, a constant value is assumed (no variation). ' +
- 'If the generator is assumed to be ON continuously, specify Status=FIXED, or ' +
- 'designate a curve that is 1.0 per unit at all times. ' +
- 'Set to NONE to reset to no loadahape. ' +
- 'Nominally for 8760 simulations. If there are fewer points in the designated shape than ' +
- 'the number of points in the solution, the curve is repeated.');
- AddProperty('daily', 8, 'Dispatch shape to use for daily simulations. Must be previously defined ' +
- 'as a Loadshape object of 24 hrs, typically. If generator is assumed to be ' +
- 'ON continuously, specify Status=FIXED, or designate a Loadshape object' +
- 'that is 1.0 perunit for all hours. ' +
- 'Set to NONE to reset to no loadahape. '); // daily dispatch (hourly)
- AddProperty('duty', 9, 'Load shape to use for duty cycle dispatch simulations such as for wind generation. ' +
- 'Must be previously defined as a Loadshape object. ' +
- 'Typically would have time intervals less than 1 hr -- perhaps, in seconds. ' +
- 'Set Status=Fixed to ignore Loadshape designation. ' +
- 'Set to NONE to reset to no loadahape. ' +
- 'Designate the number of points to solve using the Set Number=xxxx command. ' +
- 'If there are fewer points in the actual shape, the shape is assumed to repeat.'); // as for wind generation
- AddProperty('dispmode', 10, '{Default* | Loadlevel | Price } Default = Default. Dispatch mode. ' +
- 'In default mode, gen is either always on or follows dispatch curve as specified. ' +
- 'Otherwise, the gen comes on when either the global default load level (Loadshape "default") or the price level ' +
- 'exceeds the dispatch value.'); // = 0 | >0
- AddProperty('dispvalue', 11, 'Dispatch value. ' + CRLF +
- 'If = 0.0 (default) then Generator follow dispatch curves, if any. ' + CRLF +
- 'If > 0 then Generator is ON only when either the price signal (in Price dispatch mode) ' +
- 'exceeds this value or the active circuit load multiplier * "default" loadshape value * the default yearly growth factor ' +
- 'exceeds this value. Then the generator follows dispatch curves (duty, daily, or yearly), if any (see also Status).'); // = 0 | >0
- AddProperty('conn', 12, '={wye|LN|delta|LL}. Default is wye.');
- AddProperty('Rneut', 14, 'Removed due to causing confusion - Add neutral impedance externally.');
- AddProperty('Xneut', 15, 'Removed due to causing confusion - Add neutral impedance externally.');
- AddProperty('status', 16, '={Fixed | Variable*}. If Fixed, then dispatch multipliers do not apply. ' +
- 'The generator is alway at full power when it is ON. ' +
- ' Default is Variable (follows curves).'); // fixed or variable
- AddProperty('class', 17, 'An arbitrary integer number representing the class of Generator so that Generator values may ' +
- 'be segregated by class.'); // integer
- AddProperty('Vpu', 18, 'Per Unit voltage set point for Model = 3 (typical power flow model). Default is 1.0. '); // per unit set point voltage for power flow model
- AddProperty('maxkvar', 19, 'Maximum kvar limit for Model = 3. Defaults to twice the specified load kvar. ' +
- 'Always reset this if you change PF or kvar properties.');
- AddProperty('minkvar', 20, 'Minimum kvar limit for Model = 3. Enter a negative number if generator can absorb vars.' +
- ' Defaults to negative of Maxkvar. Always reset this if you change PF or kvar properties.');
- AddProperty('pvfactor', 21, 'Deceleration factor for P-V generator model (Model=3). Default is 0.1. ' +
- 'If the circuit converges easily, you may want to use a higher number such as 1.0. ' +
- 'Use a lower number if solution diverges. Use Debugtrace=yes to create a file that will ' +
- 'trace the convergence of a generator model.');
- AddProperty('forceon', 25, '{Yes | No} Forces generator ON despite requirements of other dispatch modes. ' +
- 'Stays ON until this property is set to NO, or an internal algorithm cancels the forced ON state.');
- AddProperty('kVA', 26, 'kVA rating of electrical machine. Defaults to 1.2* kW if not specified. Applied to machine or inverter definition for Dynamics mode solutions. ');
- AddProperty('MVA', 27, 'MVA rating of electrical machine. Alternative to using kVA=.');
- AddProperty('Xd', 28, 'Per unit synchronous reactance of machine. Presently used only for Thevinen impedance for power flow calcs of user models (model=6). ' +
- 'Typically use a value 0.4 to 1.0. Default is 1.0');
- AddProperty('Xdp', 29, 'Per unit transient reactance of the machine. Used for Dynamics mode and Fault studies. Default is 0.27.' +
- 'For user models, this value is used for the Thevinen/Norton impedance for Dynamics Mode.');
- AddProperty('Xdpp', 30, 'Per unit subtransient reactance of the machine. Used for Harmonics. Default is 0.20.');
- AddProperty('H', 31, 'Per unit mass constant of the machine. MW-sec/MVA. Default is 1.0.');
- AddProperty('D', 32, 'Damping constant. Usual range is 0 to 4. Default is 1.0. Adjust to get damping');
- AddProperty('UserModel', 33, 'Name of DLL containing user-written model, which computes the terminal currents for Dynamics studies, ' +
- 'overriding the default model. Set to "none" to negate previous setting.');
- AddProperty('UserData', 34, 'String (in quotes or parentheses) that gets passed to user-written model for defining the data required for that model.');
- AddProperty('ShaftModel', 35, 'Name of user-written DLL containing a Shaft model, which models the prime mover and determines the power on the shaft for Dynamics studies. ' +
- 'Models additional mass elements other than the single-mass model in the DSS default model. Set to "none" to negate previous setting.');
- AddProperty('ShaftData', 36, 'String (in quotes or parentheses) that gets passed to user-written shaft dynamic model for defining the data for that model.');
- AddProperty('DutyStart', 37, 'Starting time offset [hours] into the duty cycle shape for this generator, defaults to 0');
- AddProperty('debugtrace', 22, '{Yes | No } Default is no. Turn this on to capture the progress of the generator model ' +
- 'for each iteration. Creates a separate file for each generator named "GEN_name.CSV".');
- AddProperty('Balanced', 38, '{Yes | No*} Default is No. For Model=7, force balanced current only for 3-phase generators. Force zero- and negative-sequence to zero.');
- AddProperty('XRdp', 39, 'Default is 20. X/R ratio for Xdp property for FaultStudy and Dynamic modes.');
- AddProperty('UseFuel', 40, '{Yes | *No}. Activates the use of fuel for the operation of the generator. When the fuel level' +
- ' reaches the reserve level, the generator stops until it gets refueled. By default, the generator ' +
- 'is connected to a continuous fuel supply, Use this mode to mimic dependency on fuel level for different ' +
- 'generation technologies.');
- AddProperty('FuelkWh', 41, '{*0}Is the nominal level of fuel for the generator (kWh). It only applies if UseFuel = Yes/True');
- AddProperty('%Fuel', 42, 'It is a number between 0 and 100 representing the current amount of fuel avaiable in percentage ' +
- 'of FuelkWh. It only applies if UseFuel = Yes/True');
- AddProperty('%Reserve', 43, 'It is a number between 0 and 100 representing the reserve level in percentage of FuelkWh. ' +
- 'It only applies if UseFuel = Yes/True');
- AddProperty('Refuel', 44, 'It is a boolean value (Yes/True, No/False) that can be used to manually refuel the generator when needed. ' +
- 'It only applies if UseFuel = Yes/True');
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- // Override default help string
- PropertyHelp[NumPropsThisClass + 1] := 'Name of harmonic voltage or current spectrum for this generator. ' +
- 'Voltage behind Xd" for machine - default. Current injection for inverter. ' +
- 'Default value is "default", which is defined when the DSS starts.';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enum properties
+ PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.conn)] := ptruint(@obj.Connection);
+ PropertyOffset2[ord(TProp.conn)] := PtrInt(DSS.ConnectionEnum);
+
+ PropertyType[ord(TProp.dispmode)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.dispmode)] := ptruint(@obj.DispatchMode);
+ PropertyOffset2[ord(TProp.dispmode)] := PtrInt(GenDispModeEnum);
+
+ PropertyType[ord(TProp.status)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.status)] := ptruint(@obj.IsFixed); // LongBool as Integer
+ PropertyOffset2[ord(TProp.status)] := PtrInt(GenStatusEnum);
+
+ // string properties
+ PropertyType[ord(TProp.UserModel)] := TPropertyType.StringProperty;
+ PropertyType[ord(TProp.UserData)] := TPropertyType.StringProperty;
+ PropertyType[ord(TProp.ShaftModel)] := TPropertyType.StringProperty;
+ PropertyType[ord(TProp.ShaftData)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.UserModel)] := ptruint(@obj.UserModelNameStr);
+ PropertyOffset[ord(TProp.UserData)] := ptruint(@obj.UserModelEditStr);
+ PropertyOffset[ord(TProp.ShaftModel)] := ptruint(@obj.ShaftModelNameStr);
+ PropertyOffset[ord(TProp.ShaftData)] := ptruint(@obj.ShaftModelEditStr);
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+
+ // boolean properties
+ PropertyType[ord(TProp.debugtrace)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.forceon)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.Balanced)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.UseFuel)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.debugtrace)] := ptruint(@obj.DebugTrace);
+ PropertyOffset[ord(TProp.forceon)] := ptruint(@obj.ForcedON);
+ PropertyOffset[ord(TProp.Balanced)] := ptruint(@obj.ForceBalanced);
+ PropertyOffset[ord(TProp.UseFuel)] := ptruint(@obj.UseFuel);
+
+ // integer properties
+ PropertyType[ord(TProp.cls)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.model)] := TPropertyType.IntegerProperty; //TODO: enum
+ PropertyOffset[ord(TProp.cls)] := ptruint(@obj.GenClass);
+ PropertyOffset[ord(TProp.model)] := ptruint(@obj.GenModel);
+
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // object properties
+ PropertyType[ord(TProp.yearly)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.daily)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.duty)] := TPropertyType.DSSObjectReferenceProperty;
+
+ PropertyOffset[ord(TProp.yearly)] := ptruint(@obj.YearlyShapeObj);
+ PropertyOffset[ord(TProp.daily)] := ptruint(@obj.DailyDispShapeObj);
+ PropertyOffset[ord(TProp.duty)] := ptruint(@obj.DutyShapeObj);
+
+ PropertyOffset2[ord(TProp.yearly)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.daily)] := ptruint(DSS.LoadShapeClass);
+ PropertyOffset2[ord(TProp.duty)] := ptruint(DSS.LoadShapeClass);
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.kW)] := ptruint(@obj.kWBase);
+ PropertyOffset[ord(TProp.pf)] := ptruint(@obj.PFNominal);
+ PropertyOffset[ord(TProp.Vpu)] := ptruint(@obj.Vpu);
+ PropertyOffset[ord(TProp.maxkvar)] := ptruint(@obj.kvarMax);
+ PropertyOffset[ord(TProp.minkvar)] := ptruint(@obj.kvarMin);
+ PropertyOffset[ord(TProp.pvfactor)] := ptruint(@obj.PVFactor);
+ PropertyOffset[ord(TProp.dispvalue)] := ptruint(@obj.DispatchValue);
+ PropertyOffset[ord(TProp.Vminpu)] := ptruint(@obj.VMinPu);
+ PropertyOffset[ord(TProp.Vmaxpu)] := ptruint(@obj.VMaxPu);
+ PropertyOffset[ord(TProp.DutyStart)] := ptruint(@obj.DutyStart);
+ PropertyOffset[ord(TProp.FuelkWh)] := ptruint(@obj.FuelkWh);
+ PropertyOffset[ord(TProp.pctFuel)] := ptruint(@obj.pctFuel);
+ PropertyOffset[ord(TProp.pctReserve)] := ptruint(@obj.pctReserve);
+
+ PropertyOffset[ord(TProp.kVA)] := ptruint(@obj.GenVars.kVArating);
+ PropertyOffset[ord(TProp.Xd)] := ptruint(@obj.GenVars.puXd);
+ PropertyOffset[ord(TProp.Xdp)] := ptruint(@obj.GenVars.puXdp);
+ PropertyOffset[ord(TProp.Xdpp)] := ptruint(@obj.GenVars.puXdpp);
+ PropertyOffset[ord(TProp.H)] := ptruint(@obj.GenVars.Hmass);
+ PropertyOffset[ord(TProp.D)] := ptruint(@obj.GenVars.Dpu);
+ PropertyOffset[ord(TProp.XRdp)] := ptruint(@obj.Genvars.XRdp);// X/R for dynamics model
+
+ PropertyOffset[ord(TProp.kv)] := ptruint(@obj.Genvars.kVGeneratorBase);
+ PropertyOffset[ord(TProp.kvar)] := ptruint(@obj.kvarBase);
+
+ // adv doubles
+ PropertyOffset[ord(TProp.MVA)] := ptruint(@obj.GenVars.kVArating);
+ PropertyScale[ord(TProp.MVA)] := 1000.0;
+ PropertyFlags[ord(TProp.MVA)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.MVA)] := ord(TProp.kVA);
+
+ // boolean action
+ PropertyType[ord(TProp.Refuel)] := TPropertyType.BooleanActionProperty;
+ PropertyOffset[ord(TProp.Refuel)] := ptruint(@DoRefuel);
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGenerator.NewObject(const ObjName: String): Integer;
+function TGenerator.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new Generator and add it to Generator class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TGeneratorObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TGenerator.SetNcondsForConnection;
-
+procedure SetNcondsForConnection(Obj: TObj);
begin
- with DSS.ActiveGeneratorObj do
- begin
+ with Obj do
case Connection of
0:
NConds := Fnphases + 1;
@@ -536,384 +554,199 @@ procedure TGenerator.SetNcondsForConnection;
NConds := Fnphases;
end;
end;
- end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TGenerator.InterpretConnection(const S: String);
-
-// Accepts
-// delta or LL (Case insensitive)
-// Y, wye, or LN
+procedure TGeneratorObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- TestS: String;
-
+ i: Integer;
+ kVA_Gen: Double;
begin
- with DSS.ActiveGeneratorObj do
- begin
- TestS := lowercase(S);
- case TestS[1] of
- 'y', 'w':
- Connection := 0; {Wye}
- 'd':
- Connection := 1; {Delta or line-Line}
- 'l':
- case Tests[2] of
- 'n':
- Connection := 0;
- 'l':
- Connection := 1;
- end;
-
- end;
-
- SetNCondsForConnection;
-
- {VBase is always L-N voltage unless 1-phase device or more than 3 phases}
+ if (Idx > 0) and (Idx <= NumPropsThisClass) then
+ case TProp(Idx) of
+ TProp.Conn:
+ begin
+ SetNCondsForConnection(self);
+ // VBase is always L-N voltage unless 1-phase device or more than 3 phases
+ with GenVars do
+ // CASE Connection OF
+ // 1: VBase := kVGeneratorBase * 1000.0 ;
+ // Else
+ case Fnphases of
+ 2, 3:
+ VBase := kVGeneratorBase * InvSQRT3x1000; // L-N Volts
+ else
+ VBase := kVGeneratorBase * 1000.0; // Just use what is supplied
+ end;
+ VBase95 := Vminpu * VBase;
+ VBase105 := Vmaxpu * VBase;
- with GenVars do {CASE Connection OF
- 1: VBase := kVGeneratorBase * 1000.0 ;
- Else}
- case Fnphases of
- 2, 3:
- VBase := kVGeneratorBase * InvSQRT3x1000; // L-N Volts
- else
- VBase := kVGeneratorBase * 1000.0; // Just use what is supplied
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
end;
- {End;}
- VBase95 := Vminpu * VBase;
- VBase105 := Vmaxpu * VBase;
-
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
- end;
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function InterpretDispMode(const S: String): Integer;
-begin
-
- case lowercase(S)[1] of
- 'l':
- Result := LOADMODE;
- 'p':
- Result := PRICEMODE;
- else
- Result := DEFAULT;
- end;
-
-end;
-
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGenerator.Edit: Integer;
-var
- i,
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
-
-begin
- // continue parsing with contents of Parser
- DSS.ActiveGeneratorObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveGeneratorObj;
-
- Result := 0;
-
- with DSS.ActiveGeneratorObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if (Length(ParamName) = 0) then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[PropertyIdxMap[ParamPointer]] := Param
- else
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Generator "' + Name + '"', 560);
+ TProp.kV:
+ with Genvars do
+ begin
+ case FNphases of
+ 2, 3:
+ VBase := kVGeneratorBase * InvSQRT3x1000;
+ else
+ VBase := kVGeneratorBase * 1000.0;
+ end;
+ end;
- if ParamPointer > 0 then
- case PropertyIdxMap[ParamPointer] of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 561);
- 1:
- NPhases := Parser.Intvalue; // num phases
- 2:
- SetBus(1, param);
- 3:
- PresentkV := Parser.DblValue;
- 4:
- kWBase := Parser.DblValue;
- 5:
- PFNominal := Parser.DblValue;
- 6:
- GenModel := Parser.IntValue;
- 7:
- YearlyShape := Param;
- 8:
- DailyDispShape := Param;
- 9:
- DutyShape := Param;
- 10:
- DispatchMode := InterpretDispMode(Param);
- 11:
- DispatchValue := Parser.DblValue;
- 12:
- InterpretConnection(Param);
- 13:
- Presentkvar := Parser.DblValue;
- 14:
- DoSimpleMsg('Rneut property has been deleted. Use external impedance.', 5611);
- 15:
- DoSimpleMsg('Xneut property has been deleted. Use external impedance.', 5612);
- 16:
- if lowercase(Param[1]) = 'f' then
- IsFixed := TRUE
- else
- IsFixed := FALSE;
- 17:
- GenClass := Parser.IntValue;
- 18:
- Vpu := Parser.DblValue;
- 19:
- kvarMax := Parser.DblValue;
- 20:
- kvarMin := Parser.DblValue;
- 21:
- PVFactor := Parser.DblValue; //decelaration factor
- 22:
- DebugTrace := InterpretYesNo(Param);
- 23:
- VMinPu := Parser.DblValue;
- 24:
- VMaxPu := Parser.DblValue;
- 25:
- FForcedON := InterpretYesNo(Param);
- 26:
- GenVars.kVArating := Parser.DblValue;
- 27:
- GenVars.kVArating := Parser.DblValue * 1000.0; // 'MVA';
- 28:
- GenVars.puXd := Parser.DblValue;
- 29:
- GenVars.puXdp := Parser.DblValue;
- 30:
- GenVars.puXdpp := Parser.DblValue;
- 31:
- GenVars.Hmass := Parser.DblValue;
- 32:
- GenVars.Dpu := Parser.DblValue;
- 33:
- UserModel.Name := Parser.StrValue; // Connect to user written models
- 34:
- if UserModel.Exists then
- UserModel.Edit := Parser.StrValue; // Send edit string to user model
- 35:
- ShaftModel.Name := Parser.StrValue;
- 36:
- ShaftModel.Edit := Parser.StrValue;
- 37:
- DutyStart := Parser.DblValue;
- 38:
- ForceBalanced := InterpretYesNo(Param);
- 39:
- Genvars.XRdp := Parser.DblValue; // X/R for dynamics model
- 40:
- UseFuel := InterpretYesNo(Param);
- 41:
- FuelkWh := Parser.DblValue;
- 42:
- pctFuel := Parser.DblValue;
- 43:
- pctReserve := Parser.DblValue;
- 44:
- if InterpretYesNo(Param) then
- begin
- pctFuel := 100.0;
- GenActive := True;
- end
+ TProp.kvar:
+ begin
+ Genvars.Qnominalperphase := 1000.0 * kvarBase / Fnphases; // init to something reasonable
+ kVA_Gen := Sqrt(Sqr(kWBase) + Sqr(kvarBase));
+ if kVA_Gen <> 0.0 then
+ PFNominal := kWBase / kVA_Gen
else
- // Inherited parameters
- ClassEdit(DSS.ActiveGeneratorObj, ParamPointer - NumPropsThisClass)
- end;
+ PFNominal := 1.0;
+ if (kWBase * kvarBase) < 0.0 then
+ PFNominal := -PFNominal;
- if ParamPointer > 0 then
- case PropertyIdxMap[ParamPointer] of
- 1:
- SetNcondsForConnection; // Force Reallocation of terminal info
+ kvarMax := 2.0 * kvarBase;
+ kvarMin := -kvarMax;
+ end;
+
+ TProp.phases:
+ SetNCondsForConnection(self); // Force Reallocation of terminal info
// keep kvar nominal up to date with kW and PF
- 4, 5:
- SyncUpPowerQuantities;
+ TProp.kW, TProp.pf:
+ SyncUpPowerQuantities;
+
+ TProp.UserModel:
+ UserModel.Name := UserModelNameStr; // Connect to user written models
+ TProp.UserData:
+ if UserModel.Exists then
+ UserModel.Edit := UserModelEditStr; // Send edit string to user model
+ TProp.ShaftModel:
+ ShaftModel.Name := ShaftModelNameStr;
+ TProp.ShaftData:
+ if ShaftModel.Exists then
+ ShaftModel.Edit := ShaftModelEditStr;
// if a model 3 generator added, force calc of dQdV
- 6:
- if GenModel = 3 then
- ActiveCircuit.Solution.SolutionInitialized := FALSE;
-
- {Set shape objects; returns nil if not valid}
- {Sets the kW and kvar properties to match the peak kW demand from the Loadshape}
- 7:
- begin
- YearlyShapeObj := DSS.LoadShapeClass.Find(YearlyShape);
- if Assigned(YearlyShapeObj) then
- with YearlyShapeObj do
- if UseActual then
- SetkWkvar(MaxP, MaxQ);
- end;
- 8:
- begin
- DailyDispShapeObj := DSS.LoadShapeClass.Find(DailyDispShape);
- if Assigned(DailyDispShapeObj) then
- with DailyDispShapeObj do
- if UseActual then
- SetkWkvar(MaxP, MaxQ);
- end;
- 9:
- begin
- DutyShapeObj := DSS.LoadShapeClass.Find(DutyShape);
- if Assigned(DutyShapeObj) then
- with DutyShapeObj do
- if UseActual then
- SetkWkvar(MaxP, MaxQ);
- end;
-
- 22:
- if DebugTrace then
- begin
- FreeAndNil(TraceFile);
- TraceFile := TFileStream.Create(DSS.OutputDirectory + 'GEN_' + Name + '.CSV', fmCreate);
- FSWrite(TraceFile, 't, Iteration, LoadMultiplier, Mode, LoadModel, GenModel, dQdV, Avg_Vpu, Vdiff, MQnominalperphase, MPnominalperphase, CurrentType');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Iinj' + IntToStr(i) + '|');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Iterm' + IntToStr(i) + '|');
- for i := 1 to nphases do
- FSWrite(Tracefile, ', |Vterm' + IntToStr(i) + '|');
- FSWrite(TraceFile, ',Vthev, Theta');
- FSWriteln(TraceFile);
- FSFlush(Tracefile);
- end
- else
- begin
- FreeAndNil(TraceFile);
- end;
-
- 26, 27:
- kVANotSet := FALSE;
+ TProp.model:
+ if GenModel = 3 then
+ ActiveCircuit.Solution.SolutionInitialized := FALSE;
+
+ // Set shape objects; returns nil if not valid
+ // Sets the kW and kvar properties to match the peak kW demand from the Loadshape
+ TProp.yearly:
+ if Assigned(YearlyShapeObj) then
+ with YearlyShapeObj do
+ if UseActual then
+ SetkWkvar(MaxP, MaxQ);
+ TProp.daily:
+ if Assigned(DailyDispShapeObj) then
+ with DailyDispShapeObj do
+ if UseActual then
+ SetkWkvar(MaxP, MaxQ);
+ TProp.duty:
+ if Assigned(DutyShapeObj) then
+ with DutyShapeObj do
+ if UseActual then
+ SetkWkvar(MaxP, MaxQ);
+
+ TProp.debugtrace:
+ if DebugTrace then
+ begin
+ FreeAndNil(TraceFile);
+ TraceFile := TBufferedFileStream.Create(DSS.OutputDirectory + 'GEN_' + Name + '.csv', fmCreate);
+ FSWrite(TraceFile, 't, Iteration, LoadMultiplier, Mode, LoadModel, GenModel, dQdV, Avg_Vpu, Vdiff, MQnominalperphase, MPnominalperphase, CurrentType');
+ for i := 1 to fnphases do
+ FSWrite(Tracefile, ', |Iinj' + IntToStr(i) + '|');
+ for i := 1 to fnphases do
+ FSWrite(Tracefile, ', |Iterm' + IntToStr(i) + '|');
+ for i := 1 to fnphases do
+ FSWrite(Tracefile, ', |Vterm' + IntToStr(i) + '|');
+ FSWrite(TraceFile, ',Vthev, Theta');
+ FSWriteln(TraceFile);
+ FSFlush(Tracefile);
+ end
+ else
+ begin
+ FreeAndNil(TraceFile);
end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+
+ TProp.kVA, TProp.MVA:
+ kVANotSet := FALSE;
end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
+
+function TGenerator.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ with TObj(ptr) do
+ begin
RecalcElementData;
YPrimInvalid := TRUE;
+ Exclude(Flags, Flg.EditionActive);
end;
-
+ Result := True;
end;
-//----------------------------------------------------------------------------
-function TGenerator.MakeLike(const OtherGeneratorName: String): Integer;
+procedure TGeneratorObj.MakeLike(OtherPtr: Pointer);
var
- OtherGenerator: TGeneratorObj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this line name in the present collection}
- OtherGenerator := Find(OtherGeneratorName);
- if (OtherGenerator <> NIL) then
- with DSS.ActiveGeneratorObj do
- begin
-
- if (Fnphases <> OtherGenerator.Fnphases) then
- begin
- Nphases := OtherGenerator.Fnphases;
- NConds := Fnphases; // Forces reallocation of terminal stuff
+ inherited MakeLike(OtherPtr);
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
- end;
+ Other := TObj(OtherPtr);
+ if (Fnphases <> Other.Fnphases) then
+ begin
+ FNphases := Other.Fnphases;
+ NConds := Fnphases; // Forces reallocation of terminal stuff
- GenVars.kVGeneratorBase := OtherGenerator.GenVars.kVGeneratorBase;
- Vbase := OtherGenerator.Vbase;
- Vminpu := OtherGenerator.Vminpu;
- Vmaxpu := OtherGenerator.Vmaxpu;
- Vbase95 := OtherGenerator.Vbase95;
- Vbase105 := OtherGenerator.Vbase105;
- kWBase := OtherGenerator.kWBase;
- kvarBase := OtherGenerator.kvarBase;
- Genvars.Pnominalperphase := OtherGenerator.Genvars.Pnominalperphase;
- PFNominal := OtherGenerator.PFNominal;
- Genvars.Qnominalperphase := OtherGenerator.Genvars.Qnominalperphase;
- varMin := OtherGenerator.varMin;
- varMax := OtherGenerator.varMax;
- Connection := OtherGenerator.Connection;
- YearlyShape := OtherGenerator.YearlyShape;
- YearlyShapeObj := OtherGenerator.YearlyShapeObj;
- DailyDispShape := OtherGenerator.DailyDispShape;
- DailyDispShapeObj := OtherGenerator.DailyDispShapeObj;
- DutyShape := OtherGenerator.DutyShape;
- DutyShapeObj := OtherGenerator.DutyShapeObj;
- DutyStart := OtherGenerator.DutyStart;
- DispatchMode := OtherGenerator.DispatchMode;
- DispatchValue := OtherGenerator.DispatchValue;
- GenClass := OtherGenerator.GenClass;
- GenModel := OtherGenerator.GenModel;
- IsFixed := OtherGenerator.IsFixed;
- GenVars.VTarget := OtherGenerator.Genvars.VTarget;
- Vpu := OtherGenerator.Vpu;
- kvarMax := OtherGenerator.kvarMax;
- kvarMin := OtherGenerator.kvarMin;
- FForcedON := OtherGenerator.FForcedON;
- kVANotSet := OtherGenerator.kVANotSet;
- UseFuel := OtherGenerator.UseFuel;
- FuelkWh := OtherGenerator.FuelkWh;
- pctFuel := OtherGenerator.pctFuel;
- pctReserve := OtherGenerator.pctReserve;
-
- GenVars.kVArating := OtherGenerator.GenVars.kVArating;
- GenVars.puXd := OtherGenerator.GenVars.puXd;
- GenVars.puXdp := OtherGenerator.GenVars.puXdp;
- GenVars.puXdpp := OtherGenerator.GenVars.puXdpp;
- GenVars.Hmass := OtherGenerator.GenVars.Hmass;
- GenVars.Theta := OtherGenerator.GenVars.Theta;
- GenVars.Speed := OtherGenerator.GenVars.Speed;
- GenVars.w0 := OtherGenerator.GenVars.w0;
- GenVars.dSpeed := OtherGenerator.GenVars.dSpeed;
- GenVars.D := OtherGenerator.GenVars.D;
- GenVars.Dpu := OtherGenerator.GenVars.Dpu;
- GenVars.XRdp := OtherGenerator.GenVars.Xrdp;
-
- UserModel.Name := OtherGenerator.UserModel.Name; // Connect to user written models
- ShaftModel.Name := OtherGenerator.ShaftModel.Name;
-
- ClassMakeLike(OtherGenerator);
-
- for i := 1 to ParentClass.NumProperties do
- FPropertyValue^[i] := OtherGenerator.FPropertyValue^[i];
-
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Load MakeLike: "' + OtherGeneratorName + '" Not Found.', 562);
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
+ end;
+ Vbase := Other.Vbase;
+ Vminpu := Other.Vminpu;
+ Vmaxpu := Other.Vmaxpu;
+ Vbase95 := Other.Vbase95;
+ Vbase105 := Other.Vbase105;
+ kWBase := Other.kWBase;
+ kvarBase := Other.kvarBase;
+
+ GenVars := Other.GenVars; // record, copy everything at once
+ PFNominal := Other.PFNominal;
+ varMin := Other.varMin;
+ varMax := Other.varMax;
+ Connection := Other.Connection;
+ YearlyShapeObj := Other.YearlyShapeObj;
+ DailyDispShapeObj := Other.DailyDispShapeObj;
+ DutyShapeObj := Other.DutyShapeObj;
+ DutyStart := Other.DutyStart;
+ DispatchMode := Other.DispatchMode;
+ DispatchValue := Other.DispatchValue;
+ GenClass := Other.GenClass;
+ GenModel := Other.GenModel;
+ IsFixed := Other.IsFixed;
+
+ Vpu := Other.Vpu;
+ kvarMax := Other.kvarMax;
+ kvarMin := Other.kvarMin;
+ ForcedON := Other.ForcedON;
+ kVANotSet := Other.kVANotSet;
+ UseFuel := Other.UseFuel;
+ FuelkWh := Other.FuelkWh;
+ pctFuel := Other.pctFuel;
+ pctReserve := Other.pctReserve;
+
+ UserModel.Name := Other.UserModel.Name; // Connect to user written models
+ ShaftModel.Name := Other.ShaftModel.Name;
+ UserModelNameStr := Other.UserModelNameStr;
+ ShaftModelNameStr := Other.ShaftModelNameStr;
end;
-{--------------------------------------------------------------------------}
procedure TGenerator.ResetRegistersAll; // Force all EnergyMeters in the circuit to reset
-
var
pGen: TGeneratorObj;
-
begin
pGen := ActiveCircuit.Generators.First;
while (pGen <> NIL) do
@@ -921,15 +754,11 @@ procedure TGenerator.ResetRegistersAll; // Force all EnergyMeters in the circui
pGen.ResetRegisters;
pGen := ActiveCircuit.Generators.Next;
end;
-
end;
-{--------------------------------------------------------------------------}
procedure TGenerator.SampleAll; // Force all EnergyMeters in the circuit to take a sample
-
var
pGen: TGeneratorObj;
-
begin
pGen := ActiveCircuit.Generators.First;
while pGen <> NIL do
@@ -940,31 +769,26 @@ procedure TGenerator.SampleAll; // Force all EnergyMeters in the circuit to tak
end;
end;
-//----------------------------------------------------------------------------
constructor TGeneratorObj.Create(ParClass: TDSSClass; const SourceName: String);
begin
inherited create(ParClass);
- Name := LowerCase(SourceName);
+ Name := AnsiLowerCase(SourceName);
DSSObjType := ParClass.DSSClassType; // + GEN_ELEMENT; // In both PCelement and Genelement list
TraceFile := nil;
- Nphases := 3;
+ FNphases := 3;
Fnconds := 4; // defaults to wye
Yorder := 0; // To trigger an initial allocation
Nterms := 1; // forces allocations
kWBase := 1000.0;
kvarBase := 60.0;
-
kvarMax := kvarBase * 2.0;
kvarMin := -kvarmax;
PFNominal := 0.88;
- YearlyShape := '';
YearlyShapeObj := NIL; // if YearlyShapeobj = nil then the load alway stays nominal * global multipliers
- DailyDispShape := '';
DailyDispShapeObj := NIL; // if DaillyShapeobj = nil then the load alway stays nominal * global multipliers
- DutyShape := '';
DutyShapeObj := NIL; // if DutyShapeobj = nil then the load alway stays nominal * global multipliers
DutyStart := 0.0;
Connection := 0; // Wye (star)
@@ -975,7 +799,6 @@ constructor TGeneratorObj.Create(ParClass: TDSSClass; const SourceName: String);
DQDVSaved := 0.0; // Initialize this here. Allows generators to be turned off and on
-
GeneratorSolutionCount := -1; // For keep track of the present solution in Injcurrent calcs
OpenGeneratorSolutionCount := -1;
YPrimOpenCond := NIL;
@@ -992,13 +815,11 @@ constructor TGeneratorObj.Create(ParClass: TDSSClass; const SourceName: String);
RandomMult := 1.0;
IsFixed := FALSE;
- {Machine rating stuff}
+ // Machine rating stuff
GenVars.kVArating := kWBase * 1.2;
kVANotSet := TRUE; // Flag for default value for kVA
- //GenVars.Vd := 7200.0;
-
-
+ //GenVars.Vd := 7200.0;
with GenVars do
begin
puXd := 1.0;
@@ -1016,13 +837,17 @@ constructor TGeneratorObj.Create(ParClass: TDSSClass; const SourceName: String);
XRdp := 20.0;
end;
- {Advertise Genvars struct as public}
+ // Advertise Genvars struct as public
PublicDataStruct := pointer(@Genvars);
PublicDataSize := SizeOf(TGeneratorVars);
UserModel := TGenUserModel.Create(DSS, @Genvars);
ShaftModel := TGenUserModel.Create(DSS, @Genvars);
+ UserModelNameStr := '';
+ UserModelEditStr := '';
+ ShaftModelNameStr := '';
+ ShaftModelEditStr := '';
DispatchValue := 0.0; // Follow curves
@@ -1035,12 +860,12 @@ constructor TGeneratorObj.Create(ParClass: TDSSClass; const SourceName: String);
PVFactor := 0.1;
DebugTrace := FALSE;
- FForcedON := FALSE;
+ ForcedON := FALSE;
GenSwitchOpen := FALSE;
ShapeIsActual := FALSE;
ForceBalanced := FALSE;
- Spectrum := 'defaultgen'; // override base class
+ SpectrumObj := DSS.SpectrumClass.DefaultGen; // override base class
UseFuel := False;
GenActive := True;
@@ -1048,14 +873,9 @@ constructor TGeneratorObj.Create(ParClass: TDSSClass; const SourceName: String);
pctFuel := 100.0;
pctReserve := 20.0;
- InitPropertyValues(0);
-
RecalcElementData;
-
end;
-
-//----------------------------------------------------------------------------
destructor TGeneratorObj.Destroy;
begin
FreeAndNil(TraceFile);
@@ -1065,7 +885,6 @@ destructor TGeneratorObj.Destroy;
inherited Destroy;
end;
-//----------------------------------------------------------------------------
procedure TGeneratorObj.Randomize(Opt: Integer);
begin
case Opt of
@@ -1080,9 +899,7 @@ procedure TGeneratorObj.Randomize(Opt: Integer);
end;
end;
-//----------------------------------------------------------------------------
procedure TGeneratorObj.CalcDailyMult(Hr: Double);
-
begin
if (DailyDispShapeObj <> NIL) then
begin
@@ -1093,10 +910,7 @@ procedure TGeneratorObj.CalcDailyMult(Hr: Double);
ShapeFactor := CDOUBLEONE; // Default to no daily variation
end;
-
-//----------------------------------------------------------------------------
procedure TGeneratorObj.CalcDutyMult(Hr: Double);
-
begin
if DutyShapeObj <> NIL then
begin
@@ -1107,11 +921,9 @@ procedure TGeneratorObj.CalcDutyMult(Hr: Double);
CalcDailyMult(Hr); // Default to Daily Mult if no duty curve specified
end;
-//----------------------------------------------------------------------------
procedure TGeneratorObj.CalcYearlyMult(Hr: Double);
-
begin
-{Yearly curve is assumed to be hourly only}
+ // Yearly curve is assumed to be hourly only
if YearlyShapeObj <> NIL then
begin
ShapeFactor := YearlyShapeObj.GetMultAtHour(Hr);
@@ -1119,16 +931,12 @@ procedure TGeneratorObj.CalcYearlyMult(Hr: Double);
end
else
ShapeFactor := CDOUBLEONE; // Defaults to no variation
-
end;
-
-//----------------------------------------------------------------------------
procedure TGeneratorObj.SetNominalGeneration;
var
Factor: Double;
GenOn_Saved: Boolean;
-
begin
GenOn_Saved := GenON;
ShapeFactor := CDOUBLEONE;
@@ -1138,7 +946,7 @@ procedure TGeneratorObj.SetNominalGeneration;
if not (IsDynamicModel or IsHarmonicModel) then // Leave generator in whatever state it was prior to entering Dynamic mode
begin
GenON := TRUE; // Init to on then check if it should be off
- if not FForcedON then
+ if not ForcedON then
case DispatchMode of
LOADMODE:
if (DispatchValue > 0.0) and (GeneratorDispatchReference < DispatchValue) then
@@ -1149,12 +957,11 @@ procedure TGeneratorObj.SetNominalGeneration;
end;
end;
-
if not GenON then
begin
- // If Generator is OFF enter as tiny resistive load (.0001 pu) so we don't get divide by zero in matrix
+ // If Generator is OFF enter as tiny resistive load (.0001 pu) so we don't get divide by zero in matrix
Genvars.Pnominalperphase := -0.1 * kWBase / Fnphases;
- // Pnominalperphase := 0.0;
+ // Pnominalperphase := 0.0;
Genvars.Qnominalperphase := 0.0;
end
else
@@ -1188,7 +995,7 @@ procedure TGeneratorObj.SetNominalGeneration;
TSolveMode.DYNAMICMODE:
begin
Factor := ActiveCircuit.GenMultiplier;
- // This mode allows use of one class of load shape
+ // This mode allows use of one class of load shape
case ActiveCircuit.ActiveLoadShapeClass of
USEDAILY:
CalcDailyMult(DynaVars.dblHour);
@@ -1224,7 +1031,7 @@ procedure TGeneratorObj.SetNominalGeneration;
end;
end;
- if not (IsDynamicModel or IsHarmonicModel) then //******
+ if not (IsDynamicModel or IsHarmonicModel) then
begin
if ShapeIsActual then
Genvars.Pnominalperphase := 1000.0 * ShapeFactor.re / Fnphases
@@ -1233,7 +1040,7 @@ procedure TGeneratorObj.SetNominalGeneration;
with Genvars do
if GenModel = 3 then
- begin { Just make sure present value is reasonable}
+ begin // Just make sure present value is reasonable
if Qnominalperphase > varMax then
Qnominalperphase := varMax
else
@@ -1242,58 +1049,51 @@ procedure TGeneratorObj.SetNominalGeneration;
end
else
begin
- { for other generator models}
+ // for other generator models
if ShapeIsActual then
Qnominalperphase := 1000.0 * ShapeFactor.im / Fnphases
else
Qnominalperphase := 1000.0 * kvarBase * Factor * ShapeFactor.im / Fnphases;
end;
end;
- end; {ELSE GenON}
+ end; // ELSE GenON
if not (IsDynamicModel or IsHarmonicModel) then
- begin //******
-
+ begin
case GenModel of
6:
Yeq := Cinv(cmplx(0.0, -Genvars.Xd)); // Gets negated in CalcYPrim
else
with Genvars do
- Yeq := CDivReal(Cmplx(Pnominalperphase, -Qnominalperphase), Sqr(Vbase)); // Vbase must be L-N for 3-phase
+ Yeq := Cmplx(Pnominalperphase, -Qnominalperphase) / Sqr(Vbase); // Vbase must be L-N for 3-phase
if (Vminpu <> 0.0) then
- Yeq95 := CDivReal(Yeq, sqr(Vminpu)) // at 95% voltage
+ Yeq95 := Yeq / sqr(Vminpu) // at 95% voltage
else
Yeq95 := Yeq; // Always a constant Z model
if (Vmaxpu <> 0.0) then
- Yeq105 := CDivReal(Yeq, Sqr(Vmaxpu)) // at 105% voltage
+ Yeq105 := Yeq / Sqr(Vmaxpu) // at 105% voltage
else
Yeq105 := Yeq;
end;
- { When we leave here, all the Yeq's are in L-N values}
-
+ // When we leave here, all the Yeq's are in L-N values
if GenModel = 7 then
with Genvars do
begin
- PhaseCurrentLimit := Cdivreal(Cmplx(Pnominalperphase, -Qnominalperphase), VBase95);
+ PhaseCurrentLimit := Cmplx(Pnominalperphase, -Qnominalperphase) / VBase95;
Model7MaxPhaseCurr := Cabs(PhaseCurrentLimit);
end;
-
end;
- end; {With ActiveCircuit}
+ end; // With ActiveCircuit
- // If generator state changes, force re-calc of Y matrix
+ // If generator state changes, force re-calc of Y matrix
if GenON <> GenON_Saved then
YPrimInvalid := TRUE;
-
end;
-//----------------------------------------------------------------------------
procedure TGeneratorObj.RecalcElementData;
-
begin
-
VBase95 := VMinPu * VBase;
VBase105 := VMaxPu * VBase;
@@ -1301,7 +1101,7 @@ procedure TGeneratorObj.RecalcElementData;
varMin := 1000.0 * kvarMin / Fnphases;
varMax := 1000.0 * kvarMax / Fnphases;
- {Populate data structures used for interchange with user-written models.}
+ // Populate data structures used for interchange with user-written models.
with GenVars do
begin
Xd := puXd * 1000.0 * SQR(kVGeneratorBase) / kVARating;
@@ -1314,29 +1114,6 @@ procedure TGeneratorObj.RecalcElementData;
SetNominalGeneration;
- {Now check for errors. If any of these came out nil and the string was not nil, give warning}
- if CompareText(YearlyShape, 'none') = 0 then
- YearlyShape := '';
- if CompareText(DailyDispShape, 'none') = 0 then
- DailyDispShape := '';
- if CompareText(DutyShape, 'none') = 0 then
- DutyShape := '';
-
- if YearlyShapeObj = NIL then
- if Length(YearlyShape) > 0 then
- DoSimpleMsg('WARNING! Yearly load shape: "' + YearlyShape + '" Not Found.', 563);
- if DailyDispShapeObj = NIL then
- if Length(DailyDispShape) > 0 then
- DoSimpleMsg('WARNING! Daily load shape: "' + DailyDispShape + '" Not Found.', 564);
- if DutyShapeObj = NIL then
- if Length(DutyShape) > 0 then
- DoSimpleMsg('WARNING! Duty load shape: "' + DutyShape + '" Not Found.', 565);
-
- SpectrumObj := DSS.SpectrumClass.Find(Spectrum);
- if SpectrumObj = NIL then
- DoSimpleMsg('ERROR! Spectrum "' + Spectrum + '" Not Found.', 566);
-
-
YQFixed := -varBase / Sqr(VBase); //10-17-02 Fixed negative sign
GenVars.Vtarget := Vpu * 1000.0 * GenVars.kVGeneratorBase;
@@ -1350,24 +1127,19 @@ procedure TGeneratorObj.RecalcElementData;
Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
- {Update any user-written models}
+ // Update any user-written models
if Usermodel.Exists then
UserModel.FUpdateModel;
if Shaftmodel.Exists then
Shaftmodel.FUpdateModel;
-
end;
-//----------------------------------------------------------------------------
procedure TGeneratorObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
-
var
Y, Yij: Complex;
i, j: Integer;
FreqMultiplier: Double;
-
begin
-
FYprimFreq := ActiveCircuit.Solution.Frequency;
FreqMultiplier := FYprimFreq / BaseFrequency;
@@ -1380,9 +1152,9 @@ procedure TGeneratorObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
Y := Cmplx(EPSILON, 0.0);
if Connection = 1 then
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
+ Y := Y / 3.0; // Convert to delta impedance
Y.im := Y.im / FreqMultiplier;
- Yij := Cnegate(Y);
+ Yij := -Y;
for i := 1 to Fnphases do
begin
case Connection of
@@ -1402,34 +1174,29 @@ procedure TGeneratorObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
end;
end;
- (**** Removed Neutral / Neutral may float
-
- IF Connection = 0 Then With Ymatrix Do // Take care of neutral issues
- Begin
- AddElement(Fnconds, Fnconds, YNeut); // Add in user specified Neutral Z, if any
- // Bump up neutral-ground in case neutral ends up floating
- SetElement(Fnconds, Fnconds, CmulReal(GetElement(Fnconds, Fnconds), 1.000001));
- End;
-
- *)
+// Removed Neutral / Neutral may float
+//
+// IF Connection = 0 Then With Ymatrix Do // Take care of neutral issues
+// Begin
+// AddElement(Fnconds, Fnconds, YNeut); // Add in user specified Neutral Z, if any
+// // Bump up neutral-ground in case neutral ends up floating
+// SetElement(Fnconds, Fnconds, GetElement(Fnconds, Fnconds) * 1.000001);
+// End;
+//
+//
end
-
else
begin // Regular power flow generator model
-
- {Yeq is always expected as the equivalent line-neutral admittance}
-
- Y := cnegate(Yeq); // negate for generation Yeq is L-N quantity
-
- // ****** Need to modify the base admittance for real harmonics calcs
+ // Yeq is always expected as the equivalent line-neutral admittance
+ Y := -Yeq; // negate for generation Yeq is L-N quantity
+ // ****** Need to modify the base admittance for real harmonics calcs
Y.im := Y.im / FreqMultiplier;
case Connection of
-
0:
with YMatrix do
begin // WYE
- Yij := Cnegate(Y);
+ Yij := -Y;
for i := 1 to Fnphases do
begin
SetElement(i, i, Y);
@@ -1440,8 +1207,8 @@ procedure TGeneratorObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
1:
with YMatrix do
begin // Delta or L-L
- Y := CDivReal(Y, 3.0); // Convert to delta impedance
- Yij := Cnegate(Y);
+ Y := Y / 3.0; // Convert to delta impedance
+ Yij := -Y;
for i := 1 to Fnphases do
begin
j := i + 1;
@@ -1453,21 +1220,15 @@ procedure TGeneratorObj.CalcYPrimMatrix(Ymatrix: TcMatrix);
end;
end;
end;
- end; {ELSE IF Solution.mode}
-
+ end; // ELSE IF Solution.mode
end;
-
-//----------------------------------------------------------------------------
procedure TGeneratorObj.CalcYPrim;
-
var
i: Integer;
-
begin
-
- // Build only shunt Yprim
- // Build a dummy Yprim Series so that CalcV does not fail
+ // Build only shunt Yprim
+ // Build a dummy Yprim Series so that CalcV does not fail
if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Shunt = NIL) OR (Yprim_Series = NIL) {YPrimInvalid} then
begin
if YPrim_Shunt <> NIL then
@@ -1489,39 +1250,31 @@ procedure TGeneratorObj.CalcYPrim;
if ActiveCircuit.Solution.LoadModel = POWERFLOW then
begin
-
// 12-7-99 we'll start with Yeq in system matrix
SetNominalGeneration;
CalcYPrimMatrix(YPrim_Shunt);
-
end
else
begin
-
- // ADMITTANCE model wanted
-
+ // ADMITTANCE model wanted
SetNominalGeneration;
CalcYPrimMatrix(YPrim_Shunt);
-
end;
- // Set YPrim_Series based on diagonals of YPrim_shunt so that CalcVoltages doesn't fail
+ // Set YPrim_Series based on diagonals of YPrim_shunt so that CalcVoltages doesn't fail
for i := 1 to Yorder do
- Yprim_Series.SetElement(i, i, CmulReal(Yprim_Shunt.Getelement(i, i), 1.0e-10));
+ Yprim_Series.SetElement(i, i, Yprim_Shunt.Getelement(i, i) * 1.0e-10);
YPrim.CopyFrom(YPrim_Shunt);
- // Account for Open Conductors
+ // Account for Open Conductors
inherited CalcYPrim;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.StickCurrInTerminalArray(TermArray: pComplexArray; const Curr: Complex; i: Integer);
- {Add the current into the proper location according to connection}
-
- {Reverse of similar routine in load (Cnegates are switched)}
+// Add the current into the proper location according to connection
+// Reverse of similar routine in load (Cnegates are switched)
var
j: Integer;
@@ -1530,29 +1283,26 @@ procedure TGeneratorObj.StickCurrInTerminalArray(TermArray: pComplexArray; const
0:
begin //Wye
- Caccum(TermArray^[i], Curr);
- Caccum(TermArray^[Fnconds], Cnegate(Curr)); // Neutral
+ TermArray^[i] += Curr;
+ TermArray^[Fnconds] -= Curr; // Neutral
end;
1:
begin //DELTA
- Caccum(TermArray^[i], Curr);
+ TermArray^[i] += Curr;
j := i + 1;
if j > Fnconds then
j := 1;
- Caccum(TermArray^[j], Cnegate(Curr));
+ TermArray^[j] -= Curr;
end;
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.WriteTraceRecord(const s: String);
-
var
i: Integer;
sout: String;
begin
-
try
if (not DSS.InShowResults) then
begin
@@ -1560,8 +1310,8 @@ procedure TGeneratorObj.WriteTraceRecord(const s: String);
[ActiveCircuit.Solution.DynaVars.t + ActiveCircuit.Solution.Dynavars.IntHour * 3600.0,
ActiveCircuit.Solution.Iteration,
ActiveCircuit.LoadMultiplier]),
- GetSolutionModeID(DSS), ', ',
- GetLoadModel(DSS), ', ',
+ DSS.SolveModeEnum.OrdinalToString(ord(DSS.ActiveCircuit.Solution.mode)), ', ',
+ DSS.DefaultLoadModelEnum.OrdinalToString(DSS.ActiveCircuit.Solution.LoadModel), ', ',
GenModel: 0, ', ',
DQDV: 8: 0, ', ',
(V_Avg * 0.001732 / GenVars.kVgeneratorbase): 8: 3, ', ',
@@ -1571,17 +1321,17 @@ procedure TGeneratorObj.WriteTraceRecord(const s: String);
s, ', '
);
FSWrite(TraceFile, sout);
- for i := 1 to nphases do
+ for i := 1 to fnphases do
begin
WriteStr(sout, (Cabs(InjCurrent^[i])): 8: 1, ', ');
FSWrite(TraceFile, sout);
end;
- for i := 1 to nphases do
+ for i := 1 to fnphases do
begin
WriteStr(sout, (Cabs(ITerminal^[i])): 8: 1, ', ');
FSWrite(TraceFile, sout);
end;
- for i := 1 to nphases do
+ for i := 1 to fnphases do
begin
WriteStr(sout, (Cabs(Vterminal^[i])): 8: 1, ', ');
FSWrite(TraceFile, sout);
@@ -1598,7 +1348,7 @@ procedure TGeneratorObj.WriteTraceRecord(const s: String);
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
+
function TGeneratorObj.CheckOnFuel(const Deriv: Double; Const Interval: Double): Boolean;
Begin
Result := True;
@@ -1611,11 +1361,9 @@ function TGeneratorObj.CheckOnFuel(const Deriv: Double; Const Interval: Double):
// GenON := False;
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TGeneratorObj.DoConstantPQGen;
-
-{Compute total terminal current for Constant PQ}
+procedure TGeneratorObj.DoConstantPQGen;
+// Compute total terminal current for Constant PQ
var
i: Integer;
Curr, V: Complex;
@@ -1623,7 +1371,7 @@ procedure TGeneratorObj.DoConstantPQGen;
// V012,I012 :Array[0..2] of Complex;
// Iabc :Array[1..3] of Complex;
begin
- //Treat this just like the Load model
+ //Treat this just like the Load model
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
ZeroITerminal;
@@ -1638,13 +1386,13 @@ procedure TGeneratorObj.DoConstantPQGen;
0:
begin {Wye}
if VMag <= VBase95 then
- Curr := Cmul(Yeq95, V) // Below 95% use an impedance model
+ Curr := Yeq95 * V // Below 95% use an impedance model
else
if VMag > VBase105 then
- Curr := Cmul(Yeq105, V) // above 105% use an impedance model
+ Curr := Yeq105 * V // above 105% use an impedance model
else
with Genvars do
- Curr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), V)); // Between 95% -105%, constant PQ
+ Curr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / V); // Between 95% -105%, constant PQ
end;
1:
begin {Delta}
@@ -1652,17 +1400,17 @@ procedure TGeneratorObj.DoConstantPQGen;
2, 3:
VMag := VMag / SQRT3; // L-N magnitude
else
- {leave Vmag as is}
+ // leave Vmag as is
end;
if VMag <= VBase95 then
- Curr := Cmul(CdivReal(Yeq95, 3.0), V) // Below 95% use an impedance model
+ Curr := (Yeq95 / 3.0) * V // Below 95% use an impedance model
else
if VMag > VBase105 then
- Curr := Cmul(CdivReal(Yeq105, 3.0), V) // above 105% use an impedance model
+ Curr := (Yeq105 / 3.0) * V // above 105% use an impedance model
else
with Genvars do
- Curr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), V)); // Between 95% -105%, constant PQ
+ Curr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / V); // Between 95% -105%, constant PQ
end;
end;
@@ -1670,60 +1418,50 @@ procedure TGeneratorObj.DoConstantPQGen;
if UseFuel and (not GenActive) then
Curr := cmplx(0, 0);
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
- {END;}
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.DoConstantZGen;
var
i: Integer;
Curr,
Yeq2: Complex;
-
begin
-
-// Assume Yeq is kept up to date
+ // Assume Yeq is kept up to date
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the load
ZeroITerminal;
if Connection = 0 then
Yeq2 := Yeq
else
- Yeq2 := CdivReal(Yeq, 3.0);
+ Yeq2 := Yeq / 3.0;
for i := 1 to Fnphases do
begin
- Curr := Cmul(Yeq2, Vterminal^[i]); // Yeq is always line to neutral
+ Curr := Yeq2 * Vterminal^[i]; // Yeq is always line to neutral
// Checks the output in case of using Fuel
if UseFuel and (not GenActive) then
Curr := cmplx(0, 0);
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.DoPVTypeGen;
-{Compute total terminal current for Constant P,|V|}
+// Compute total terminal current for Constant P,|V|
// Constant P, constant |V|
-
var
-
i: Integer;
DQ: Double;
Curr: Complex;
-
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the generator
ZeroITerminal;
@@ -1738,8 +1476,6 @@ procedure TGeneratorObj.DoPVTypeGen;
else
V_Avg := V_Avg / Fnphases;
- // 12-9-99 added empirical 0.7 factor to improve iteration
- // 12-17-99 changed to 0.1 because first guess was consistently too high
DQ := PVFactor * DQDV * (GenVars.Vtarget - V_Avg); // Vtarget is L-N
if (Abs(DQ) > DeltaQMax) then
if (DQ < 0.0) then
@@ -1749,7 +1485,7 @@ procedure TGeneratorObj.DoPVTypeGen;
with Genvars do
Qnominalperphase := Qnominalperphase + DQ;
- { Test Limits}
+ // Test Limits
with Genvars do
begin
if (Qnominalperphase > varMax) then
@@ -1758,29 +1494,26 @@ procedure TGeneratorObj.DoPVTypeGen;
if (Qnominalperphase < varMin) then
Qnominalperphase := varMin;
- // Compute injection currents using W and var values
- // Do not use comstant Z models outside normal range
- // Presumably the var source will take care of the voltage problems
+ // Compute injection currents using W and var values
+ // Do not use comstant Z models outside normal range
+ // Presumably the var source will take care of the voltage problems
for i := 1 to Fnphases do
begin
- Curr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), Vterminal^[i]));
+ Curr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / Vterminal^[i]);
// Checks the output in case of using Fuel
if UseFuel and (not GenActive) then
Curr := cmplx(0, 0);
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
end; {With}
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.DoFixedQGen;
-
-{Compute total terminal current for Fixed Q}
+// Compute total terminal current for Fixed Q
// Constant P, Fixed Q Q is always kvarBase
var
i: Integer;
@@ -1802,12 +1535,12 @@ procedure TGeneratorObj.DoFixedQGen;
0:
begin
if VMag <= VBase95 then
- Curr := Cmul(Cmplx(Yeq95.re, YQfixed), V) // Below 95% use an impedance model
+ Curr := Cmplx(Yeq95.re, YQfixed) * V // Below 95% use an impedance model
else
if VMag > VBase105 then
- Curr := Cmul(Cmplx(Yeq105.re, YQfixed), V) // above 105% use an impedance model
+ Curr := Cmplx(Yeq105.re, YQfixed) * V // above 105% use an impedance model
else
- Curr := Conjg(Cdiv(Cmplx(Genvars.Pnominalperphase, varBase), V));
+ Curr := cong(Cmplx(Genvars.Pnominalperphase, varBase) / V);
end;
1:
begin
@@ -1818,12 +1551,12 @@ procedure TGeneratorObj.DoFixedQGen;
{leave Vmag as is}
end;
if VMag <= VBase95 then
- Curr := Cmul(Cmplx(Yeq95.re / 3.0, YQfixed / 3.0), V) // Below 95% use an impedance model
+ Curr := Cmplx(Yeq95.re / 3.0, YQfixed / 3.0) * V // Below 95% use an impedance model
else
if VMag > VBase105 then
- Curr := Cmul(Cmplx(Yeq105.re / 3.0, YQfixed / 3.0), V) // above 105% use an impedance model
+ Curr := Cmplx(Yeq105.re / 3.0, YQfixed / 3.0) * V // above 105% use an impedance model
else
- Curr := Conjg(Cdiv(Cmplx(Genvars.Pnominalperphase, varBase), V));
+ Curr := cong(Cmplx(Genvars.Pnominalperphase, varBase) / V);
end;
end;
@@ -1831,16 +1564,14 @@ procedure TGeneratorObj.DoFixedQGen;
if UseFuel and (not GenActive) then
Curr := cmplx(0, 0);
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.DoFixedQZGen;
-
-{Compute total terminal current for }
+// Compute total terminal current for
// Constant P, Fixed Q Q is always a fixed Z derived from kvarBase
var
i: Integer;
@@ -1849,7 +1580,6 @@ procedure TGeneratorObj.DoFixedQZGen;
Vmag: Double;
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the load
ZeroITerminal;
@@ -1863,14 +1593,14 @@ procedure TGeneratorObj.DoFixedQZGen;
0:
begin
if Vmag <= VBase95 then
- Curr := Cmul(Cmplx(Yeq95.re, YQfixed), V) // Below 95% use an impedance model
+ Curr := Cmplx(Yeq95.re, YQfixed) * V // Below 95% use an impedance model
else
if VMag > VBase105 then
- Curr := Cmul(Cmplx(Yeq105.re, YQfixed), V)
+ Curr := Cmplx(Yeq105.re, YQfixed) * V
else
begin
- Curr := Conjg(Cdiv(Cmplx(Genvars.Pnominalperphase, 0.0), V)); // P component of current
- Caccum(Curr, Cmul(Cmplx(0.0, YQFixed), V)); // add in Q component of current
+ Curr := cong(Cmplx(Genvars.Pnominalperphase, 0.0) / V); // P component of current
+ Curr += Cmplx(0.0, YQFixed) * V; // add in Q component of current
end;
end;
1:
@@ -1879,17 +1609,17 @@ procedure TGeneratorObj.DoFixedQZGen;
2, 3:
VMag := VMag / SQRT3; // L-N magnitude
else
- {leave Vmag as is}
+ // leave Vmag as is
end;
if Vmag <= VBase95 then
- Curr := Cmul(Cmplx(Yeq95.re / 3.0, YQfixed / 3.0), V) // Below 95% use an impedance model
+ Curr := Cmplx(Yeq95.re / 3.0, YQfixed / 3.0) * V // Below 95% use an impedance model
else
if VMag > VBase105 then
- Curr := Cmul(Cmplx(Yeq105.re / 3.0, YQfixed / 3.0), V)
+ Curr := Cmplx(Yeq105.re / 3.0, YQfixed / 3.0) * V
else
begin
- Curr := Conjg(Cdiv(Cmplx(Genvars.Pnominalperphase, 0.0), V)); // P component of current
- Caccum(Curr, Cmul(Cmplx(0.0, YQFixed / 3.0), V)); // add in Q component of current
+ Curr := cong(Cmplx(Genvars.Pnominalperphase, 0.0) / V); // P component of current
+ Curr += Cmplx(0.0, YQFixed / 3.0) * V; // add in Q component of current
end;
end;
end;
@@ -1898,19 +1628,17 @@ procedure TGeneratorObj.DoFixedQZGen;
if UseFuel and (not GenActive) then
Curr := cmplx(0, 0);
- StickCurrInTerminalArray(ITerminal, Cnegate(Curr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -Curr, i); // Put into Terminal array taking into account connection
IterminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, Curr, i); // Put into Terminal array taking into account connection
- end; {FOR}
+ end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
+
procedure TGeneratorObj.DoUserModel;
-{Compute total terminal Current from User-written model}
+// Compute total terminal Current from User-written model
var
i: Integer;
-
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
if UserModel.Exists then // Check automatically selects the usermodel if true
@@ -1921,22 +1649,17 @@ procedure TGeneratorObj.DoUserModel;
with ActiveCircuit.Solution do
begin // Negate currents from user model for power flow generator model
for i := 1 to FnConds do
- Caccum(InjCurrent^[i], Cnegate(Iterminal^[i]));
+ InjCurrent^[i] -= Iterminal^[i];
end;
end
else
begin
- DoSimpleMsg('Generator.' + name + ' model designated to use user-written model, but user-written model is not defined.', 567);
+ DoSimpleMsg('%s model designated to use user-written model, but user-written model is not defined.', [FullName], 567);
end;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.DoCurrentLimitedPQ;
-{Compute total terminal current for Constant PQ, but limit to max current below
- Vminpu}
-
-
+// Compute total terminal current for Constant PQ, but limit to max current below Vminpu
var
i: Integer;
PhaseCurr, DeltaCurr, VLN, VLL: Complex;
@@ -1944,7 +1667,7 @@ procedure TGeneratorObj.DoCurrentLimitedPQ;
V012: array[0..2] of Complex; // Sequence voltages
begin
- //Treat this just like the Load model
+ //Treat this just like the Load model
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array
CalcVTerminalPhase; // get actual voltage across each phase of the load
@@ -1961,18 +1684,17 @@ procedure TGeneratorObj.DoCurrentLimitedPQ;
for i := 1 to Fnphases do
begin
-
case Connection of
0:
begin
VLN := Vterminal^[i]; // VTerminal is LN for this connection
VMagLN := Cabs(VLN);
with Genvars do
- PhaseCurr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLN));
+ PhaseCurr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLN);
if Cabs(PhaseCurr) > Model7MaxPhaseCurr then
- PhaseCurr := Conjg(Cdiv(PhaseCurrentLimit, CDivReal(VLN, VMagLN)));
+ PhaseCurr := cong(PhaseCurrentLimit / (VLN / VMagLN));
- StickCurrInTerminalArray(ITerminal, Cnegate(PhaseCurr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -PhaseCurr, i); // Put into Terminal array taking into account connection
ITerminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, PhaseCurr, i); // Put into Terminal array taking into account connection
end;
@@ -1984,47 +1706,39 @@ procedure TGeneratorObj.DoCurrentLimitedPQ;
2, 3: // 2 or 3 phase generator model 7
begin
with Genvars do
- DeltaCurr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLL));
+ DeltaCurr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLL);
if Cabs(DeltaCurr) * SQRT3 > Model7MaxPhaseCurr then
- DeltaCurr := Conjg(Cdiv(PhaseCurrentLimit, CDivReal(VLL, VMagLL / SQRT3)));
+ DeltaCurr := cong(PhaseCurrentLimit / (VLL / (VMagLL / SQRT3)));
end
else // 1-phase generator model 7
with Genvars do
- DeltaCurr := Conjg(Cdiv(Cmplx(Pnominalperphase, Qnominalperphase), VLL));
+ DeltaCurr := cong(Cmplx(Pnominalperphase, Qnominalperphase) / VLL);
if Cabs(DeltaCurr) > Model7MaxPhaseCurr then
- DeltaCurr := Conjg(Cdiv(PhaseCurrentLimit, CDivReal(VLL, VMagLL)));
+ DeltaCurr := cong(PhaseCurrentLimit / (VLL / VMagLL));
end;
// Checks the output in case of using Fuel
if UseFuel and (not GenActive) then
DeltaCurr := cmplx(0, 0);
- StickCurrInTerminalArray(ITerminal, Cnegate(DeltaCurr), i); // Put into Terminal array taking into account connection
+ StickCurrInTerminalArray(ITerminal, -DeltaCurr, i); // Put into Terminal array taking into account connection
ITerminalUpdated := TRUE;
StickCurrInTerminalArray(InjCurrent, DeltaCurr, i); // Put into Terminal array taking into account connection
end;
end;
-
end;
-
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.DoDynamicMode;
-
-{Compute Total Current and add into InjTemp}
-
+// Compute Total Current and add into InjTemp
var
i: Integer;
V012,
I012: array[0..2] of Complex;
-
begin
-
CalcYPrimContribution(InjCurrent); // Init InjCurrent Array and computes VTerminal L-N
- {Inj = -Itotal (in) - Yprim*Vtemp}
+ // Inj = -Itotal (in) - Yprim*Vtemp
case GenModel of
@@ -2035,7 +1749,7 @@ procedure TGeneratorObj.DoDynamicMode;
end
else
begin
- DoSimpleMsg(Format('Dynamics model missing for Generator.%s ', [Name]), 5671);
+ DoSimpleMsg('Dynamics model missing for %s ', [FullName], 5671);
DSS.SolutionAbort := TRUE;
end;
else
@@ -2048,22 +1762,22 @@ procedure TGeneratorObj.DoDynamicMode;
case Genmodel of
7:
begin // simple inverter model
- // Assume inverter stays in phase with terminal voltage
- CalcVthev_Dyn_Mod7(CSub(VTerminal^[1], VTerminal^[2]));
+ // Assume inverter stays in phase with terminal voltage
+ CalcVthev_Dyn_Mod7(VTerminal^[1] - VTerminal^[2]);
end;
else
CalcVthev_Dyn; // Update for latest phase angle
end;
- ITerminal^[1] := CDiv(CSub(Csub(VTerminal^[1], Vthev), VTerminal^[2]), Zthev); // ZThev is based on Xd'
+ ITerminal^[1] := (VTerminal^[1] - Vthev - VTerminal^[2]) / Zthev; // ZThev is based on Xd'
if Genmodel = 7 then
begin
if Cabs(Iterminal^[1]) > Model7MaxPhaseCurr then // Limit the current but keep phase angle
ITerminal^[1] := ptocomplex(topolar(Model7MaxPhaseCurr, cang(Iterminal^[1])));
end;
- ITerminal^[2] := Cnegate(ITerminal^[1]);
+ ITerminal^[2] := -ITerminal^[1];
end;
3:
@@ -2074,15 +1788,15 @@ procedure TGeneratorObj.DoDynamicMode;
case GenModel of
7:
begin // simple inverter model
- // Positive Sequence Contribution to Iterminal
- // Assume inverter stays in phase with pos seq voltage
- // and pos seq current is limited
+ // Positive Sequence Contribution to Iterminal
+ // Assume inverter stays in phase with pos seq voltage
+ // and pos seq current is limited
CalcVthev_Dyn_Mod7(V012[1]);
- // Positive Sequence Contribution to Iterminal
- // Ref Frame here is all L-N
+ // Positive Sequence Contribution to Iterminal
+ // Ref Frame here is all L-N
- I012[1] := CDiv(Csub(V012[1], Vthev), Zthev); // ZThev is based on Xd'
+ I012[1] := (V012[1] - Vthev) / Zthev; // ZThev is based on Xd'
if Cabs(I012[1]) > Model7MaxPhaseCurr // Limit the current but keep phase angle
then
I012[1] := ptocomplex(topolar(Model7MaxPhaseCurr, cang(I012[1])));
@@ -2090,32 +1804,32 @@ procedure TGeneratorObj.DoDynamicMode;
then
I012[2] := CZERO
else
- I012[2] := Cdiv(V012[2], Zthev); // for inverter ZThev is (Xd' + j0)
+ I012[2] := V012[2] / Zthev; // for inverter ZThev is (Xd' + j0)
end
else
- // Positive Sequence Contribution to Iterminal
+ // Positive Sequence Contribution to Iterminal
CalcVthev_Dyn; // Update for latest phase angle
- // Positive Sequence Contribution to Iterminal
- I012[1] := CDiv(Csub(V012[1], Vthev), Zthev); // ZThev is based on Xd'
- I012[2] := Cdiv(V012[2], Cmplx(0.0, Xdpp)); // machine use Xd"
+ // Positive Sequence Contribution to Iterminal
+ I012[1] := (V012[1] - Vthev) / Zthev; // ZThev is based on Xd'
+ I012[2] := V012[2] / Cmplx(0.0, Xdpp); // machine use Xd"
end;
- {Adjust for generator connection}
+ // Adjust for generator connection
if (Connection = 1) or ForceBalanced then
I012[0] := CZERO
else
- I012[0] := Cdiv(V012[0], Cmplx(0.0, Xdpp));
+ I012[0] := V012[0] / Cmplx(0.0, Xdpp);
SymComp2Phase(ITerminal, pComplexArray(@I012)); // Convert back to phase components
- // Neutral current
+ // Neutral current
if Connection = 0 then
- ITerminal^[FnConds] := Cnegate(CmulReal(I012[0], 3.0));
+ ITerminal^[FnConds] := -I012[0] * 3;
end;
else
- DoSimpleMsg(Format('Dynamics mode is implemented only for 1- or 3-phase Generators. Generator.%s has %d phases.', [name, Fnphases]), 5671);
+ DoSimpleMsg('Dynamics mode is implemented only for 1- or 3-phase Generators. %s has %d phases.', [FullName, Fnphases], 5671);
DSS.SolutionAbort := TRUE;
end;
@@ -2123,67 +1837,57 @@ procedure TGeneratorObj.DoDynamicMode;
IterminalUpdated := TRUE;
- {Add it into inj current array}
+ // Add it into inj current array
for i := 1 to FnConds do
- Caccum(InjCurrent^[i], Cnegate(Iterminal^[i]));
+ InjCurrent^[i] -= Iterminal^[i];
- {Take Care of any shaft model calcs}
+ // Take Care of any shaft model calcs
if (GenModel = 6) and ShaftModel.Exists then // auto selects model
begin // Compute Mech Power to shaft
ShaftModel.FCalc(Vterminal, Iterminal); // Returns pshaft at least
end;
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.DoHarmonicMode;
+// Compute Injection Current Only when in harmonics mode
-{Compute Injection Current Only when in harmonics mode}
-
-{Assumes spectrum is a voltage source behind subtransient reactance and YPrim has been built}
-{Vd is the fundamental frequency voltage behind Xd" for phase 1}
-
+// Assumes spectrum is a voltage source behind subtransient reactance and YPrim has been built
+// Vd is the fundamental frequency voltage behind Xd" for phase 1
var
i: Integer;
E: Complex;
GenHarmonic: Double;
-
+ pBuffer: PCBuffer24;
begin
-
+ pBuffer := @TGenerator(ParentClass).cBuffer;
ComputeVterminal;
with ActiveCircuit.Solution do
begin
GenHarmonic := Frequency / GenFundamental;
- E := CmulReal(SpectrumObj.GetMult(GenHarmonic), GenVars.VThevHarm); // Get base harmonic magnitude
+ E := SpectrumObj.GetMult(GenHarmonic) * GenVars.VThevHarm; // Get base harmonic magnitude
RotatePhasorRad(E, GenHarmonic, GenVars.ThetaHarm); // Time shift by fundamental frequency phase shift
for i := 1 to Fnphases do
begin
- cBuffer[i] := E;
+ pBuffer[i] := E;
if i < Fnphases then
RotatePhasorDeg(E, GenHarmonic, -120.0); // Assume 3-phase generator
end;
end;
- {Handle Wye Connection}
+ // Handle Wye Connection
if Connection = 0 then
- cbuffer[Fnconds] := Vterminal^[Fnconds]; // assume no neutral injection voltage
-
- {Inj currents = Yprim (E) }
- YPrim.MVMult(InjCurrent, pComplexArray(@cBuffer));
+ pBuffer[Fnconds] := Vterminal^[Fnconds]; // assume no neutral injection voltage
+ // Inj currents = Yprim (E)
+ YPrim.MVMult(InjCurrent, pComplexArray(pBuffer));
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.CalcVTerminalPhase;
-
var
i, j: Integer;
-
begin
-
-{ Establish phase voltages and stick in Vterminal}
+ // Establish phase voltages and stick in Vterminal
case Connection of
0:
@@ -2204,32 +1908,20 @@ procedure TGeneratorObj.CalcVTerminalPhase;
Vterminal^[i] := VDiff(NodeRef^[i], NodeRef^[j]);
end;
end;
-
end;
-
GeneratorSolutionCount := ActiveCircuit.Solution.SolutionCount;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.CalcVTerminal;
-
-{Put terminal voltages in an array}
-
-
+// Put terminal voltages in an array
begin
-
ComputeVTerminal;
-
GeneratorSolutionCount := ActiveCircuit.Solution.SolutionCount;
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.CalcGenModelContribution;
// Calculates generator current and adds it properly into the injcurrent array
// routines may also compute ITerminal (ITerminalUpdated flag)
-
begin
IterminalUpdated := FALSE;
with ActiveCircuit, ActiveCircuit.Solution do
@@ -2260,85 +1952,26 @@ procedure TGeneratorObj.CalcGenModelContribution;
else
DoConstantPQGen; // for now, until we implement the other models.
end;
- end; {ELSE}
- end; {WITH}
-
- {When this is done, ITerminal is up to date}
-
+ end;
+ end;
+ // When this is done, ITerminal is up to date
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.CalcInjCurrentArray;
-
-
// Difference between currents in YPrim and total current
-
-
begin
-
-
-// Now Get Injection Currents
+ // Now Get Injection Currents
if GenSwitchOpen then
ZeroInjCurrent
else
CalcGenModelContribution;
-(* We're not going to mess with this logic here -- too complicated: Use an open line in series
- to look at open phase conditions.
-
- ELSE Begin
-
- // some terminals not closed use admittance model for injection
- If OpenGeneratorSolutionCount <> ActiveCircuit.Solution.SolutionCount Then Begin
-
- // Rebuild the Yprimopencond if a new solution because values may have changed.
-
- // only reallocate when necessary
- If YPrimOpenCond=nil Then YPrimOpenCond := TcMatrix.CreateMatrix(Yorder)
- ELSE YPrimOpenCond.Clear;
- If YPrimOpenCond.Order <> Yorder Then Begin
- YPrimOpenCond.Free;
- YPrimOpenCond := TcMatrix.CreateMatrix(Yorder);
- End;
- CalcYPrimMatrix(YPrimOpenCond);
-
- {Now Account for the Open Conductors}
- {For any conductor that is open, zero out row and column}
- With YPrimOpenCond Do Begin
- k := 0;
- FOR i := 1 TO Fnterms Do Begin
- FOR j := 1 TO Fnconds Do Begin
- If Not Terminals[i - 1].ConductorsClosed[j - 1] Then Begin
- ZeroRow(j+k);
- ZeroCol(j+k);
- SetElement(j+k, j+k, Cmplx(1.0e-12,0.0)); // In case node gets isolated
- End;
- End;
- k := k+Fnconds;
- End;
- End;
- OpenGeneratorSolutionCount := ActiveCircuit.Solution.SolutionCount;
-
- End;
-
- With ActiveCircuit.Solution Do
- FOR i := 1 TO Yorder Do Begin
- Ref := NodeRef^[i];
- If Ref=0 Then Vterminal^[i] := cZero
- ELSE Vterminal^[i] := V^[ref];
- End;
- YPrimOpenCond.MVmult(InjTemp, Vterminal);
- For i := 1 to Yorder Do InjTemp^[i] := Cnegate(InjTemp^[i]);
- End;
- *)
-end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TGeneratorObj.GetTerminalCurrents(Curr: pComplexArray);
+ // We're not going to mess with this logic here -- too complicated: Use an open line in series
+ // to look at open phase conditions.
+end;
+procedure TGeneratorObj.GetTerminalCurrents(Curr: pComplexArray);
// Compute total Currents
-
-
begin
with ActiveCircuit.Solution do
begin
@@ -2352,39 +1985,27 @@ procedure TGeneratorObj.GetTerminalCurrents(Curr: pComplexArray);
if (DebugTrace) then
WriteTraceRecord('TotalCurrent');
-
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
function TGeneratorObj.InjCurrents: Integer;
-
-
begin
-
with ActiveCircuit.Solution do
begin
if LoadsNeedUpdating then
SetNominalGeneration; // Set the nominal kW, etc for the type of solution being done
CalcInjCurrentArray; // Difference between currents in YPrim and total terminal current
-
if (DebugTrace) then
WriteTraceRecord('Injection');
- // Add into System Injection Current Array
-
+ // Add into System Injection Current Array
Result := inherited InjCurrents;
-
end;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.ResetRegisters;
-
var
i: Integer;
-
begin
for i := 1 to NumGenregisters do
Registers[i] := 0.0;
@@ -2393,37 +2014,31 @@ procedure TGeneratorObj.ResetRegisters;
FirstSampleAfterReset := TRUE; // initialize for trapezoidal integration
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.Integrate(Reg: Integer; const Deriv: Double; const Interval: Double);
-
begin
if ActiveCircuit.TrapezoidalIntegration then
begin
- {Trapezoidal Rule Integration}
+ // Trapezoidal Rule Integration
if not FirstSampleAfterReset then
Registers[Reg] := Registers[Reg] + 0.5 * Interval * (Deriv + Derivatives[Reg]);
end
- else {Plain Euler integration}
+ else
+ // Plain Euler integration
Registers[Reg] := Registers[Reg] + Interval * Deriv;
Derivatives[Reg] := Deriv;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.TakeSample;
// Update Energy from metered zone
-
var
S: Complex;
Smag: Double;
HourValue: Double;
-
begin
-
-// Compute energy in Generator branch
+ // Compute energy in Generator branch
if Enabled then
begin
-
if GenON then
begin
S := cmplx(Get_PresentkW, Get_Presentkvar);
@@ -2438,13 +2053,13 @@ procedure TGeneratorObj.TakeSample;
end;
if GenON or ActiveCircuit.TrapezoidalIntegration then
- {Make sure we always integrate for Trapezoidal case
- Don't need to for Gen Off and normal integration}
+ // Make sure we always integrate for Trapezoidal case
+ // Don't need to for Gen Off and normal integration
with ActiveCircuit.Solution do
begin
if ActiveCircuit.PositiveSequence then
begin
- S := CmulReal(S, 3.0);
+ S := S * 3;
Smag := 3.0 * Smag;
end;
Integrate(Reg_kWh, S.re, IntervalHrs); // Accumulate the power
@@ -2456,18 +2071,15 @@ procedure TGeneratorObj.TakeSample;
FirstSampleAfterReset := FALSE;
if UseFuel then
GenActive := CheckonFuel(S.re, IntervalHrs);
-
end;
end;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
function TGeneratorObj.Get_PresentkW: Double;
begin
Result := Genvars.Pnominalperphase * 0.001 * Fnphases;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
function TGeneratorObj.Get_PresentkV: Double;
begin
Result := Genvars.kVGeneratorBase;
@@ -2478,25 +2090,20 @@ function TGeneratorObj.Get_Presentkvar: Double;
Result := Genvars.Qnominalperphase * 0.001 * Fnphases;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.InitDQDVCalc;
-
begin
DQDV := 0.0;
Genvars.Qnominalperphase := 0.5 * (varmax + varmin); // avg of the limits
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.BumpUpQ;
-{Bump up vars by 10% of range for next calc}
+// Bump up vars by 10% of range for next calc
begin
with Genvars do
Qnominalperphase := Qnominalperphase + 0.1 * (varmax - varmin);
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.RememberQV;
-
var
i: Integer;
@@ -2510,13 +2117,11 @@ procedure TGeneratorObj.RememberQV;
V_Remembered := V_Avg;
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.CalcDQDV;
var
Vdiff: Double;
i: Integer;
begin
-
CalcVTerminal;
V_Avg := 0.0;
for i := 1 to Fnphases do
@@ -2532,78 +2137,36 @@ procedure TGeneratorObj.CalcDQDV;
DQDVSaved := DQDV; //Save for next time Allows generator to be enabled/disabled during simulation
end;
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
procedure TGeneratorObj.ResetStartPoint;
-
begin
Genvars.Qnominalperphase := 1000.0 * kvarBase / Fnphases;
end;
-
-// - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - -
-procedure TGeneratorObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i, idx: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- FSWriteln(F, Format('!DQDV=%10.2g', [DQDV]));
-
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- idx := PropertyIdxMap[i];
- case idx of
- 34, 36:
- FSWriteln(F, '~ ' + PropertyName^[i] + '=(' + PropertyValue[idx] + ')');
- 44:
- FSWriteln(F,'~ ',PropertyName^[i],'=False') // This one has no variable associated, not needed
- else
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[idx]);
- end;
- end;
-
- FSWriteln(F);
-
-end;
-
-
procedure TGeneratorObj.InitHarmonics;
var
E, Va: complex;
begin
-
YPrimInvalid := TRUE; // Force rebuild of YPrims
GenFundamental := ActiveCircuit.Solution.Frequency; // Whatever the frequency is when we enter here.
with GenVars do
begin
-
Yeq := Cinv(Cmplx(0.0, Xdpp)); // used for current calcs Always L-N
- {Compute reference Thevinen voltage from phase 1 current}
-
+ // Compute reference Thevinen voltage from phase 1 current
if GenON then
begin
-
ComputeIterminal; // Get present value of current
with ActiveCircuit.solution do
case Connection of
- 0:
- begin {wye - neutral is explicit}
- Va := Csub(NodeV^[NodeRef^[1]], NodeV^[NodeRef^[Fnconds]]);
- end;
- 1:
- begin {delta -- assume neutral is at zero}
+ 0:{wye - neutral is explicit}
+ Va := NodeV^[NodeRef^[1]] - NodeV^[NodeRef^[Fnconds]];
+ 1:{delta -- assume neutral is at zero}
Va := NodeV^[NodeRef^[1]];
- end;
end;
- E := Csub(Va, Cmul(Iterminal^[1], cmplx(0.0, Xdpp)));
+ E := Va - Iterminal^[1] * cmplx(0.0, Xdpp);
Vthevharm := Cabs(E); // establish base mag and angle
ThetaHarm := Cang(E);
end
@@ -2613,77 +2176,20 @@ procedure TGeneratorObj.InitHarmonics;
ThetaHarm := 0.0;
end;
end;
-
-end;
-
-procedure TGeneratorObj.InitPropertyValues(ArrayOffset: Integer);
-
-begin
-
- PropertyValue[1] := '3'; //'phases';
- PropertyValue[2] := Getbus(1); //'bus1';
- PropertyValue[3] := '12.47';
- PropertyValue[4] := '100';
- PropertyValue[5] := '.80';
- PropertyValue[6] := '1';
- PropertyValue[7] := '';
- PropertyValue[8] := '';
- PropertyValue[9] := '';
- PropertyValue[10] := 'Default';
- PropertyValue[11] := '0.0';
- PropertyValue[12] := 'wye';
- PropertyValue[13] := '60';
- PropertyValue[14] := '0'; // 'rneut'; // if entered -, assume open
- PropertyValue[15] := '0'; //'xneut';
- PropertyValue[16] := 'variable'; //'status' fixed or variable
- PropertyValue[17] := '1'; //'class'
- PropertyValue[18] := '1.0';
- PropertyValue[19] := Str_Real(kvarMax, 3);
- PropertyValue[20] := Str_Real(kvarMin, 3);
- PropertyValue[21] := '0.1';
- PropertyValue[22] := 'no';
- PropertyValue[23] := '0.90';
- PropertyValue[24] := '1.10';
- PropertyValue[25] := 'No';
- PropertyValue[26] := Format('%-g', [GenVars.kVARating]);
- PropertyValue[27] := Format('%-g', [GenVars.kVARating * 0.001]);
- PropertyValue[28] := Format('%-g', [GenVars.puXd]);
- PropertyValue[29] := Format('%-g', [GenVars.puXdp]);
- PropertyValue[30] := Format('%-g', [GenVars.puXdpp]);
- PropertyValue[31] := Format('%-g', [GenVars.Hmass]);
- PropertyValue[32] := Format('%-g', [GenVars.Dpu]);
- PropertyValue[33] := '';
- PropertyValue[34] := '';
- PropertyValue[35] := '';
- PropertyValue[36] := '';
- PropertyValue[37] := '0';
- PropertyValue[38] := 'No';
- PropertyValue[39] := '20';
- PropertyValue[40] := 'No';
- PropertyValue[41] := '0.0';
- PropertyValue[42] := '100.0';
- PropertyValue[43] := '20.0';
- PropertyValue[44] := 'No';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
end;
procedure TGeneratorObj.InitStateVars;
var
- {VNeut,}
-
+ // VNeut,
i: Integer;
V012,
I012: array[0..2] of Complex;
Vabc: array[1..3] of Complex;
-
begin
YPrimInvalid := TRUE; // Force rebuild of YPrims
with GenVars do
begin
-
case Genmodel of
7:
Zthev := Cmplx(Xdp, 0.0); // use Xd' as an equivalent R for the inverter
@@ -2693,19 +2199,17 @@ procedure TGeneratorObj.InitStateVars;
Yeq := Cinv(Zthev);
- {Compute nominal Positive sequence voltage behind transient reactance}
-
+ // Compute nominal Positive sequence voltage behind transient reactance
if GenON then
with ActiveCircuit.Solution do
begin
-
ComputeIterminal;
case Fnphases of
1:
begin
- Edp := Csub(CSub(NodeV^[NodeRef^[1]], NodeV^[NodeRef^[2]]), Cmul(ITerminal^[1], Zthev));
+ Edp := NodeV^[NodeRef^[1]] - NodeV^[NodeRef^[2]] - ITerminal^[1] * Zthev;
VThevMag := Cabs(Edp);
end;
@@ -2718,25 +2222,23 @@ procedure TGeneratorObj.InitStateVars;
for i := 1 to FNphases do
Vabc[i] := NodeV^[NodeRef^[i]]; // Wye Voltage
Phase2SymComp(pComplexArray(@Vabc), pComplexArray(@V012));
- Edp := Csub(V012[1], Cmul(I012[1], Zthev)); // Pos sequence
+ Edp := V012[1] - I012[1] * Zthev; // Pos sequence
VThevMag := Cabs(Edp);
end;
else
- DoSimpleMsg(Format('Dynamics mode is implemented only for 1- or 3-phase Generators. Generator.' + name + ' has %d phases.', [Fnphases]), 5672);
+ DoSimpleMsg('Dynamics mode is implemented only for 1- or 3-phase Generators. %s has %d phases.', [FullName, Fnphases], 5672);
DSS.SolutionAbort := TRUE;
end;
-
-
- // Shaft variables
- // Theta is angle on Vthev[1] relative to system reference
- //Theta := Cang(Vthev^[1]); // Assume source at 0
+ // Shaft variables
+ // Theta is angle on Vthev[1] relative to system reference
+ //Theta := Cang(Vthev^[1]); // Assume source at 0
Theta := Cang(Edp);
if GenModel = 7 then
Model7LastAngle := Theta;
dTheta := 0.0;
w0 := Twopi * ActiveCircuit.Solution.Frequency;
- // recalc Mmass and D in case the frequency has changed
+ // recalc Mmass and D in case the frequency has changed
with GenVars do
begin
GenVars.Mmass := 2.0 * GenVars.Hmass * GenVars.kVArating * 1000.0 / (w0); // M = W-sec
@@ -2747,8 +2249,8 @@ procedure TGeneratorObj.InitStateVars;
Speed := 0.0; // relative to synch speed
dSpeed := 0.0;
- // Init User-written models
- //Ncond:Integer; V, I:pComplexArray; const X,Pshaft,Theta,Speed,dt,time:Double
+ // Init User-written models
+ //Ncond:Integer; V, I:pComplexArray; const X,Pshaft,Theta,Speed,dt,time:Double
with ActiveCircuit.Solution do
if GenModel = 6 then
begin
@@ -2768,26 +2270,22 @@ procedure TGeneratorObj.InitStateVars;
Speed := 0.0;
dSpeed := 0.0;
end;
- end; {With}
+ end;
end;
procedure TGeneratorObj.IntegrateStates;
-
var
TracePower: Complex;
-
-
begin
// Compute Derivatives and then integrate
ComputeIterminal;
-// Check for user-written exciter model.
+ // Check for user-written exciter model.
//Function(V, I:pComplexArray; const Pshaft,Theta,Speed,dt,time:Double)
with ActiveCircuit.Solution, GenVars do
begin
-
with DynaVars do
if (IterationFlag = 0) then
begin {First iteration of new time step}
@@ -2795,20 +2293,20 @@ procedure TGeneratorObj.IntegrateStates;
SpeedHistory := Speed + 0.5 * h * dSpeed;
end;
- // Compute shaft dynamics
+ // Compute shaft dynamics
TracePower := TerminalPowerIn(Vterminal, Iterminal, FnPhases);
dSpeed := (Pshaft + TracePower.re - D * Speed) / Mmass;
// dSpeed := (Torque + TerminalPowerIn(Vtemp,Itemp,FnPhases).re/Speed) / (Mmass);
dTheta := Speed;
- // Trapezoidal method
+ // Trapezoidal method
with DynaVars do
begin
Speed := SpeedHistory + 0.5 * h * dSpeed;
Theta := ThetaHistory + 0.5 * h * dTheta;
end;
- // Write Dynamics Trace Record
+ // Write Dynamics Trace Record
if DebugTrace then
begin
FSWrite(TraceFile, Format('t=%-.5g ', [Dynavars.t]));
@@ -2829,17 +2327,13 @@ procedure TGeneratorObj.IntegrateStates;
if ShaftModel.Exists then
ShaftModel.Integrate;
end;
-
-
end;
end;
function TGeneratorObj.Get_Variable(i: Integer): Double;
-{Return variables one at a time}
-
+// Return variables one at a time
var
N, k: Integer;
-
begin
N := 0;
Result := -9999.99; // error return value
@@ -2861,34 +2355,32 @@ function TGeneratorObj.Get_Variable(i: Integer): Double;
6:
Result := dTheta;
else
- begin
- if UserModel.Exists then
begin
- N := UserModel.FNumVars;
- k := (i - NumGenVariables);
- if k <= N then
+ if UserModel.Exists then
begin
- Result := UserModel.FGetVariable(k);
- Exit;
+ N := UserModel.FNumVars;
+ k := (i - NumGenVariables);
+ if k <= N then
+ begin
+ Result := UserModel.FGetVariable(k);
+ Exit;
+ end;
end;
- end;
- {If we get here, must be in the Shaft Model if anywhere}
- if ShaftModel.Exists then
- begin
- k := i - (NumGenVariables + N);
- if k > 0 then
- Result := ShaftModel.FGetVariable(k);
+ // If we get here, must be in the Shaft Model if anywhere
+ if ShaftModel.Exists then
+ begin
+ k := i - (NumGenVariables + N);
+ if k > 0 then
+ Result := ShaftModel.FGetVariable(k);
+ end;
end;
end;
- end;
-
end;
procedure TGeneratorObj.Set_Variable(i: Integer; Value: Double);
var
N, k: Integer;
-
begin
N := 0;
if i < 1 then
@@ -2987,135 +2479,109 @@ function TGeneratorObj.VariableName(i: Integer): String;
6:
Result := 'dTheta (Deg)';
else
- begin
- if UserModel.Exists then // Checks for existence and Selects
begin
- pName := PAnsiChar(@Buff);
- n := UserModel.FNumVars;
- i2 := i - NumGenVariables;
- if i2 <= n then
+ if UserModel.Exists then // Checks for existence and Selects
begin
- // DLL functions require AnsiString type
- UserModel.FGetVarName(i2, pName, BuffSize);
- Result := String(pName);
- Exit;
+ pName := PAnsiChar(@Buff);
+ n := UserModel.FNumVars;
+ i2 := i - NumGenVariables;
+ if i2 <= n then
+ begin
+ // DLL functions require AnsiString type
+ UserModel.FGetVarName(i2, pName, BuffSize);
+ Result := String(pName);
+ Exit;
+ end;
end;
- end;
-
- if ShaftModel.Exists then
- begin
- pName := PAnsiChar(Buff);
- i2 := i - NumGenVariables - n;
- if i2 > 0 then
- UserModel.FGetVarName(i2, pName, BuffSize);
- Result := String(pName);
- end;
- end;
- end;
-
-end;
-function TGeneratorObj.GetPropertyValue(Index: Integer): String;
-
-begin
- Result := '';
- case Index of
- 3:
- Result := Format('%.6g', [Genvars.kVGeneratorBase]);
- 4:
- Result := Format('%.6g', [kWBase]);
- 5:
- Result := Format('%.6g', [PFNominal]);
- 7:
- Result := Yearlyshape;
- 8:
- Result := Dailydispshape;
- 9:
- Result := DutyShape;
- 13:
- Result := Format('%.6g', [kvarBase]);
- 19:
- Result := Format('%.6g', [kvarMax]);
- 20:
- Result := Format('%.6g', [kvarMin]);
- 26:
- Result := Format('%.6g', [Genvars.kVArating]);
- 27:
- Result := Format('%.6g', [Genvars.kVArating * 0.001]);
- 34, 36:
- begin
- Result := '(' + inherited GetPropertyValue(index) + ')';
+ if ShaftModel.Exists then
+ begin
+ pName := PAnsiChar(Buff);
+ i2 := i - NumGenVariables - n;
+ if i2 > 0 then
+ UserModel.FGetVarName(i2, pName, BuffSize);
+ Result := String(pName);
+ end;
end;
- 37:
- Result := Format('%.6g', [DutyStart]);
- 38:
- Result := StrYorN(ForceBalanced);
- 40:
- Result := StrYorN(UseFuel);
- 41:
- Result := Format('%.6g', [FuelkWh]);
- 42:
- Result := Format('%.6g', [pctFuel]);
- 43:
- Result := Format('%.6g', [pctReserve]);
- 44:
- Result := 'No';
- else
- Result := inherited GetPropertyValue(index);
end;
end;
-procedure TGeneratorObj.MakePosSequence;
-
+procedure TGeneratorObj.MakePosSequence();
var
- S: String;
V: Double;
-
+ had_kVA, had_MVA, had_kvars: Boolean;
+ kW_new, PF_new, new_kVA, new_MVA, new_minkvar, new_maxkvar: Double;
+ oldPhases, changes: Integer;
begin
-
- S := 'Phases=1 conn=wye';
-
- // Make sure voltage is line-neutral
+ // Make sure voltage is line-neutral
if (Fnphases > 1) or (connection <> 0) then
V := GenVars.kVGeneratorBase / SQRT3
else
V := GenVars.kVGeneratorBase;
- S := S + Format(' kV=%-.5g', [V]);
-
- // Divide the load by no. phases
+ // Divide the load by no. phases
+ changes := 3;
+ oldPhases := Fnphases;
if Fnphases > 1 then
begin
- S := S + Format(' kW=%-.5g PF=%-.5g', [kWbase / Fnphases, PFNominal]);
- if (PrpSequence^[19] <> 0) or (PrpSequence^[20] <> 0) then
- S := S + Format(' maxkvar=%-.5g minkvar=%-.5g', [kvarmax / Fnphases, kvarmin / Fnphases]);
- if PrpSequence^[26] > 0 then
- S := S + Format(' kva=%-.5g ', [genvars.kvarating / Fnphases]);
- if PrpSequence^[27] > 0 then
- S := S + Format(' MVA=%-.5g ', [genvars.kvarating / 1000.0 / Fnphases]);
+ had_kVA := PrpSequence[26] > 0;
+ had_MVA := PrpSequence[27] > 0;
+ had_kvars := (PrpSequence[19] <> 0) or (PrpSequence[20] <> 0);
+ kW_new := kWbase / Fnphases;
+ PF_new := PFNominal;
+ if had_kvars then
+ begin
+ new_minkvar := kvarmin / Fnphases;
+ new_maxkvar := kvarmax / Fnphases;
+ Inc(changes);
+ end;
+ if had_kVA then
+ begin
+ new_kVA := genvars.kvarating / Fnphases;
+ Inc(changes);
+ end;
+ if had_MVA then
+ begin
+ new_MVA := genvars.kvarating / 1000.0 / Fnphases;
+ Inc(changes);
+ end;
end;
- Parser.CmdString := S;
- Edit;
+ BeginEdit(True);
+ SetInteger(ord(TProp.Phases), 1);
+ SetInteger(ord(TProp.conn), 0);
+ SetDouble(ord(TProp.kV), V);
+ if oldPhases > 1 then
+ begin
+ SetDouble(ord(TProp.kW), kW_new);
+ SetDouble(ord(TProp.PF), PF_new);
+ if had_kvars then
+ begin
+ SetDouble(ord(TProp.minkvar), new_minkvar);
+ SetDouble(ord(TProp.maxkvar), new_maxkvar);
+ end;
+ if had_kVA then
+ SetDouble(ord(TProp.kVA), new_kVA);
+ if had_MVA then
+ SetDouble(ord(TProp.MVA), new_MVA);
+ end;
+ EndEdit(changes);
inherited;
end;
-procedure TGeneratorObj.Set_ConductorClosed(Index: Integer;
- Value: Boolean);
+procedure TGeneratorObj.Set_ConductorClosed(Index: Integer; Value: Boolean);
begin
inherited;
- // Just turn generator on or off;
+ // Just turn generator on or off;
if Value then
GenSwitchOpen := FALSE
else
GenSwitchOpen := TRUE;
-
end;
-
procedure TGeneratorObj.Set_PowerFactor(const Value: Double);
begin
PFNominal := Value;
@@ -3124,49 +2590,19 @@ procedure TGeneratorObj.Set_PowerFactor(const Value: Double);
procedure TGeneratorObj.Set_PresentkV(const Value: Double);
begin
- with Genvars do
- begin
- kVGeneratorBase := Value;
- case FNphases of
- 2, 3:
- VBase := kVGeneratorBase * InvSQRT3x1000;
- else
- VBase := kVGeneratorBase * 1000.0;
- end;
- end;
-end;
-
-procedure TGeneratorObj.Set_Presentkvar(const Value: Double);
-var
- kVA_Gen: Double;
-
-begin
- kvarBase := Value;
- Genvars.Qnominalperphase := 1000.0 * kvarBase / Fnphases; // init to something reasonable
- kVA_Gen := Sqrt(Sqr(kWBase) + Sqr(kvarBase));
- if kVA_Gen <> 0.0 then
- PFNominal := kWBase / kVA_Gen
- else
- PFNominal := 1.0;
- if (kWBase * kvarBase) < 0.0 then
- PFNominal := -PFNominal;
-
- kvarMax := 2.0 * kvarBase;
- kvarMin := -kvarMax;
+ Genvars.kVGeneratorBase := Value;
+ PropertySideEffects(ord(TProp.kV));
end;
procedure TGeneratorObj.Set_PresentkW(const Value: Double);
begin
-
kWBase := Value;
SyncUpPowerQuantities;
-
end;
procedure TGeneratorObj.SyncUpPowerQuantities;
begin
-
- // keep kvar nominal up to date with kW and PF
+ // keep kvar nominal up to date with kW and PF
if (PFNominal <> 0.0) then
begin
kvarBase := kWBase * sqrt(1.0 / Sqr(PFNominal) - 1.0);
@@ -3179,9 +2615,7 @@ procedure TGeneratorObj.SyncUpPowerQuantities;
if kVANotSet then
GenVars.kVARating := kWBase * 1.2;
-
end;
-
end;
procedure TGeneratorObj.SetDragHandRegister(Reg: Integer;
@@ -3193,10 +2627,9 @@ procedure TGeneratorObj.SetDragHandRegister(Reg: Integer;
procedure TGeneratorObj.SetkWkvar(const PkW, Qkvar: Double);
begin
-
kWBase := PkW;
- Presentkvar := Qkvar;
-
+ kvarBase := Qkvar;
+ PropertySideEffects(ord(TProp.kvar));
end;
procedure TGeneratorObj.CalcVthev_Dyn;
@@ -3207,21 +2640,18 @@ procedure TGeneratorObj.CalcVthev_Dyn;
end;
procedure TGeneratorObj.CalcVthev_Dyn_Mod7(const V: Complex);
-{Adjust VThev to be in phase with V, if possible}
-{
- If the voltage magnitude drops below 15% or so, the accuracy of determining the
- phase angle gets flaky. This algorithm approximates the action of a PLL that will
- hold the last phase angle until the voltage recovers.
-}
+// Adjust VThev to be in phase with V, if possible
+
+// If the voltage magnitude drops below 15% or so, the accuracy of determining the
+// phase angle gets flaky. This algorithm approximates the action of a PLL that will
+// hold the last phase angle until the voltage recovers.
var
Model7angle: Double;
begin
if GenSwitchOpen then
GenVars.VThevMag := 0.0;
- {
- For Phases=1, Vbase is voltage across the terminals.
- Else it is LN voltage.
- }
+ // For Phases=1, Vbase is voltage across the terminals.
+ // Else it is LN voltage.
if Cabs(V) > 0.2 * Vbase then
Model7angle := Cang(V)
else
@@ -3229,12 +2659,8 @@ procedure TGeneratorObj.CalcVthev_Dyn_Mod7(const V: Complex);
Vthev := pclx(GenVars.VthevMag, Model7angle);
Model7Lastangle := Model7angle;
-
end;
-initialization
-
- CDOUBLEONE := CMPLX(1.0, 1.0);
-// TWOPI3 := twopi/3.0;
-
+finalization GenStatusEnum.Free;
+ GenDispModeEnum.Free;
end.
diff --git a/src/PCElements/vccs.pas b/src/PCElements/vccs.pas
index 41df5d77d..c5585b718 100644
--- a/src/PCElements/vccs.pas
+++ b/src/PCElements/vccs.pas
@@ -16,33 +16,46 @@ interface
PCClass,
PCElement,
ucmatrix,
- ucomplex,
+ UComplex, DSSUcomplex,
XYCurve,
ArrayDef;
type
+{$SCOPEDENUMS ON}
+ TVCCSProp = (
+ INVALID = 0,
+ bus1 = 1,
+ phases = 2,
+ prated = 3,
+ vrated = 4,
+ ppct = 5,
+ bp1 = 6,
+ bp2 = 7,
+ filter = 8,
+ fsample = 9,
+ rmsmode = 10,
+ imaxpu = 11,
+ vrmstau = 12,
+ irmstau = 13
+ );
+{$SCOPEDENUMS OFF}
+
TVCCS = class(TPCClass)
- PRIVATE
- XY_CurveClass: TDSSClass;
PROTECTED
- procedure DefineProperties;
- function MakeLike(const OtherSource: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE;
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TVCCSObj = class(TPCElement)
PRIVATE
Fbp1: TXYcurveObj;
- Fbp1_name: String;
Fbp2: TXYcurveObj;
- Fbp2_name: String;
Ffilter: TXYcurveObj;
- Ffilter_name: String;
BaseCurr: Double; // line current at Ppct
BaseVolt: Double; // line-to-neutral voltage at Vrated
FsampleFreq: Double; // discretization frequency for Z filter
@@ -52,7 +65,7 @@ TVCCSObj = class(TPCElement)
Fkv: Double; // scale voltage to HW pu input
Fki: Double; // scale HW pu output to current
- FrmsMode: Boolean; // indicates a phasor-domain PLL simulation
+ FrmsMode: LongBool; // indicates a phasor-domain PLL simulation
FmaxIpu: Double; // maximum RMS current in per-unit of rated
FvrmsTau: Double; // LPF time constant sensing Vrms
FirmsTau: Double; // LPF time constant producing Irms
@@ -89,18 +102,17 @@ TVCCSObj = class(TPCElement)
Ppct, Prated, Vrated: Double;
constructor Create(ParClass: TDSSClass; const SourceName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
function InjCurrents: Integer; OVERRIDE;
procedure GetCurrents(Curr: pComplexArray); OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
// Support for Dynamics Mode
procedure InitStateVars; OVERRIDE;
procedure IntegrateStates; OVERRIDE;
@@ -113,7 +125,6 @@ TVCCSObj = class(TPCElement)
implementation
uses
- ParserDel,
Circuit,
DSSClassDefs,
DSSGlobals,
@@ -125,8 +136,13 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TVCCSObj;
+ TProp = TVCCSProp;
+const
+ NumPropsThisClass = Ord(High(TProp));
var
- NumPropsThisClass: Integer;
+ PropInfo: Pointer = NIL;
ALPHA1, ALPHA2: complex;
// helper functions for ring buffer indexing, 1..len
@@ -144,19 +160,12 @@ function OffsetIdx(idx, offset, len: Integer): Integer;
Result := MapIdx(idx + offset, len);
end;
-constructor TVCCS.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TVCCS.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'VCCS';
- DSSClassType := VCCS_ELEMENT + PC_ELEMENT; // participates in dynamics
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- ActiveElement := 0;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
- XY_CurveClass := GetDSSClassPtr(DSS, 'XYCurve');
+ inherited Create(dssContext, VCCS_ELEMENT, 'VCCS');
end;
destructor TVCCS.Destroy;
@@ -165,198 +174,119 @@ destructor TVCCS.Destroy;
end;
procedure TVCCS.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
- NumPropsThisClass := 13;
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
- PropertyName[1] := 'bus1';
- PropertyName[2] := 'phases';
- PropertyName[3] := 'prated';
- PropertyName[4] := 'vrated';
- PropertyName[5] := 'ppct';
- PropertyName[6] := 'bp1';
- PropertyName[7] := 'bp2';
- PropertyName[8] := 'filter';
- PropertyName[9] := 'fsample';
- PropertyName[10] := 'rmsmode';
- PropertyName[11] := 'imaxpu';
- PropertyName[12] := 'vrmstau';
- PropertyName[13] := 'irmstau';
-
- // define Property help values
- PropertyHelp[1] := 'Name of bus to which source is connected.' + CRLF + 'bus1=busname' + CRLF + 'bus1=busname.1.2.3';
- PropertyHelp[2] := 'Number of phases. Defaults to 1.';
- PropertyHelp[3] := 'Total rated power, in Watts.';
- PropertyHelp[4] := 'Rated line-to-line voltage, in Volts';
- PropertyHelp[5] := 'Steady-state operating output, in percent of rated.';
- PropertyHelp[6] := 'XYCurve defining the input piece-wise linear block.';
- PropertyHelp[7] := 'XYCurve defining the output piece-wise linear block.';
- PropertyHelp[8] := 'XYCurve defining the digital filter coefficients (x numerator, y denominator).';
- PropertyHelp[9] := 'Sample frequency [Hz} for the digital filter.';
- PropertyHelp[10] := 'True if only Hz is used to represent a phase-locked loop (PLL), ignoring the BP1, BP2 and time-domain transformations. Default is no.';
- PropertyHelp[11] := 'Maximum output current in per-unit of rated; defaults to 1.1';
- PropertyHelp[12] := 'Time constant in sensing Vrms for the PLL; defaults to 0.0015';
- PropertyHelp[13] := 'Time constant in producing Irms from the PLL; defaults to 0.0015';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo, False);
+
+ // integer properties
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+
+ // boolean properties
+ PropertyType[ord(TProp.rmsmode)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.rmsmode)] := ptruint(@obj.FrmsMode);
+
+ // object properties
+ PropertyType[ord(TProp.bp1)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.bp2)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.filter)] := TPropertyType.DSSObjectReferenceProperty;
+
+ PropertyOffset[ord(TProp.bp1)] := ptruint(@obj.Fbp1);
+ PropertyOffset[ord(TProp.bp2)] := ptruint(@obj.Fbp2);
+ PropertyOffset[ord(TProp.filter)] := ptruint(@obj.Ffilter);
+
+ PropertyOffset2[ord(TProp.bp1)] := ptruint(DSS.XYCurveClass);
+ PropertyOffset2[ord(TProp.bp2)] := ptruint(DSS.XYCurveClass);
+ PropertyOffset2[ord(TProp.filter)] := ptruint(DSS.XYCurveClass);
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.prated)] := ptruint(@obj.Prated);
+ PropertyOffset[ord(TProp.vrated)] := ptruint(@obj.Vrated);
+ PropertyOffset[ord(TProp.ppct)] := ptruint(@obj.Ppct);
+ PropertyOffset[ord(TProp.fsample)] := ptruint(@obj.FsampleFreq);
+ PropertyOffset[ord(TProp.imaxpu)] := ptruint(@obj.FmaxIpu);
+ PropertyOffset[ord(TProp.vrmstau)] := ptruint(@obj.FvrmsTau);
+ PropertyOffset[ord(TProp.irmstau)] := ptruint(@obj.FirmsTau);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
+ inherited DefineProperties;
+end;
- // Override help string
- PropertyHelp[NumPropsThisClass + 1] := 'Harmonic spectrum assumed for this source. Default is "default".';
+function TVCCS.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
+begin
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-function TVCCS.NewObject(const ObjName: String): Integer;
+procedure TVCCSObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- with ActiveCircuit do
- begin
- ActiveCktElement := TVCCSObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
+ case Idx of
+ ord(TProp.phases):
+ if FNPhases <> previousIntVal then
+ NConds := Fnphases; // Force Reallocation of terminal info
end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-function TVCCS.Edit: Integer;
-var
- ParamPointer: Integer;
- ParamName,
- Param: String;
+function TVCCS.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
begin
- // continue parsing with contents of Parser
- DSS.ActiveVCCSObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveVCCSObj;
- Result := 0;
-
- with DSS.ActiveVCCSObj do
+ with TObj(ptr) do
begin
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 330);
- 1:
- SetBus(1, param);
- 2:
- begin
- Nphases := Parser.IntValue; // num phases
- NConds := Fnphases; // Force Reallocation of terminal info
- end;
- 3:
- Prated := Parser.DblValue;
- 4:
- Vrated := Parser.DblValue;
- 5:
- Ppct := Parser.DblValue;
- 6:
- begin
- Fbp1_name := Parser.StrValue;
- if Length(Fbp1_name) > 0 then
- begin
- Fbp1 := XY_CurveClass.Find(Fbp1_name);
- end;
- end;
- 7:
- begin
- Fbp2_name := Parser.StrValue;
- if Length(Fbp2_name) > 0 then
- begin
- Fbp2 := XY_CurveClass.Find(Fbp2_name);
- end;
- end;
- 8:
- begin
- Ffilter_name := Parser.StrValue;
- if Length(Ffilter_name) > 0 then
- begin
- Ffilter := XY_CurveClass.Find(Ffilter_name);
- end;
- end;
- 9:
- FsampleFreq := Parser.DblValue;
- 10:
- FrmsMode := InterpretYesNo(Param);
- 11:
- FmaxIpu := Parser.DblValue;
- 12:
- FvrmsTau := Parser.DblValue;
- 13:
- FirmsTau := Parser.DblValue;
- else
- ClassEdit(DSS.ActiveVCCSObj, ParamPointer - NumPropsThisClass)
- end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- end;
RecalcElementData;
YPrimInvalid := TRUE;
+ Exclude(Flags, Flg.EditionActive);
end;
+ Result := True;
end;
-//----------------------------------------------------------------------------
-function TVCCS.MakeLike(const OtherSource: String): Integer;
+procedure TVCCSObj.MakeLike(OtherPtr: Pointer);
var
- OtherVCCS: TVCCSObj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this line name in the present collection}
- OtherVCCS := Find(OtherSource);
- if OtherVCCS <> NIL then
- with DSS.ActiveVCCSObj do
- begin
- if Fnphases <> OtherVCCS.Fnphases then
- begin
- Nphases := OtherVCCS.Fnphases;
- NConds := Fnphases; // Forces reallocation of terminal stuff
+ inherited MakeLike(OtherPtr);
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
- end;
- Prated := OtherVCCS.Prated;
- Vrated := OtherVCCS.Vrated;
- Ppct := OtherVCCS.Ppct;
- Fbp1 := OtherVCCS.Fbp1;
- Fbp2 := OtherVCCS.Fbp2;
- Ffilter := OtherVCCS.Ffilter;
- Fbp1_name := OtherVCCS.Fbp1_name;
- Fbp2_name := OtherVCCS.Fbp2_name;
- Ffilter_name := OtherVCCS.Ffilter_name;
- FsampleFreq := OtherVCCS.FsampleFreq;
- FrmsMode := OtherVCCS.FrmsMode;
- FmaxIpu := OtherVCCS.FmaxIpu;
- FvrmsTau := OtherVCCS.FvrmsTau;
- FirmsTau := OtherVCCS.FirmsTau;
-
- ClassMakeLike(OtherVCCS); // set spectrum, base frequency
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherVCCS.PropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in VCCS MakeLike: "' + OtherSource + '" Not Found.', 332);
+ Other := TObj(OtherPtr);
+ if Fnphases <> Other.Fnphases then
+ begin
+ FNphases := Other.Fnphases;
+ NConds := Fnphases; // Forces reallocation of terminal stuff
+
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
+ end;
+ Prated := Other.Prated;
+ Vrated := Other.Vrated;
+ Ppct := Other.Ppct;
+ Fbp1 := Other.Fbp1;
+ Fbp2 := Other.Fbp2;
+ Ffilter := Other.Ffilter;
+ FsampleFreq := Other.FsampleFreq;
+ FrmsMode := Other.FrmsMode;
+ FmaxIpu := Other.FmaxIpu;
+ FvrmsTau := Other.FvrmsTau;
+ FirmsTau := Other.FirmsTau;
end;
constructor TVCCSObj.Create(ParClass: TDSSClass; const SourceName: String);
begin
inherited create(ParClass);
- Name := LowerCase(SourceName);
+ Name := AnsiLowerCase(SourceName);
DSSObjType := ParClass.DSSClassType;
- Nphases := 1;
+ FNphases := 1;
Fnconds := 1;
Nterms := 1;
@@ -372,17 +302,17 @@ constructor TVCCSObj.Create(ParClass: TDSSClass; const SourceName: String);
FirmsTau := 0.0015;
Fwinlen := 0;
- Ffilter_name := '';
- Fbp1_name := '';
- Fbp2_name := '';
+
+ Ffilter := NIL;
+ Fbp1 := NIL;
+ Fbp2 := NIL;
+
y2 := NIL;
z := NIL;
whist := NIL;
zlast := NIL;
wlast := NIL;
- InitPropertyValues(0);
-
Yorder := Fnterms * Fnconds;
RecalcElementData;
end;
@@ -399,11 +329,6 @@ destructor TVCCSObj.Destroy;
procedure TVCCSObj.RecalcElementData;
begin
- SpectrumObj := DSS.SpectrumClass.Find(Spectrum);
- if SpectrumObj = NIL then
- begin
- DoSimpleMsg('Spectrum Object "' + Spectrum + '" for Device VCCS.' + Name + ' Not Found.', 333);
- end;
Reallocmem(InjCurrent, SizeOf(InjCurrent^[1]) * Yorder);
Irated := Prated / Vrated / FNphases;
@@ -417,7 +342,7 @@ procedure TVCCSObj.RecalcElementData;
Fkv := 1.0 / BaseVolt / sqrt(2.0);
Fki := BaseCurr * sqrt(2.0);
- if Length(Ffilter_name) > 0 then
+ if Ffilter <> NIL then
begin
Ffiltlen := Ffilter.NumPoints;
Fwinlen := Trunc(FsampleFreq / BaseFrequency);
@@ -470,18 +395,18 @@ procedure TVCCSObj.GetCurrents(Curr: pComplexArray);
GetInjCurrents(ComplexBuffer); // Get present value of inj currents
// Add Together with yprim currents
for i := 1 to Yorder do
- Curr^[i] := Cnegate(ComplexBuffer^[i]);
+ Curr^[i] := -ComplexBuffer^[i];
except
On E: Exception do
- DoErrorMsg(('GetCurrents for VCCS Element: ' + Name + '.'), E.Message,
- 'Inadequate storage allotted for circuit element?', 335);
+ DoErrorMsg(Format(_('GetCurrents for VCCS Element: %s.'), [Name]),
+ E.Message, _('Inadequate storage allotted for circuit element?'), 335);
end;
end;
procedure TVCCSObj.UpdateSequenceVoltage;
begin
if FNPhases = 3 then
- sV1 := cdivreal(cadd(Vterminal^[1], cadd(cmul(ALPHA1,Vterminal^[2]), cmul(ALPHA2,Vterminal^[3]))), 3.0)
+ sV1 := (Vterminal^[1] + (ALPHA1 * Vterminal^[2] + ALPHA2 * Vterminal^[3])) / 3.0
else
sV1 := Vterminal^[1];
end;
@@ -502,7 +427,7 @@ procedure TVCCSObj.GetInjCurrents(Curr: pComplexArray);
UpdateSequenceVoltage;
// IterminalUpdated := FALSE;
- if DSS.ActiveSolutionObj.IsDynamicModel then
+ if ActiveCircuit.Solution.IsDynamicModel then
begin
if FrmsMode then
begin
@@ -513,8 +438,8 @@ procedure TVCCSObj.GetInjCurrents(Curr: pComplexArray);
3:
begin
Curr^[1] := i1;
- Curr^[2] := cmul(i1, ALPHA2);
- Curr^[3] := cmul(i1, ALPHA1);
+ Curr^[2] := i1 * ALPHA2;
+ Curr^[3] := i1 * ALPHA1;
end;
else
for i := 1 to Fnphases do
@@ -540,48 +465,10 @@ procedure TVCCSObj.GetInjCurrents(Curr: pComplexArray);
end;
end;
-procedure TVCCSObj.DumpProperties(F: TFileStream; Complete: Boolean);
-var
- i: Integer;
-begin
- inherited DumpProperties(F, Complete);
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
- if Complete then
- begin
- FSWriteln(F);
- FSWriteln(F);
- end;
-end;
-
-procedure TVCCSObj.InitPropertyValues(ArrayOffset: Integer);
-begin
- PropertyValue[1] := GetBus(1);
- PropertyValue[2] := '1';
- PropertyValue[3] := '250';
- PropertyValue[4] := '208';
- PropertyValue[5] := '100';
- PropertyValue[6] := 'NONE';
- PropertyValue[7] := 'NONE';
- PropertyValue[8] := 'NONE';
- PropertyValue[9] := '5000';
- PropertyValue[10] := 'no';
- PropertyValue[11] := '1.1';
- PropertyValue[12] := '0.0015';
- PropertyValue[13] := '0.0015';
- inherited InitPropertyValues(NumPropsThisClass);
-end;
-
-procedure TVCCSObj.MakePosSequence;
+procedure TVCCSObj.MakePosSequence();
begin
if Fnphases > 1 then
- begin
- Parser.CmdString := 'phases=1';
- Edit;
- end;
+ SetInteger(ord(TProp.Phases), 1);
inherited;
end;
@@ -621,7 +508,7 @@ procedure TVCCSObj.InitPhasorStates;
s5 := 0;
s6 := 0;
sV1 := cmplx(1.0, 0.0);
- vlast := cdivreal(Vterminal^[1], BaseVolt);
+ vlast := Vterminal^[1] / BaseVolt;
// initialize the history terms for HW model source convention
for i := 1 to Ffiltlen do
@@ -667,11 +554,11 @@ procedure TVCCSObj.InitStateVars;
s5 := 0;
s6 := 0;
sV1 := cmplx(1.0, 0.0);
- vlast := cdivreal(Vterminal^[1], BaseVolt);
+ vlast := Vterminal^[1] / BaseVolt;
// initialize the history terms for HW model source convention
d := 1 / FsampleFreq;
- wd := 2 * Pi * DSS.ActiveSolutionObj.Frequency * d;
+ wd := 2 * Pi * ActiveCircuit.Solution.Frequency * d;
for i := 1 to Ffiltlen do
begin
wt := vang - wd * (Ffiltlen - i);
@@ -707,8 +594,8 @@ procedure TVCCSObj.IntegratePhasorStates;
vpu := cabs(sV1) / BaseVolt;
if vpu > 0.0 then
begin
- h := DSS.ActiveSolutionObj.DynaVars.h;
- corrector := DSS.ActiveSolutionObj.DynaVars.IterationFlag;
+ h := ActiveCircuit.Solution.DynaVars.h;
+ corrector := ActiveCircuit.Solution.DynaVars.IterationFlag;
nstep := trunc(1e-6 + h * FSampleFreq);
// Vrms from LPF
d := vpu - s1;
@@ -780,15 +667,15 @@ procedure TVCCSObj.IntegrateStates;
ComputeIterminal;
- t := DSS.ActiveSolutionObj.DynaVars.t;
- h := DSS.ActiveSolutionObj.DynaVars.h;
- f := DSS.ActiveSolutionObj.Frequency;
- corrector := DSS.ActiveSolutionObj.DynaVars.IterationFlag;
+ t := ActiveCircuit.Solution.DynaVars.t;
+ h := ActiveCircuit.Solution.DynaVars.h;
+ f := ActiveCircuit.Solution.Frequency;
+ corrector := ActiveCircuit.Solution.DynaVars.IterationFlag;
d := 1 / FSampleFreq;
nstep := trunc(1e-6 + h / d);
w := 2 * Pi * f;
- vnow := cdivreal(Vterminal^[1], BaseVolt);
+ vnow := Vterminal^[1] / BaseVolt;
vin := 0;
y := 0;
iu := sIdxU;
@@ -943,4 +830,5 @@ procedure TVCCSObj.Set_Variable(i: Integer; Value: Double);
initialization
ALPHA1 := cmplx(-0.5, 0.5 * sqrt(3.0)); // 1 at 120 degrees
ALPHA2 := cmplx(-0.5, -ALPHA1.im); // 1 at 240 degrees
+ PropInfo := NIL;
end.
diff --git a/src/PDElements/AutoTrans.pas b/src/PDElements/AutoTrans.pas
index fda8ef847..dcfe12eb6 100644
--- a/src/PDElements/AutoTrans.pas
+++ b/src/PDElements/AutoTrans.pas
@@ -7,17 +7,12 @@
----------------------------------------------------------
}
-{
- Change log
- 7-14-2018 Created from Transformer
- 9-19-2018 committed
- 12-4-2018 Corrected indices for mapping into Yprim
- 1-3-2019 Default last nphase nodes of X terminal (2) to same as first neutral node
- 3-6-2021 Added code for readability
-}
-
-{ You can designate a AutoTrans to be a substation by setting the sub=yes parameter}
-
+// Change log
+// 7-14-2018 Created from Transformer
+// 9-19-2018 committed
+// 12-4-2018 Corrected indices for mapping into Yprim
+// 1-3-2019 Default last nphase nodes of X terminal (2) to same as first neutral node
+// 3-6-2021 Added code for readability
interface
@@ -28,14 +23,64 @@ interface
PDClass,
Circuit,
PDElement,
- uComplex,
+ UComplex, DSSUcomplex,
UcMatrix,
- ParserDel,
Arraydef,
math;
type
{$SCOPEDENUMS ON}
+ TAutoTransProp = (
+ INVALID = 0,
+ phases=1,
+ windings=2,
+
+ // Winding Definition
+ wdg=3,
+ bus=4,
+ conn=5,
+ kV=6,
+ kVA=7,
+ tap=8,
+ pctR=9,
+ Rdcohms=10,
+ Core=11,
+
+ // General Data
+ buses=12,
+ conns=13,
+ kVs=14,
+ kVAs=15,
+ taps=16,
+ XHX=17,
+ XHT=18,
+ XXT=19,
+ XSCarray=20,
+ thermal=21,
+ n=22,
+ m=23,
+ flrise=24,
+ hsrise=25,
+ pctloadloss=26,
+ pctnoloadloss=27,
+ normhkVA=28,
+ emerghkVA=29,
+ sub=30,
+ MaxTap=31,
+ MinTap=32,
+ NumTaps=33,
+ subname=34,
+ pctimag=35,
+ ppm_antifloat=36,
+ pctRs=37,
+
+ //bank=38, // removed, unused
+ //XfmrCode=39, // removed, unused
+ XRConst=38, //- was 40
+ LeadLag=39, // was 41
+ WdgCurrents=40 // was 42
+ );
+
TAutoTransConnection = (
Wye = 0,
Delta = 1,
@@ -45,35 +90,17 @@ interface
TAutoTrans = class(TPDClass)
-
- PRIVATE
-
- procedure SetActiveWinding(w: Integer);
- procedure InterpretAutoConnection(const S: String);
- procedure InterpretAllConns(const S: String);
- procedure InterpretAllBuses(const S: String);
- procedure InterpretAllTaps(const S: String);
- procedure InterpretAllkVRatings(const S: String);
- procedure InterpretAllkVARatings(const S: String);
- procedure InterpretAllRs(const S: String);
- function TrapZero(const Value: Double; DefaultValue: Double): Double;
- function InterpretLeadLag(const S: String): Boolean;
-
- {PROCEDURE MakeNewBusNameForNeutral(Var NewBusName:String; Nphases:Integer);}
PROTECTED
- procedure DefineProperties;
- function MakeLike(const AutoTransfName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ function BeginEdit(ptr: Pointer; SetActive: Boolean=True): Pointer; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
- TAutoWinding = class(Tobject)
- PUBLIC
+ TAutoWinding = object
Connection: TAutoTransConnection;
kVLL,
VBase,
@@ -93,9 +120,7 @@ TAutoWinding = class(Tobject)
NumTaps: Integer;
procedure ComputeAntiFloatAdder(PPM_Factor, VABase1ph: Double);
-
- constructor Create(iWinding: Integer);
- destructor Destroy; OVERRIDE;
+ procedure Init(iWinding: Integer);
end;
AutoWindingArray = array[1..3] of TAutoWinding;
@@ -106,8 +131,7 @@ TAutoTransObj = class(TPDElement)
DeltaDirection: Integer;
ppm_FloatFactor: Double; // parts per million winding float factor
- pctImag: Double;
- XRConst: Boolean;
+ XRConst: LongBool;
function Get_PresentTap(i: Integer): Double;
procedure Set_PresentTap(i: Integer; const Value: Double);
@@ -122,21 +146,13 @@ TAutoTransObj = class(TPDElement)
function Get_WdgConnection(i: Integer): Integer;
function Get_WdgkVA(i: Integer): Double;
function Get_Xsc(i: Integer): Double;
- function Get_WdgYPPM(i: Integer): Double;
procedure CalcY_Terminal(FreqMult: Double);
procedure GICBuildYTerminal;
procedure BuildYPrimComponent(YPrim_Component, Y_Terminal: TCMatrix);
- procedure FetchXfmrCode(const Code: String);
-
- function GetAutoWindingCurrentsResult: String;
-
- procedure SetBusAuto(iwdg: Integer; const s: String);
-
PROTECTED
- NumWindings: Integer;
MaxWindings: Integer;
TermRef: pIntegerArray; // keeps track of terminal connections
@@ -162,27 +178,31 @@ TAutoTransObj = class(TPDElement)
m_thermal: Double; {Exponents}
FLrise: Double;
HSrise: Double;
- pctLoadLoss: Double;
- pctNoLoadLoss: Double;
- HVLeadsLV: Boolean;
+ HVLeadsLV: LongBool;
XHXChanged: Boolean;
procedure SetTermRef;
PUBLIC
+ NumWindings: Integer;
ActiveWinding: Integer; // public for COM interface
- IsSubstation: Boolean;
+ IsSubstation: LongBool;
SubstationName: String;
Winding: pAutoWindingArray;
XfmrBank: String;
XfmrCode: String;
CoreType: Integer; {0=Shell; 1=1ph; 3-3leg; 5=5-leg}
- strCoreType: String;
+ pctImag: Double;
+ pctLoadLoss: Double;
+ pctNoLoadLoss: Double;
constructor Create(ParClass: TDSSClass; const TransfName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
+ procedure SetBus(iwdg: Integer; const s: String); override;
procedure SetNumWindings(N: Integer);
@@ -190,21 +210,19 @@ TAutoTransObj = class(TPDElement)
procedure SetNodeRef(iTerm: Integer; NodeRefArray: pIntegerArray); OVERRIDE;
procedure CalcYPrim; OVERRIDE;
- {GetLosses override for AutoTrans}
+ // GetLosses override for AutoTrans
procedure GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Complex); OVERRIDE;
- {Getcurrents Override for AutoTrans}
+ // Getcurrents Override for AutoTrans
procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present values of terminal
function RotatePhases(iPhs: Integer): Integer;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
procedure SaveWrite(F: TFileStream); OVERRIDE;
procedure GetAutoWindingVoltages(iWind: Integer; VBuffer: pComplexArray);
procedure GetAllWindingCurrents(CurrBuffer: pComplexArray);
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
property PresentTap[i: Integer]: Double READ Get_PresentTap WRITE Set_PresentTap;
property Mintap[i: Integer]: Double READ Get_MinTap;
@@ -215,30 +233,12 @@ TAutoTransObj = class(TPDElement)
// CIM accessors
property NumTaps[i: Integer]: Integer READ Get_NumTaps;
- property NumberOfWindings: Integer READ NumWindings;
property WdgResistance[i: Integer]: Double READ Get_WdgResistance;
property WdgkVA[i: Integer]: Double READ Get_WdgkVA;
property WdgConnection[i: Integer]: Integer READ Get_WdgConnection;
- property WdgYPPM[i: Integer]: Double READ Get_WdgYPPM;
property XscVal[i: Integer]: Double READ Get_Xsc;
- property XhlVal: Double READ puXHX;
- property XhtVal: Double READ puXHT;
- property XltVal: Double READ puXXT;
- property NormalHkVA: Double READ NormMaxHkVA;
- property EmergHkVA: Double READ EmergMaxHkVA;
- property thTau: Double READ ThermalTimeConst;
- property thN: Double READ n_thermal;
- property thM: Double READ m_thermal;
- property thFLRise: Double READ FLRise;
- property thHSRise: Double READ HSRise;
- property loadLossPct: Double READ pctLoadLoss;
- property noLoadLossPct: Double READ pctNoLoadLoss;
- property imagPct: Double READ pctImag;
- property ppmFloatFac: Double READ ppm_FloatFactor;
- property baseVA: Double READ VAbase;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
implementation
uses
@@ -251,709 +251,486 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TAutoTransObj;
+ TProp = TAutoTransProp;
const
- NumPropsThisClass = 42;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
+ AutoTransConnectionEnum: TDSSEnum;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TAutoTrans.Create(dssContext: TDSSContext); // Creates superstructure for all AutoTrans objects
+constructor TAutoTrans.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'AutoTrans';
- DSSClassType := DSSClassType + AUTOTRANS_ELEMENT; // override PDElement (kept in both actually)
-
- ActiveElement := 0;
- DefineProperties;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ AutoTransConnectionEnum := TDSSEnum.Create('AutoTrans: Connection', True, 1, 2,
+ ['wye', 'delta', 'series', 'y', 'ln', 'll'],
+ [0, 1, 2, 0, 0, 1]);
+ end;
- {Make space for AutoTrans property list}
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE; {Allow property list abbreviations}
+ inherited Create(dssContext, AUTOTRANS_ELEMENT, 'AutoTrans');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TAutoTrans.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TAutoTrans.DefineProperties;
-begin
-
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-{ Define Property names }
-
- PropertyName[1] := 'phases';
- PropertyName[2] := 'windings';
-
- // Winding Definition
- PropertyName[3] := 'wdg';
- PropertyName[4] := 'bus';
- PropertyName[5] := 'conn';
- PropertyName[6] := 'kV'; // FOR 2-and 3- phase always kVLL ELSE actual winding KV
- PropertyName[7] := 'kVA';
- PropertyName[8] := 'tap';
- PropertyName[9] := '%R';
- PropertyName[10] := 'Rdcohms';
- PropertyName[11] := 'Core';
-
- // General Data
- PropertyName[12] := 'buses';
- PropertyName[13] := 'conns';
- PropertyName[14] := 'kVs';
- PropertyName[15] := 'kVAs';
- PropertyName[16] := 'taps';
- PropertyName[17] := 'XHX';
- PropertyName[18] := 'XHT';
- PropertyName[19] := 'XXT';
- PropertyName[20] := 'XSCarray'; // x12 13 14... 23 24.. 34 ..
- PropertyName[21] := 'thermal';
- PropertyName[22] := 'n';
- PropertyName[23] := 'm';
- PropertyName[24] := 'flrise';
- PropertyName[25] := 'hsrise';
- PropertyName[26] := '%loadloss';
- PropertyName[27] := '%noloadloss';
- PropertyName[28] := 'normhkVA';
- PropertyName[29] := 'emerghkVA';
- PropertyName[30] := 'sub'; // =y/n
- PropertyName[31] := 'MaxTap';
- PropertyName[32] := 'MinTap';
- PropertyName[33] := 'NumTaps';
- PropertyName[34] := 'subname';
- PropertyName[35] := '%imag';
- PropertyName[36] := 'ppm_antifloat';
- PropertyName[37] := '%Rs';
-
- PropertyName[38] := 'bank';
- PropertyName[39] := 'XfmrCode';
- PropertyName[40] := 'XRConst';
- PropertyName[41] := 'LeadLag';
- PropertyName[42] := 'WdgCurrents';
-
-
- // define Property help values
- PropertyHelp[1] := 'Number of phases this AutoTransformer. Default is 3.';
- PropertyHelp[2] := 'Number of windings, this AutoTransformer. (Also is the number of terminals) ' +
- 'Default is 2. This property triggers memory allocation for the AutoTrans and will cause other properties to revert to default values.';
- // Winding Definition
- PropertyHelp[3] := 'Set this = to the number of the winding you wish to define. Then set ' +
- 'the values for this winding. Winding 1 is always the Series winding. ' +
- 'Winding 2 is always Common winding (wye connected). ' +
- 'Repeat for each winding. Alternatively, use ' +
- 'the array collections (buses, kVAs, etc.) to define the windings. Note: ' +
- 'reactances are BETWEEN pairs of windings; they are not the property of a single winding.';
- PropertyHelp[4] := 'Bus connection spec for this winding.';
- PropertyHelp[5] := 'Connection of this winding {Series, wye*, Delta, LN, LL }. Default is "wye" with the neutral solidly grounded. ' + CRLF +
- 'For AutoTrans, Winding 1 is always Series and Winding 2 (the Common winding) is always Wye. ' + CRLF +
- 'If only 2 windings, no need to specify connections.';
- PropertyHelp[6] := 'For 2-or 3-phase, enter phase-phase kV rating. Otherwise, kV rating of the actual winding. ' +
- 'Specify H terminal kV rating for Series winding.';
- PropertyHelp[7] := 'Base kVA rating of the winding. Side effect: forces change of max normal and emerg kVA ratings.' +
- 'If 2-winding AutoTrans, forces other winding to same value. ' +
- 'When winding 1 is defined, all other windings are defaulted to the same rating ' +
- 'and the first two winding resistances are defaulted to the %loadloss value.';
- PropertyHelp[8] := 'Per unit tap that this winding is on.';
- PropertyHelp[9] := 'Percent ac resistance this winding. This value is for the power flow model.' +
- 'Is derived from the full load losses in the transformer test report.';
- PropertyHelp[10] := 'Winding dc resistance in OHMS. Specify this for GIC analysis. From transformer test report (divide by number of phases). ' +
- 'Defaults to 85% of %R property (the ac value that includes stray losses).';
- PropertyHelp[11] := '{Shell*|5-leg|3-Leg|1-phase} Core Type. Used for GIC analysis in auxiliary programs. Not used inside OpenDSS.';
-
- // General Data
- PropertyHelp[12] := 'Use this to specify all the bus connections at once using an array. Example:' + CRLF + CRLF +
- 'New AutoTrans.T1 buses=[Hbus, Xbus]';
- PropertyHelp[13] := 'Use this to specify all the Winding connections at once using an array. Example:' + CRLF + CRLF +
- 'New AutoTrans.T1 buses=[Hbus, Xbus] ' +
- '~ conns=(series, wye)';
- PropertyHelp[14] := 'Use this to specify the kV ratings of all windings at once using an array. Example:' + CRLF + CRLF +
- 'New AutoTrans.T1 buses=[Hbus, Xbus] ' + CRLF +
- '~ conns=(series, wye)' + CRLF +
- '~ kvs=(115, 12.47)' + CRLF + CRLF +
- 'See kV= property for voltage rules.';
- PropertyHelp[15] := 'Use this to specify the kVA ratings of all windings at once using an array.';
- PropertyHelp[16] := 'Use this to specify the p.u. tap of all windings at once using an array.';
- PropertyHelp[17] := 'Use this to specify the percent reactance, H-L (winding 1 to winding 2). Use ' +
- 'for 2- or 3-winding AutoTranss. On the kVA base of winding 1(H-X). See also X12.';
- PropertyHelp[18] := 'Use this to specify the percent reactance, H-T (winding 1 to winding 3). Use ' +
- 'for 3-winding AutoTranss only. On the kVA base of winding 1(H-X). See also X13.';
- PropertyHelp[19] := 'Use this to specify the percent reactance, L-T (winding 2 to winding 3). Use ' +
- 'for 3-winding AutoTranss only. On the kVA base of winding 1(H-X). See also X23.';
- PropertyHelp[20] := 'Use this to specify the percent reactance between all pairs of windings as an array. ' +
- 'All values are on the kVA base of winding 1. The order of the values is as follows:' + CRLF + CRLF +
- '(x12 13 14... 23 24.. 34 ..) ' + CRLF + CRLF +
- 'There will be n(n-1)/2 values, where n=number of windings.';
- PropertyHelp[21] := 'Thermal time constant of the AutoTrans in hours. Typically about 2.';
- PropertyHelp[22] := 'n Exponent for thermal properties in IEEE C57. Typically 0.8.';
- PropertyHelp[23] := 'm Exponent for thermal properties in IEEE C57. Typically 0.9 - 1.0';
- PropertyHelp[24] := 'Temperature rise, deg C, for full load. Default is 65.';
- PropertyHelp[25] := 'Hot spot temperature rise, deg C. Default is 15.';
- PropertyHelp[26] := 'Percent load loss at full load. The %R of the High and Low windings (1 and 2) are adjusted to agree at rated kVA loading.';
- PropertyHelp[27] := 'Percent no load losses at rated excitatation voltage. Default is 0. Converts to a resistance in parallel with the magnetizing impedance in each winding.';
- PropertyHelp[28] := 'Normal maximum kVA rating of H winding (winding 1+2). Usually 100% - 110% of' +
- 'maximum nameplate rating, depending on load shape. Defaults to 110% of kVA rating of Winding 1.';
- PropertyHelp[29] := 'Emergency (contingency) kVA rating of H winding (winding 1+2). Usually 140% - 150% of' +
- 'maximum nameplate rating, depending on load shape. Defaults to 150% of kVA rating of Winding 1.';
- PropertyHelp[30] := '={Yes|No} Designates whether this AutoTrans is to be considered a substation.' +
- 'Default is No.'; // =y/n
-
- PropertyHelp[31] := 'Max per unit tap for the active winding. Default is 1.10';
- PropertyHelp[32] := 'Min per unit tap for the active winding. Default is 0.90';
- PropertyHelp[33] := 'Total number of taps between min and max tap. Default is 32 (16 raise and 16 lower taps about the neutral position). The neutral position is not counted.';
- PropertyHelp[34] := 'Substation Name. Optional. Default is null. If specified, printed on plots';
- PropertyHelp[35] := 'Percent magnetizing current. Default=0.0. Magnetizing branch is in parallel with windings in each phase. Also, see "ppm_antifloat".';
- PropertyHelp[36] := 'Default=1 ppm. Parts per million of AutoTrans winding VA rating connected to ground to protect against accidentally floating a winding without a reference. ' +
- 'If positive then the effect is adding a very large reactance to ground. If negative, then a capacitor.';
- PropertyHelp[37] := 'Use this property to specify all the winding ac %resistances using an array. Example:' + CRLF + CRLF +
- 'New AutoTrans.T1 buses=[Hibus, lowbus] ' +
- '~ %Rs=(0.2 0.3)';
- PropertyHelp[38] := 'Name of the bank this transformer is part of, for CIM, MultiSpeak, and other interfaces.';
- PropertyHelp[39] := 'Name of a library entry for transformer properties. The named XfmrCode must already be defined.';
-
- PropertyHelp[40] := '={Yes|No} Default is NO. Signifies whether or not the X/R is assumed contant for harmonic studies.';
- PropertyHelp[41] := '{Lead | Lag (default) | ANSI (default) | Euro } Designation in mixed Delta-wye connections the ' +
- 'relationship between HV to LV winding. Default is ANSI 30 deg lag, e.g., Dy1 of Yd1 vector group. ' +
- 'To get typical European Dy11 connection, specify either "lead" or "Euro"';
- PropertyHelp[42] := '(Read only) Makes winding currents available via return on query (? AutoTrans.TX.WdgCurrents). ' +
- 'Order: Phase 1, Wdg 1, Wdg 2, ..., Phase 2 ...';
-
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TAutoTrans.NewObject(const ObjName: String): Integer;
+function XscSize(obj: TObj): Integer;
begin
- // create a new object of this class and add to list
- with ActiveCircuit do
- begin
-
- ActiveCktElement := TAutoTransObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject); // Return index of AutoTrans in AutoTrans list
-
- end;
-
+ with obj do
+ Result := (NumWindings - 1) * NumWindings div 2;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TAutoTrans.Edit: Integer;
-{
- A Transf Defaults to 3-phases, 2-windings (both wye)
-}
+function GetWindingCurrentsResult(Obj: TObj): String;
+// Returns string mag, angle
var
- ParamPointer,
- i: Integer;
- ParamName: String; {For parsing property names}
- Param: String;
-
+ WindingCurrents: pComplexArray;
+ i, j, k: Integer;
begin
- // continue parsing cmdline presently in Parser
-
- {Make this object the active circuit element}
- DSS.ActiveAutoTransObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveAutoTransObj; // use property to set this value
-
- Result := 0;
-
- with DSS.ActiveAutoTransObj do
+ with Obj do
begin
- XHXChanged := FALSE;
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "AutoTrans.' + Name + '"', 100110);
- 1:
- Nphases := Parser.IntValue;
- 2:
- SetNumWindings(Parser.IntValue); // Reallocate stuff if bigger
- 3:
- SetActiveWinding(Parser.IntValue);
- 4:
- SetbusAuto(ActiveWinding, param);
- 5:
- InterpretAutoConnection(Param);
- 6:
- Winding^[ActiveWinding].kVLL := parser.Dblvalue;
- 7:
- Winding^[ActiveWinding].kVA := parser.Dblvalue;
- 8:
- Winding^[ActiveWinding].puTap := parser.Dblvalue;
- 9:
- Winding^[ActiveWinding].Rpu := parser.Dblvalue * 0.01; // %R
- 10:
- Winding^[ActiveWinding].RdcOhms := Parser.DblValue;
- 11:
- strCoreType := Param;
- 12:
- InterpretAllBuses(Param);
- 13:
- InterpretAllConns(Param);
- 14:
- InterpretAllkVRatings(Param);
- 15:
- InterpretAllkVARatings(Param);
- 16:
- InterpretAllTaps(Param);
- 17:
- puXHX := TrapZero(parser.Dblvalue, 7.0) * 0.01;
- 18:
- puXHT := TrapZero(parser.Dblvalue, 35.0) * 0.01;
- 19:
- puXXT := TrapZero(parser.Dblvalue, 30.0) * 0.01;
- 20:
- Parser.ParseAsVector(((NumWindings - 1) * NumWindings div 2), puXSC);
- 21:
- ThermalTimeConst := Parser.DblValue;
- 22:
- n_thermal := Parser.DblValue;
- 23:
- m_thermal := Parser.DblValue;
- 24:
- FLrise := Parser.DblValue;
- 25:
- HSRise := Parser.DblValue;
- 26:
- pctLoadLoss := Parser.DblValue;
- 27:
- pctNoLoadLoss := Parser.DblValue;
- 28:
- NormMaxHkVA := Parser.Dblvalue;
- 29:
- EmergMaxHkVA := Parser.Dblvalue;
- 30:
- IsSubstation := InterpretYesNo(Param);
- 31:
- Winding^[ActiveWinding].MaxTap := Parser.DblValue;
- 32:
- Winding^[ActiveWinding].MinTap := Parser.DblValue;
- 33:
- Winding^[ActiveWinding].NumTaps := Parser.IntValue;
- 34:
- SubstationName := Param;
- 35:
- pctImag := Parser.DblValue;
- 36:
- ppm_FloatFactor := Parser.DblValue * 1.0e-6;
- 37:
- InterpretAllRs(Param);
- 38:
-{XfmrBank := Param};
- 39:
- FetchXfmrCode(Param); // Do nothing until we define auto code
- 40:
- XRConst := InterpretYesNo(Param);
- 41:
- HVLeadsLV := InterpretLeadLag(Param);
- 42:
- PropertyValue[45] := ''; // placeholder, do nothing just throw value away if someone tries to set it.
- else
- // Inherited properties
- ClassEdit(DSS.ActiveAutoTransObj, ParamPointer - NumPropsThisClass)
- end;
+ WindingCurrents := AllocMem(Sizeof(Complex) * 2 * FNPhases * NumWindings);
- {Take care of properties that require some additional work,}
- case ParamPointer of
- 1:
- NConds := 2 * Fnphases; // Force redefinition of number of conductors and reallocation of matrices
- // YPrim is built with windings not connected. Connected in NodeRef
- // default all winding kVAs to first winding so latter Donot have to be specified
- 7:
- if (ActiveWinding = 1) then
- begin
- for i := 2 to NumWindings do
- Winding^[i].kVA := Winding^[1].kVA;
- NormMaxHkVA := 1.1 * Winding^[1].kVA; // Defaults for new winding rating.
- EmergMaxHkVA := 1.5 * Winding^[1].kVA;
- end
- else
- if NumWindings = 2 then
- begin
- Winding^[1].kVA := Winding^[2].kVA; // For 2-winding, force both kVAs to be same
- end;
- // Update LoadLosskW if winding %r changed. Using only windings 1 and 2
- 9:
- pctLoadLoss := (Winding^[1].Rpu + Winding^[2].Rpu) * 100.0;
- 10:
- Winding^[ActiveWinding].RdcSpecified := TRUE;
- 11:
- CoreType := InterpretCoreType(Param); // Assign integer number
- 15:
- begin
- NormMaxHkVA := 1.1 * Winding^[1].kVA; // Defaults for new winding rating.
- EmergMaxHkVA := 1.5 * Winding^[1].kVA;
- end;
- 17..19:
- XHXChanged := TRUE;
- 20:
- for i := 1 to ((NumWindings - 1) * NumWindings div 2) do
- puXSC^[i] := puXSC^[i] * 0.01; // Convert to per unit
-
- 26:
- begin // Assume load loss is split evenly between windings 1 and 2
- Winding^[1].Rpu := pctLoadLoss / 2.0 / 100.0;
- Winding^[2].Rpu := Winding^[1].Rpu;
- end;
- 37:
- pctLoadLoss := (Winding^[1].Rpu + Winding^[2].Rpu) * 100.0; // Update
- 38:
- DoSimpleMsg('Bank Property not used with AutoTrans object.', 100130);
- 39:
- DoSimpleMsg('XFmrCode Property not used with AutoTrans object.', 100131);
- else
- end;
+ GetAllWindingCurrents(WindingCurrents);
- //YPrim invalidation on anything that changes impedance values
- case ParamPointer of
- 5..19:
- YprimInvalid := TRUE;
- 26..27:
- YprimInvalid := TRUE;
- 35..37:
- YprimInvalid := TRUE;
- else
+ Result := '';
+ k := 0;
+ for i := 1 to Fnphases do
+ begin
+ for j := 1 to NumWindings do
+ begin
+ k := k + 1;
+ Result := Result + Format('%.7g, (%.5g), ', [Cabs(WindingCurrents^[k]), Cdang(WindingCurrents^[k])]);
+ k := k + 1;
+ // Skip currents from other end of the winding
end;
-
- {Advance to next property on input line}
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
end;
- RecalcElementData;
+ Reallocmem(WindingCurrents, 0); // throw away temp array
end;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TAutoTrans.SetActiveWinding(w: Integer);
-
-begin
- with DSS.ActiveAutoTransObj do
- if (w > 0) and (w <= NumWindings) then
- ActiveWinding := w
- else
- DoSimpleMsg('Wdg parameter invalid for "' + DSS.ActiveAutoTransObj.Name + '"', 100112);
-end;
-
-function TAutoTrans.TrapZero(const Value: Double; DefaultValue: Double): Double;
+procedure TAutoTrans.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
- if Value = 0.0 then
- begin
- DoSimpleMsg('Zero Reactance specified for AutoTrans.' + DSS.ActiveAutoTransObj.Name, 1011201);
- Result := DefaultValue;
- end
- else
- Result := Value;
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TAutoTrans.InterpretAutoConnection(const S: String);
+ Numproperties := NumPropsThisClass;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ PropertyStructArrayOffset := ptruint(@obj.Winding);
+ PropertyStructArrayStep := SizeOf(TAutoWinding);
+ PropertyStructArrayIndexOffset := ptruint(@obj.ActiveWinding);
+ PropertyStructArrayCountOffset := ptruint(@obj.NumWindings);
+
+ // RO string
+ PropertyType[ord(TProp.WdgCurrents)] := TPropertyType.StringSilentROFunctionProperty;
+ PropertyOffset[ord(TProp.WdgCurrents)] := ptruint(@GetWindingCurrentsResult);
+
+ // double array
+ PropertyType[ord(TProp.Xscarray)] := TPropertyType.DoubleVArrayProperty;
+ PropertyOffset[ord(TProp.Xscarray)] := ptruint(@obj.puXSC);
+ PropertyOffset3[ord(TProp.Xscarray)] := ptruint(@XscSize);
+ PropertyFlags[ord(TProp.Xscarray)] := [TPropertyFlag.SizeIsFunction];
+ PropertyScale[ord(TProp.Xscarray)] := 0.01;
+
+ // enums
+ PropertyType[ord(TProp.Core)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.Core)] := ptruint(@obj.CoreType);
+ PropertyOffset2[ord(TProp.Core)] := PtrInt(DSS.CoreTypeEnum);
+
+ PropertyType[ord(TProp.LeadLag)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.LeadLag)] := ptruint(@obj.HVLeadsLV); // LongBool as Integer
+ PropertyOffset2[ord(TProp.LeadLag)] := PtrInt(DSS.LeadLagEnum);
+
+ // boolean properties
+ PropertyType[ord(TProp.sub)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.XRConst)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.sub)] := ptruint(@obj.IsSubstation);
+ PropertyOffset[ord(TProp.XRConst)] := ptruint(@obj.XRConst);
+
+ // double-on-struct array properties
+ PropertyType[ord(TProp.kV)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.kV)] := ptruint(@TAutoWinding(nil^).kVLL);
+
+ PropertyType[ord(TProp.kVA)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.kVA)] := ptruint(@TAutoWinding(nil^).kVA);
+
+ PropertyType[ord(TProp.tap)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.tap)] := ptruint(@TAutoWinding(nil^).puTap);
+
+ PropertyType[ord(TProp.MaxTap)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.MaxTap)] := ptruint(@TAutoWinding(nil^).MaxTap);
+
+ PropertyType[ord(TProp.MinTap)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.MinTap)] := ptruint(@TAutoWinding(nil^).MinTap);
+
+ PropertyType[ord(TProp.RdcOhms)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.RdcOhms)] := ptruint(@TAutoWinding(nil^).RdcOhms);
+
+ PropertyType[ord(TProp.pctR)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.pctR)] := ptruint(@TAutoWinding(nil^).Rpu);
+ PropertyScale[ord(TProp.pctR)] := 0.01;
+
+ // bus, indirect
+ PropertyType[ord(TProp.bus)] := TPropertyType.BusOnStructArrayProperty;
+ PropertyOffset[ord(TProp.bus)] := 1; // dummy value, just to mark the property as handled
+
+ PropertyType[ord(TProp.buses)] := TPropertyType.BusesOnStructArrayProperty;
+ PropertyOffset[ord(TProp.buses)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.buses)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.buses)] := ord(TProp.bus);
+
+ // enum on array of structs
+ PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumOnStructArrayProperty;
+ PropertyOffset[ord(TProp.conn)] := ptruint(@TAutoWinding(nil^).Connection);
+ PropertyOffset2[ord(TProp.conn)] := PtrInt(AutoTransConnectionEnum);
+
+ // array of enums on array of structs
+ PropertyType[ord(TProp.conns)] := TPropertyType.MappedStringEnumArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.conns)] := ptruint(@TAutoWinding(nil^).Connection);
+ PropertyOffset2[ord(TProp.conns)] := PtrInt(AutoTransConnectionEnum);
+ PropertyFlags[ord(TProp.conns)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.conns)] := ord(TProp.conn);
+
+ // integer on struct array
+ PropertyType[ord(TProp.NumTaps)] := TPropertyType.IntegerOnStructArrayProperty;
+ PropertyOffset[ord(TProp.NumTaps)] := ptruint(@TAutoWinding(nil^).NumTaps);
+
+ // integer properties
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ PropertyType[ord(TProp.windings)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.windings)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.windings)] := [TPropertyFlag.NonZero, TPropertyFlag.NonNegative];
+
+ PropertyType[ord(TProp.wdg)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.wdg)] := ptruint(@obj.ActiveWinding);
+ PropertyFlags[ord(TProp.wdg)] := [TPropertyFlag.IntegerStructIndex];
+
+ // string properties
+ PropertyType[ord(TProp.subname)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.subname)] := ptruint(@obj.SubstationName);
+
+ // double properties
+ PropertyOffset[ord(TProp.thermal)] := ptruint(@obj.ThermalTimeConst);
+ PropertyOffset[ord(TProp.n)] := ptruint(@obj.n_thermal);
+ PropertyOffset[ord(TProp.m)] := ptruint(@obj.m_thermal);
+ PropertyOffset[ord(TProp.flrise)] := ptruint(@obj.FLrise);
+ PropertyOffset[ord(TProp.hsrise)] := ptruint(@obj.HSRise);
+ PropertyFlags[ord(TProp.thermal)] := [TPropertyFlag.Unused];
+ PropertyFlags[ord(TProp.n)] := [TPropertyFlag.Unused];
+ PropertyFlags[ord(TProp.m)] := [TPropertyFlag.Unused];
+ PropertyFlags[ord(TProp.flrise)] := [TPropertyFlag.Unused];
+ PropertyFlags[ord(TProp.hsrise)] := [TPropertyFlag.Unused];
+
+ PropertyOffset[ord(TProp.pctloadloss)] := ptruint(@obj.pctLoadLoss);
+ PropertyOffset[ord(TProp.pctnoloadloss)] := ptruint(@obj.pctNoLoadLoss);
+ PropertyOffset[ord(TProp.normhkVA)] := ptruint(@obj.NormMaxHkVA);
+ PropertyOffset[ord(TProp.emerghkVA)] := ptruint(@obj.EmergMaxHkVA);
+ PropertyOffset[ord(TProp.pctimag)] := ptruint(@obj.pctImag);
+
+ // scaled double
+ PropertyOffset[ord(TProp.ppm_antifloat)] := ptruint(@obj.ppm_FloatFactor);
+ PropertyScale[ord(TProp.ppm_antifloat)] := 1.0e-6;
+
+ // adv double percent properties
+ PropertyOffset[ord(TProp.XHX)] := ptruint(@obj.puXHX);
+ PropertyOffset[ord(TProp.XHT)] := ptruint(@obj.puXHT);
+ PropertyOffset[ord(TProp.XXT)] := ptruint(@obj.puXXT);
+
+ PropertyScale[ord(TProp.XHX)] := 0.01;
+ PropertyScale[ord(TProp.XHT)] := 0.01;
+ PropertyScale[ord(TProp.XXT)] := 0.01;
+
+ PropertyTrapZero[ord(TProp.XHX)] := 7.0;
+ PropertyTrapZero[ord(TProp.XHT)] := 35.0;
+ PropertyTrapZero[ord(TProp.XXT)] := 30.0;
+
+ // double arrays via struct array
+ PropertyType[ord(TProp.pctRs)] := TPropertyType.DoubleArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.pctRs)] := ptruint(@TAutoWinding(nil^).Rpu);
+ PropertyOffset2[ord(TProp.pctRs)] := ptruint(@obj.NumWindings);
+ PropertyScale[ord(TProp.pctRs)] := 0.01;
+ PropertyFlags[ord(TProp.pctRs)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.pctRs)] := ord(TProp.pctR);
+
+ PropertyType[ord(TProp.kVs)] := TPropertyType.DoubleArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.kVs)] := ptruint(@TAutoWinding(nil^).kVLL);
+ PropertyOffset2[ord(TProp.kVs)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.kVs)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.kVs)] := ord(TProp.kV);
+
+ PropertyType[ord(TProp.kVAs)] := TPropertyType.DoubleArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.kVAs)] := ptruint(@TAutoWinding(nil^).kVA);
+ PropertyOffset2[ord(TProp.kVAs)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.kVAs)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.kVAs)] := ord(TProp.kVA);
+
+ PropertyType[ord(TProp.taps)] := TPropertyType.DoubleArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.taps)] := ptruint(@TAutoWinding(nil^).puTap);
+ PropertyOffset2[ord(TProp.taps)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.taps)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.taps)] := ord(TProp.tap);
-// Accepts
-// delta or LL (Case insensitive)
-// Y, wye, or LN
-begin
- with DSS.ActiveAutoTransObj do
- begin
- with Winding^[ActiveWinding] do
- begin
- case ActiveWinding of
- 1:
- Connection := TAutoTransConnection.Series; // First Winding always Series
- 2:
- Connection := TAutoTransConnection.Wye; // Second Winding is always Common and Wye
- else
- case lowercase(S)[1] of
- 'y', 'w':
- Connection := TAutoTransConnection.Wye;
- 'd':
- Connection := TAutoTransConnection.Delta;
- 'l':
- case lowercase(s)[2] of
- 'n':
- Connection := TAutoTransConnection.Wye;
- 'l':
- Connection := TAutoTransConnection.Delta;
- end;
- end;
- end;
- end;
- Yorder := fNConds * fNTerms;
- YPrimInvalid := TRUE;
- end;
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TAutoTrans.InterpretAllConns(const S: String);
-// routine expecting all winding connections expressed in one array of strings
+function TAutoTrans.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- {S1,}
- S2: String;
- i: Integer;
+ Obj: TObj;
begin
-
- AuxParser.CmdString := S; // Load up Parser
-
- {Loop for no more than the expected number of windings}
- with DSS.ActiveAutoTransObj do
- for i := 1 to Numwindings do
- begin
- ActiveWinding := i;
- {S1 :=} AuxParser.NextParam; // ignore any parameter name not expecting any
- S2 := AuxParser.StrValue;
- if Length(S2) > 0 then
- InterpretAutoConnection(S2);
- end;
-
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TAutoTrans.InterpretAllBuses(const S: String);
-// routine expecting all winding bus connections expressed in one array of strings
+procedure TAutoTransObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- BusNam: String;
i: Integer;
+ OldWdgSize: Integer;
+ NewWdgSize: Integer;
begin
-
- AuxParser.CmdString := S; // Load up Parser
-
- {Loop for no more than the expected number of windings; Ignore omitted values}
- with DSS.ActiveAutoTransObj do
- for i := 1 to Numwindings do
+ case Idx of
+ ord(TProp.phases):
+ if FNPhases <> previousIntVal then
+ NConds := 2 * Fnphases; // Force redefinition of number of conductors and reallocation of matrices
+ // YPrim is built with windings not connected. Connected in NodeRef
+ // default all winding kVAs to first winding so latter Donot have to be specified
+
+ ord(TProp.conn):
begin
- ActiveWinding := i;
- AuxParser.NextParam; // ignore any parameter name not expecting any
- BusNam := AuxParser.StrValue;
- if Length(BusNam) > 0 then
- SetBusAuto(ActiveWinding, BusNam);
+ // TODO: warn if the user tries to change the connecton for first two windings?
+ with Winding[ActiveWinding] do
+ case ActiveWinding of
+ 1:
+ Connection := TAutoTransConnection.Series; // First Winding always Series
+ 2:
+ Connection := TAutoTransConnection.Wye; // Second Winding is always Common and Wye
+ end;
+ Yorder := fNConds * fNTerms;
+ // YPrimInvalid := TRUE; -- already done below
end;
-
-end;
-
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TAutoTrans.InterpretLeadLag(const S: String): Boolean;
-// routine expecting all winding bus connections expressed in one array of strings
-begin
-
- Result := FALSE; // default to ANSI 30 Deg Lag if can't understand S
-
- if CompareTextShortest(S, 'lead') = 0 then
- Result := TRUE
- else
- if CompareTextShortest(S, 'euro') = 0 then
- Result := TRUE;
-
-end;
-
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TAutoTrans.InterpretAllkVRatings(const S: String);
-// routine expecting all winding kV ratings expressed in one array of strings
-var
- DataStr: String;
- i: Integer;
-begin
-
- AuxParser.CmdString := S; // Load up Parser
-
- {Loop for no more than the expected number of windings; Ignore omitted values}
- with DSS.ActiveAutoTransObj do
- for i := 1 to Numwindings do
+ ord(TProp.conns):
begin
- ActiveWinding := i;
- AuxParser.NextParam; // ignore any parameter name not expecting any
- DataStr := AuxParser.StrValue;
- if Length(DataStr) > 0 then
- Winding^[ActiveWinding].kVLL := AuxParser.Dblvalue;
+ // TODO: warn if the user tries to change the connecton for first two windings?
+ for i := 1 to NumWindings do
+ with Winding[i] do
+ case i of
+ 1:
+ Connection := TAutoTransConnection.Series; // First Winding always Series
+ 2:
+ Connection := TAutoTransConnection.Wye; // Second Winding is always Common and Wye
+ end;
+
+ Yorder := fNConds * fNTerms;
+ // YPrimInvalid := TRUE; -- already done below
end;
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TAutoTrans.InterpretAllkVARatings(const S: String);
-// routine expecting all winding ratings expressed in one array of strings
-var
- DataStr: String;
- i: Integer;
-begin
-
- AuxParser.CmdString := S; // Load up Parser
-
- {Loop for no more than the expected number of windings; Ignore omitted values}
- with DSS.ActiveAutoTransObj do
- for i := 1 to Numwindings do
+ ord(TProp.windings):
begin
- ActiveWinding := i;
- AuxParser.NextParam; // ignore any parameter name not expecting any
- DataStr := AuxParser.StrValue;
- if Length(DataStr) > 0 then
- Winding^[ActiveWinding].kVA := AuxParser.Dblvalue;
+ if NumWindings <= 0 then
+ begin
+ i := NumWindings;
+ NumWindings := previousIntVal;
+ DoSimpleMsg('Invalid number of windings: (%d) for "%s"', [i, FullName], 100111);
+ Exit;
+ end;
+ OldWdgSize := (previousIntVal - 1) * previousIntVal div 2;
+ MaxWindings := NumWindings;
+ NewWdgSize := (NumWindings - 1) * NumWindings div 2;
+ FNconds := 2 * Fnphases;
+ Nterms := NumWindings;
+ Reallocmem(Winding, Sizeof(TAutoWinding) * MaxWindings); // Reallocate collector array
+ for i := 1 to MaxWindings do
+ Winding[i].Init(i);
+
+ // array of short circuit measurements between pairs of windings
+ ReAllocmem(puXSC, SizeOF(puXSC^[1]) * NewWdgSize);
+ for i := OldWdgSize + 1 to NewWdgSize do
+ puXSC^[i] := 0.30;
+ Reallocmem(TermRef, SizeOf(TermRef^[1]) * 2 * NumWindings * Fnphases);
+
+ // Reallocate impedance matrices
+ ZB.Free;
+ Y_1Volt.Free;
+ Y_1Volt_NL.Free;
+ Y_Term.Free;
+ Y_Term_NL.Free;
+
+ ZB := TCMatrix.CreateMatrix(NumWindings - 1);
+ Y_1Volt := TCMatrix.CreateMatrix(NumWindings);
+ Y_1Volt_NL := TCMatrix.CreateMatrix(NumWindings);
+ Y_Term := TCMatrix.CreateMatrix(2 * NumWindings);
+ Y_Term_NL := TCMatrix.CreateMatrix(2 * NumWindings);
end;
+ ord(TProp.kVA):
+ if (ActiveWinding = 1) then
+ begin
+ for i := 2 to NumWindings do
+ Winding^[i].kVA := Winding^[1].kVA;
+ NormMaxHkVA := 1.1 * Winding^[1].kVA; // Defaults for new winding rating.
+ EmergMaxHkVA := 1.5 * Winding^[1].kVA;
+ end
+ else
+ if NumWindings = 2 then
+ begin
+ Winding^[1].kVA := Winding^[2].kVA; // For 2-winding, force both kVAs to be same
+ end;
+ // Update LoadLosskW if winding %r changed. Using only windings 1 and 2
+ ord(TProp.pctR):
+ pctLoadLoss := (Winding^[1].Rpu + Winding^[2].Rpu) * 100.0;
+ ord(TProp.Rdcohms):
+ Winding^[ActiveWinding].RdcSpecified := TRUE;
+ ord(TProp.kVAs):
+ begin
+ NormMaxHkVA := 1.1 * Winding^[1].kVA; // Defaults for new winding rating.
+ EmergMaxHkVA := 1.5 * Winding^[1].kVA;
+ end;
+ 17..19:
+ XHXChanged := TRUE;
+ 26:
+ begin // Assume load loss is split evenly between windings 1 and 2
+ Winding^[1].Rpu := pctLoadLoss / 2.0 / 100.0;
+ Winding^[2].Rpu := Winding^[1].Rpu;
+ end;
+ 37:
+ pctLoadLoss := (Winding^[1].Rpu + Winding^[2].Rpu) * 100.0; // Update
+ // 38:
+ // DoSimpleMsg('Bank Property not used with AutoTrans object.', 100130);
+ // 39:
+ // DoSimpleMsg('XFmrCode Property not used with AutoTrans object.', 100131);
+ end;
-
+ //YPrim invalidation on anything that changes impedance values
+ case Idx of
+ 5..19:
+ YprimInvalid := TRUE;
+ 26..27:
+ YprimInvalid := TRUE;
+ 35..37:
+ YprimInvalid := TRUE;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TAutoTrans.InterpretAllRs(const S: String);
-// routine expecting all winding ratings expressed in one array of strings
+procedure TAutoTransObj.SetBus(iwdg: Integer; const s: String);
+// Previously "SetBusAuto"
+// Added Jan 3, 2019
var
- DataStr: String;
- i: Integer;
+ NNodes: array[1..50] of Integer; // big integer buffer
+ NumNodes: Integer;
+ ii: Integer;
+ strBusName,
+ strNewBusName: String;
begin
+ // For winding 2 set all nodes on second end of winding to same as 1st value
+ // so all neutral ends of common winding get connected to same neutral node
+ case iwdg of
+ 2:
+ begin
+ for ii := 1 to nphases do
+ NNodes[ii] := ii; // set up buffer with defaults
+ // Default all other conductors to a ground connection
+ // If user wants them ungrounded, must be specified explicitly!
+ for ii := nphases + 1 to NConds do
+ NNodes[ii] := 0;
- AuxParser.CmdString := S; // Load up Parser
+ strBusName := DSS.AuxParser.ParseAsBusName(s, NumNodes, pIntegerArray(@NNodes));
- {Loop for no more than the expected number of windings; Ignore omitted values}
- with DSS.ActiveAutoTransObj do
- for i := 1 to Numwindings do
- begin
- ActiveWinding := i;
- AuxParser.NextParam; // ignore any parameter name not expecting any
- DataStr := AuxParser.StrValue;
- if Length(DataStr) > 0 then
- Winding^[ActiveWinding].Rpu := AuxParser.Dblvalue * 0.01;
+ // Check for non-zero neutral specification
+ if NNodes[nphases + 1] > 0 then
+ begin
+ // Reconstruct new bus name
+ strNewBusName := strBusName;
+ for ii := 1 to Nphases do
+ strNewBusName := strNewBusName + Format('.%d', [NNodes[ii]]);
+ for ii := nphases + 1 to Nconds do
+ strNewBusName := strNewBusName + Format('.%d', [NNodes[nphases + 1]]);
+ inherited SetBus(iwdg, strNewBusName);
+ end
+ else
+ inherited SetBus(iwdg, s);
end;
-
+ else
+ inherited Setbus(iwdg, s); // all other windings
+ end;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TAutoTrans.InterpretAllTaps(const S: String);
-// routine expecting all winding taps expressed in one array of strings
+function TAutoTrans.BeginEdit(ptr: Pointer; SetActive: Boolean): Pointer;
var
- DataStr: String;
- i: Integer;
+ Obj: TObj;
begin
-
- AuxParser.CmdString := S; // Load up Parser
-
- {Loop for no more than the expected number of windings; Ignore omitted values}
- with DSS.ActiveAutoTransObj do
- for i := 1 to Numwindings do
- begin
- ActiveWinding := i;
- AuxParser.NextParam; // ignore any parameter name, not expecting any
- DataStr := AuxParser.StrValue;
- if Length(DataStr) > 0 then
- Winding^[ActiveWinding].puTap := AuxParser.Dblvalue;
- end;
-
+ Obj := TObj(inherited BeginEdit(ptr, SetActive));
+ Obj.XHXChanged := FALSE;
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TAutoTrans.MakeLike(const AutoTransfName: String): Integer;
+procedure TAutoTransObj.MakeLike(OtherPtr: Pointer);
var
- OtherTransf: TAutoTransObj;
+ Other: TObj;
i: Integer;
-
begin
- Result := 0;
- {See if we can find this Transf name in the present collection}
- OtherTransf := Find(AutoTransfName);
- if OtherTransf <> NIL then
- with DSS.ActiveAutoTransObj do
- begin
- Nphases := OtherTransf.Fnphases;
- SetNumWindings(OtherTransf.NumWindings);
- NConds := 2 * Fnphases; // forces reallocation of terminals and conductors
-
- Yorder := fNConds * fNTerms;
- YPrimInvalid := TRUE;
-
- for i := 1 to NumWindings do
- with Winding^[i] do
- begin
- Connection := OtherTransf.Winding^[i].Connection;
- kVLL := OtherTransf.Winding^[i].kVLL;
- kVSeries := OtherTransf.kVSeries;
- VBase := OtherTransf.Winding^[i].VBase;
- kVA := OtherTransf.Winding^[i].kVA;
- puTAP := OtherTransf.Winding^[i].puTAP;
- Rpu := OtherTransf.Winding^[i].Rpu;
- RdcOhms := OtherTransf.Winding^[i].RdcOhms;
- RdcSpecified := OtherTransf.Winding^[i].RdcSpecified;
- // copy the taps
- TapIncrement := OtherTransf.Winding^[i].TapIncrement;
- MinTap := OtherTransf.Winding^[i].MinTap;
- MaxTap := OtherTransf.Winding^[i].MaxTap;
- NumTaps := OtherTransf.Winding^[i].NumTaps;
- end;
-
- SetTermRef;
-
- puXHX := OtherTransf.puXHX;
- puXHT := OtherTransf.puXHT;
- puXXT := OtherTransf.puXXT;
-
- for i := 1 to (NumWindings * (NumWindings - 1) div 2) do
- puXSC^[i] := OtherTransf.puXSC^[i];
-
- ZB.CopyFrom(OtherTransf.ZB);
- Y_1Volt.CopyFrom(OtherTransf.Y_1Volt);
- Y_Term.CopyFrom(OtherTransf.Y_Term);
- Y_1Volt_NL.CopyFrom(OtherTransf.Y_1Volt_NL);
- Y_Term_NL.CopyFrom(OtherTransf.Y_Term_NL);
-
- ThermalTimeConst := OtherTransf.ThermalTimeConst;
- n_thermal := OtherTransf.n_thermal;
- m_thermal := OtherTransf.m_thermal;
- FLrise := OtherTransf.FLrise;
- HSrise := OtherTransf.HSrise;
- pctLoadLoss := OtherTransf.pctLoadLoss;
- pctNoLoadLoss := OtherTransf.pctNoLoadLoss;
- NormMaxHkVA := OtherTransf.NormMaxHkVA;
- EmergMaxHkVA := OtherTransf.EmergMaxHkVA;
- XRConst := OtherTransf.XRConst;
-
- XfmrBank := OtherTransf.XfmrBank;
- XfmrCode := OtherTransf.XfmrCode;
-
- ClassMakeLike(OtherTransf);
-
- for i := 1 to ParentClass.NumProperties do
- // skip read only properties
- if i <> 45 then
- PropertyValue[i] := OtherTransf.PropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in AutoTrans MakeLike: "' + AutoTransfName + '" Not Found.', 100113);
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ FNphases := Other.Fnphases;
+ SetNumWindings(Other.NumWindings);
+ NConds := 2 * Fnphases; // forces reallocation of terminals and conductors
+ Yorder := fNConds * fNTerms;
+ YPrimInvalid := TRUE;
+ for i := 1 to NumWindings do
+ Winding^[i] := Other.Winding^[i];
+
+ SetTermRef;
+
+ puXHX := Other.puXHX;
+ puXHT := Other.puXHT;
+ puXXT := Other.puXXT;
+
+ for i := 1 to (NumWindings * (NumWindings - 1) div 2) do
+ puXSC^[i] := Other.puXSC^[i];
+
+ ZB.CopyFrom(Other.ZB);
+ Y_1Volt.CopyFrom(Other.Y_1Volt);
+ Y_Term.CopyFrom(Other.Y_Term);
+ Y_1Volt_NL.CopyFrom(Other.Y_1Volt_NL);
+ Y_Term_NL.CopyFrom(Other.Y_Term_NL);
+
+ ThermalTimeConst := Other.ThermalTimeConst;
+ n_thermal := Other.n_thermal;
+ m_thermal := Other.m_thermal;
+ FLrise := Other.FLrise;
+ HSrise := Other.HSrise;
+ pctLoadLoss := Other.pctLoadLoss;
+ pctNoLoadLoss := Other.pctNoLoadLoss;
+ NormMaxHkVA := Other.NormMaxHkVA;
+ EmergMaxHkVA := Other.EmergMaxHkVA;
+ XRConst := Other.XRConst;
+
+ XfmrBank := Other.XfmrBank;
+ XfmrCode := Other.XfmrCode;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TAutoTrans Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TAutoTransObj.Create(ParClass: TDSSClass; const TransfName: String);
var
i: Integer;
begin
inherited Create(ParClass);
- Name := LowerCase(TransfName);
+ Name := AnsiLowerCase(TransfName);
DSSObjType := ParClass.DSSClassType; //DSSObjType + XFMR; // override PDElement (kept in both actually)
- Nphases := 3; // Directly set conds and phases
+ FNphases := 3; // Directly set conds and phases
fNConds := 2 * Fnphases; // 2 conductors per phase; let NodeRef connect neutral, etc.
SetNumWindings(2); // must do this after setting number of phases
ActiveWinding := 1;
@@ -980,15 +757,14 @@ constructor TAutoTransObj.Create(ParClass: TDSSClass; const TransfName: String);
EmergMaxHkVA := 1.5 * Winding^[1].kVA;
pctLoadLoss := 2.0 * Winding^[1].Rpu * 100.0; // assume two windings for init'ing
ppm_FloatFactor := 0.000001;
- {Compute antifloat added for each winding }
+ // ompute antifloat added for each winding
for i := 1 to NumWindings do
Winding^[i].ComputeAntiFloatAdder(ppm_FloatFactor, VABase / FNPhases);
- {Default the no load properties to zero}
+ // Default the no load properties to zero
pctNoLoadLoss := 0.0;
pctImag := 0.0;
- {Basefrequency := 60.0; set in base class to circuit fundamental freq; Do not reset here}
FaultRate := 0.007;
IsSubstation := FALSE;
XRConst := FALSE;
@@ -998,58 +774,9 @@ constructor TAutoTransObj.Create(ParClass: TDSSClass; const TransfName: String);
Y_Terminal_FreqMult := 0.0;
Yorder := fNTerms * fNconds;
- InitPropertyValues(0);
RecalcElementData;
end;
-procedure TAutoTransObj.SetBusAuto(iwdg: Integer; const s: String);
-// Added Jan 3, 2019
-var
- NNodes: array[1..50] of Integer; // big integer buffer
- NumNodes: Integer;
- ii: Integer;
- strBusName,
- strNewBusName: String;
-begin
-// For winding 2 set all nodes on second end of winding to same as 1st value
-// so all neutral ends of common winding get connected to same neutral node
-
- case iwdg of
-
- 2:
- begin
-
- for ii := 1 to nphases do
- NNodes[ii] := ii; // set up buffer with defaults
- // Default all other conductors to a ground connection
- // If user wants them ungrounded, must be specified explicitly!
- for ii := nphases + 1 to NConds do
- NNodes[ii] := 0;
-
- Auxparser.Token := s; // load up AuxParser
- strBusName := AuxParser.ParseAsBusName(NumNodes, pIntegerArray(@NNodes));
-
- // Check for non-zero neutral specification
- if NNodes[nphases + 1] > 0 then
- begin
- // Reconstruct new bus name
- strNewBusName := strBusName;
- for ii := 1 to Nphases do
- strNewBusName := strNewBusName + Format('.%d', [NNodes[ii]]);
- for ii := nphases + 1 to Nconds do
- strNewBusName := strNewBusName + Format('.%d', [NNodes[nphases + 1]]);
- SetBus(iwdg, strNewBusName);
- end
- else
- SetBus(iwdg, s);
-
- end;
- else
- Setbus(iwdg, s); // all other windings
- end;
-
-end;
-
procedure TAutoTransObj.SetNodeRef(iTerm: Integer; NodeRefArray: pIntegerArray);
// Overrides standard function
var
@@ -1057,9 +784,9 @@ procedure TAutoTransObj.SetNodeRef(iTerm: Integer; NodeRefArray: pIntegerArray);
begin
inherited SetNodeRef(iTerm, NodeRefArray);
- // Now fixup noderefs for series winding of AutoTrans
- // Warning **** Magic happens here
- // Redefine 2nd node of Series winding to same as first node of 2nd winding (common winding)
+ // Now fixup noderefs for series winding of AutoTrans
+ // Warning **** Magic happens here
+ // Redefine 2nd node of Series winding to same as first node of 2nd winding (common winding)
if iTerm = 2 then
if Winding^[1].Connection = TAutoTransConnection.Series then
@@ -1074,57 +801,15 @@ procedure TAutoTransObj.SetNodeRef(iTerm: Integer; NodeRefArray: pIntegerArray);
procedure TAutoTransObj.SetNumWindings(N: Integer);
var
- i: Integer;
- OldWdgSize: Integer;
- NewWdgSize: Integer;
+ prev: Integer;
begin
-
- if N > 1 then
- begin
- for i := 1 to NumWindings do
- Winding^[i].Free; // Free old winding objects
- OldWdgSize := (NumWindings - 1) * NumWindings div 2;
- NumWindings := N;
- MaxWindings := N;
- NewWdgSize := (NumWindings - 1) * NumWindings div 2;
- FNconds := 2 * Fnphases;
- Nterms := NumWindings;
- Reallocmem(Winding, Sizeof(Winding^[1]) * MaxWindings); // Reallocate collector array
- for i := 1 to MaxWindings do
- Winding^[i] := TAutoWinding.Create(i);
-
- // array of short circuit measurements between pairs of windings
- ReAllocmem(puXSC, SizeOF(puXSC^[1]) * NewWdgSize);
- for i := OldWdgSize + 1 to NewWdgSize do
- puXSC^[i] := 0.30;
- Reallocmem(TermRef, SizeOf(TermRef^[1]) * 2 * NumWindings * Fnphases);
-
- {Reallocate impedance matrices}
- ZB.Free;
- Y_1Volt.Free;
- Y_1Volt_NL.Free;
- Y_Term.Free;
- Y_Term_NL.Free;
-
- ZB := TCMatrix.CreateMatrix(NumWindings - 1);
- Y_1Volt := TCMatrix.CreateMatrix(NumWindings);
- Y_1Volt_NL := TCMatrix.CreateMatrix(NumWindings);
- Y_Term := TCMatrix.CreateMatrix(2 * NumWindings);
- Y_Term_NL := TCMatrix.CreateMatrix(2 * NumWindings);
- end
- else
- Dosimplemsg('Invalid number of windings: (' + IntToStr(N) + ') for AutoTrans.' + Name, 100111);
+ prev := NumWindings;
+ NumWindings := N;
+ PropertySideEffects(ord(TProp.windings), prev);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TAutoTransObj.Destroy;
-
-var
- i: Integer;
begin
- {Throw away stuff allocated for this object}
- for i := 1 to NumWindings do
- Winding^[i].Free;
Reallocmem(Winding, 0);
Reallocmem(puXSC, 0);
Reallocmem(TermRef, 0);
@@ -1136,18 +821,15 @@ destructor TAutoTransObj.Destroy;
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TAutoTransObj.RecalcElementData;
var
i,
ihvolt: Integer;
VFactor: Double;
-
begin
-
- // Determine Delta Direction
- // If high voltage is delta, delta leads y
- // If high voltage is wye, delta lags wye
+ // Determine Delta Direction
+ // If high voltage is delta, delta leads y
+ // If high voltage is wye, delta lags wye
if Winding^[1].connection = Winding^[2].connection then
DeltaDirection := 1
else
@@ -1188,7 +870,7 @@ procedure TAutoTransObj.RecalcElementData;
if XHXChanged then
begin
- {should only happen for 2- and 3-winding AutoTranss}
+ // should only happen for 2- and 3-winding AutoTrans
if NumWindings <= 3 then
for i := 1 to (NumWindings * (NumWindings - 1) div 2) do
case i of
@@ -1274,35 +956,32 @@ procedure TAutoTransObj.RecalcElementData;
CalcY_Terminal(1.0); // Calc Y_Terminal at base frequency
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TAutoTransObj.SaveWrite(F: TFileStream);
-{Override standard SaveWrite}
-{AutoTrans structure not conducive to standard means of saving}
+// Override standard SaveWrite
+// AutoTrans structure not conducive to standard means of saving
var
iprop: Integer;
i: Integer;
begin
- {Write only properties that were explicitly set in the
- final order they were actually set}
- iProp := GetNextPropertySet(0); // Works on ActiveDSSObject
+ // Write only properties that were explicitly set in the
+ // final order they were actually set
+ iProp := GetNextPropertySet(-9999999);
while iProp > 0 do
begin
- with ParentClass do
- {Trap wdg= and write out array properties instead}
- case RevPropertyIdxMap[iProp] of
+ // Trap wdg= and write out array properties instead
+ case iProp of
3:
begin // if WDG= was ever used write out arrays ...
for i := 12 to 16 do
- FSWrite(F, Format(' %s=%s', [PropertyName^[i], GetPropertyValue(i)]));
+ FSWrite(F, Format(' %s=%s', [ParentClass.PropertyName^[i], GetPropertyValue(i)]));
for i := 1 to Numwindings do
FSWrite(F, Format(' wdg=%d %sR=%.7g', [i, '%', Winding^[i].Rpu * 100.0]));
end;
4..9:
-{do Nothing}; // Ignore these properties; use arrays instead
-
+ ; // Ignore these properties; use arrays instead
else
if Length(PropertyValue[iProp]) > 0 then
- FSWrite(F, Format(' %s=%s', [PropertyName^[RevPropertyIdxMap[iProp]], CheckForBlanks(PropertyValue[iProp])]));
+ FSWrite(F, Format(' %s=%s', [ParentClass.PropertyName[iProp], CheckForBlanks(PropertyValue[iProp])]));
end;
iProp := GetNextPropertySet(iProp);
end;
@@ -1329,24 +1008,20 @@ procedure TAutoTransObj.SetTermRef;
TermRef^[k] := j * fNconds;
end;
else
-
-
-(*** Typical array for 3-phase auto
-This builds the YPrim and the NodeRef array maps it into Y
- TermRef^[1] := 1;
- TermRef^[2] := 4;
- TermRef^[3] := 7;
- TermRef^[4] := 10;
- TermRef^[5] := 2;
- TermRef^[6] := 5;
- TermRef^[7] := 8;
- TermRef^[8] := 11;
- TermRef^[9] := 3;
- TermRef^[10] := 6;
- TermRef^[11] := 9;
- TermRef^[12] := 12;
-
-********)
+ // Typical array for 3-phase auto
+ // This builds the YPrim and the NodeRef array maps it into Y
+ // TermRef^[1] := 1;
+ // TermRef^[2] := 4;
+ // TermRef^[3] := 7;
+ // TermRef^[4] := 10;
+ // TermRef^[5] := 2;
+ // TermRef^[6] := 5;
+ // TermRef^[7] := 8;
+ // TermRef^[8] := 11;
+ // TermRef^[9] := 3;
+ // TermRef^[10] := 6;
+ // TermRef^[11] := 9;
+ // TermRef^[12] := 12;
for i := 1 to Fnphases do
begin
@@ -1360,7 +1035,7 @@ procedure TAutoTransObj.SetTermRef;
Inc(k);
TermRef^[k] := TermRef^[k - 1] + Fnphases;
end;
-{**** WILL THIS WORK for 2-PHASE OPEN DELTA ???? Need to check this sometime}
+ // **** WILL THIS WORK for 2-PHASE OPEN DELTA ???? Need to check this sometime
TAutoTransConnection.Delta:
begin // Delta
@@ -1375,22 +1050,17 @@ procedure TAutoTransObj.SetTermRef;
Inc(k);
TermRef^[k] := i + Fnphases;
end;
- end; {CASE connection}
+ end;
end;
end;
- end; {CASE Fnphases}
+ end; // CASE Fnphases
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TAutoTransObj.CalcYPrim;
-
var
FreqMultiplier: Double;
-
begin
-
if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Shunt = NIL) OR (Yprim_Series = NIL) {YPrimInvalid} then
begin
// Reallocate YPrim if something has invalidated old allocation
@@ -1406,7 +1076,7 @@ procedure TAutoTransObj.CalcYPrim;
YPrim := TcMatrix.CreateMatrix(Yorder);
end
else
- begin {Same size as last time; just zero out to start over}
+ begin // Same size as last time; just zero out to start over
YPrim_Series.Clear; // zero out YPrim
YPrim_Shunt.Clear; // zero out YPrim
Yprim.Clear;
@@ -1423,18 +1093,18 @@ procedure TAutoTransObj.CalcYPrim;
BuildYPrimComponent(YPrim_Shunt, Y_Term_NL);
- {Combine the two Yprim components into Yprim}
+ // Combine the two Yprim components into Yprim
YPrim.CopyFrom(YPrim_Series);
Yprim.AddFrom(Yprim_Shunt);
- {Now Account for Open Conductors}
- {For any conductor that is open, zero out row and column}
+ // Now Account for Open Conductors
+ // For any conductor that is open, zero out row and column
inherited CalcYPrim;
YprimInvalid := FALSE;
end;
-procedure TAutoTransObj.DumpProperties(F: TFileStream; Complete: Boolean);
+procedure TAutoTransObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i, j: Integer;
@@ -1443,7 +1113,7 @@ procedure TAutoTransObj.DumpProperties(F: TFileStream; Complete: Boolean);
begin
inherited DumpProperties(F, Complete);
- {Basic Property Dump}
+ // Basic Property Dump
FSWriteln(F, Format('~ NumWindings=%d', [NumWindings]));
FSWriteln(F, Format('~ phases=%d', [Fnphases]));
@@ -1476,9 +1146,9 @@ procedure TAutoTransObj.DumpProperties(F: TFileStream; Complete: Boolean);
FSWriteln(F, Format('~ XHL=%.3f', [puXHX * 100.0]));
FSWriteln(F, Format('~ XHT=%.3f', [puXHT * 100.0]));
FSWriteln(F, Format('~ XLT=%.3f', [puXXT * 100.0]));
- FSWriteln(F, Format('~ X12=%.3f', [puXHX * 100.0]));
- FSWriteln(F, Format('~ X13=%.3f', [puXHT * 100.0]));
- FSWriteln(F, Format('~ X23=%.3f', [puXXT * 100.0]));
+ // FSWriteln(F, Format('~ X12=%.3f', [puXHX * 100.0]));
+ // FSWriteln(F, Format('~ X13=%.3f', [puXHT * 100.0]));
+ // FSWriteln(F, Format('~ X23=%.3f', [puXXT * 100.0]));
FSWrite(F, '~ Xscmatrix= "');
for i := 1 to (NumWindings - 1) * NumWindings div 2 do
FSWrite(F, Format('%.2f ', [puXSC^[i] * 100.0]));
@@ -1524,7 +1194,7 @@ procedure TAutoTransObj.DumpProperties(F: TFileStream; Complete: Boolean);
FSWrite(F, format('%g ', [GetElement(i, j).im]));
FSWriteln(F);
end;
- end; {WITH}
+ end;
ZBTemp.Free;
@@ -1544,7 +1214,7 @@ procedure TAutoTransObj.DumpProperties(F: TFileStream; Complete: Boolean);
FSWrite(F, Format('%.4f ', [GetElement(i, j).im]));
FSWriteln(F);
end;
- end; {WITH}
+ end;
FSWriteln(F);
FSWriteln(F, 'Y_OneVolt');
@@ -1555,6 +1225,7 @@ procedure TAutoTransObj.DumpProperties(F: TFileStream; Complete: Boolean);
for j := 1 to i do
FSWrite(F, Format('%.4f ', [GetElement(i, j).re]));
FSWriteln(F);
+
end;
for i := 1 to NumWindings do
begin
@@ -1592,17 +1263,13 @@ procedure TAutoTransObj.DumpProperties(F: TFileStream; Complete: Boolean);
procedure TAutoWinding.ComputeAntiFloatAdder(PPM_Factor, VABase1ph: Double);
begin
- Y_PPM := -PPM_Factor / (SQR(VBase) / VABase1ph) / 2.0; //12-11-12 divided by two
- // put half on each terminal of the winding.
+ Y_PPM := -PPM_Factor / (SQR(VBase) / VABase1ph) / 2.0;
+ // put half on each terminal of the winding.
end;
-constructor TAutoWinding.Create(iWinding: Integer);
-{
- Make a new winding
-}
+procedure TAutoWinding.Init(iWinding: Integer);
+// Initialize a new winding
begin
- inherited Create;
-
case iWinding of
1:
begin
@@ -1614,7 +1281,6 @@ constructor TAutoWinding.Create(iWinding: Integer);
kVLL := 12.47;
end;
-
VBase := kVLL / SQRT3 * 1000.0;
kVA := 1000.0;
puTap := 1.0;
@@ -1628,12 +1294,6 @@ constructor TAutoWinding.Create(iWinding: Integer);
NumTaps := 32;
MaxTap := 1.10;
MinTap := 0.90;
-
-end;
-
-destructor TAutoWinding.Destroy;
-begin
- inherited Destroy;
end;
function TAutoTransObj.Get_PresentTap(i: Integer): Double;
@@ -1645,10 +1305,8 @@ function TAutoTransObj.Get_PresentTap(i: Integer): Double;
end;
procedure TAutoTransObj.Set_PresentTap(i: Integer; const Value: Double);
-
var
TempVal: Double;
-
begin
if (i > 0) and (i <= NumWindings) then
with Winding^[i] do
@@ -1686,15 +1344,6 @@ function TAutoTransObj.Get_WdgkVA(i: Integer): Double;
Result := 0.0;
end;
-
-function TAutoTransObj.Get_WdgYPPM(i: Integer): Double;
-begin
- if (i > 0) and (i <= NumWindings) then
- Result := Winding^[i].Y_PPM
- else
- Result := 0.0;
-end;
-
function TAutoTransObj.Get_Xsc(i: Integer): Double;
var
imax: Integer;
@@ -1706,7 +1355,6 @@ function TAutoTransObj.Get_Xsc(i: Integer): Double;
Result := 0.0;
end;
-
function TAutoTransObj.Get_WdgConnection(i: Integer): Integer;
begin
if (i > 0) and (i <= NumWindings) then
@@ -1748,18 +1396,14 @@ function TAutoTransObj.Get_TapIncrement(i: Integer): Double;
end;
procedure TAutoTransObj.GetAllWindingCurrents(CurrBuffer: pComplexArray);
+// Return a vector of complex currents for each Winding of all phases
-{
- Return a vector of complex currents for each Winding of all phases
+// Iterm = Yterm * Vterm
- Iterm = Yterm * Vterm
-
- Yterm order is 2*NumWindings. Each phase has same Yterm.
- Vterm order is 2*NumWindings .
-
- Calculate Iterm phase-by-phase and concatenate into CurrBuffer.
-}
+// Yterm order is 2*NumWindings. Each phase has same Yterm.
+// Vterm order is 2*NumWindings .
+// Calculate Iterm phase-by-phase and concatenate into CurrBuffer.
var
jphase, k, iPhase, iWind, i: Integer;
VTerm: pComplexArray;
@@ -1767,7 +1411,6 @@ procedure TAutoTransObj.GetAllWindingCurrents(CurrBuffer: pComplexArray);
ITerm_NL: pComplexArray;
begin
-
try
Vterm := Allocmem(SizeOf(Complex) * 2 * NumWindings);
Iterm := Allocmem(SizeOf(Complex) * 2 * NumWindings);
@@ -1815,7 +1458,7 @@ procedure TAutoTransObj.GetAllWindingCurrents(CurrBuffer: pComplexArray);
for i := 1 to 2 * NumWindings do
begin
k := k + 1;
- CurrBuffer^[k] := Cadd(ITerm^[i], ITerm_NL^[i]);
+ CurrBuffer^[k] := ITerm^[i] + ITerm_NL^[i];
end;
end;
@@ -1823,61 +1466,23 @@ procedure TAutoTransObj.GetAllWindingCurrents(CurrBuffer: pComplexArray);
ReallocMem(Iterm, 0);
ReallocMem(Iterm_NL, 0);
-
except
On E: Exception do
DoSimpleMsg('Error filling voltage buffer in GetAllWindingCurrents for Circuit Element:AutoTrans.' + Name + CRLF +
'Probable Cause: Invalid definition of element.' + CRLF +
'System Error Message: ' + E.Message, 100115);
end;
-
-end;
-
-
-function TAutoTransObj.GetAutoWindingCurrentsResult: String;
-
-// Returns string mag, angle
-var
- WindingCurrents: pComplexArray;
- i, j, k: Integer;
-
-begin
-
- WindingCurrents := AllocMem(Sizeof(Complex) * 2 * FNPhases * NumWindings);
-
- GetAllWindingCurrents(WindingCurrents);
-
- Result := '';
- k := 0;
- for i := 1 to Fnphases do
- begin
- for j := 1 to NumWindings do
- begin
- k := k + 1;
- Result := Result + Format('%.7g, (%.5g), ', [Cabs(WindingCurrents^[k]), Cdang(WindingCurrents^[k])]);
- k := k + 1;
- // Skip currents from other end of the winding
- end;
- end;
-
- Reallocmem(WindingCurrents, 0); // throw away temp array
-
end;
procedure TAutoTransObj.GeTAutoWindingVoltages(iWind: Integer; VBuffer: pComplexArray);
-
// Voltages across indicated winding
// Fill Vbuffer array which must be adequately allocated by calling routine
// Order is Number of Phases
-
var
i, ii, k, NeutTerm: Integer;
-
begin
-
try
-
- {return Zero if winding number improperly specified}
+ // return Zero if winding number improperly specified
if (iWind < 1) or (iWind > NumWindings) then
begin
for i := 1 to FNconds do
@@ -1885,7 +1490,7 @@ procedure TAutoTransObj.GeTAutoWindingVoltages(iWind: Integer; VBuffer: pComplex
Exit;
end;
- {Load up VTerminal - already allocated for all cktelements}
+ // Load up VTerminal - already allocated for all cktelements
with ActiveCircuit.Solution do
for i := 1 to Yorder do
Vterminal^[i] := NodeV^[NodeRef^[i]];
@@ -1897,18 +1502,18 @@ procedure TAutoTransObj.GeTAutoWindingVoltages(iWind: Integer; VBuffer: pComplex
case Winding^[iWind].Connection of
TAutoTransConnection.Wye:
begin // Wye
- VBuffer^[i] := Csub(Vterminal^[i + k], Vterminal^[NeutTerm]);
+ VBuffer^[i] := Vterminal^[i + k] - Vterminal^[NeutTerm];
end;
TAutoTransConnection.Delta:
begin // Delta
ii := RotatePhases(i); // Get next phase in sequence
- VBuffer^[i] := CSub(Vterminal^[i + k], Vterminal^[ii + k]);
+ VBuffer^[i] := Vterminal^[i + k] - Vterminal^[ii + k];
end;
TAutoTransConnection.Series:
begin // Series (winding 1)
- VBuffer^[i] := Csub(Vterminal^[i + k], Vterminal^[i + Fnconds]);
+ VBuffer^[i] := Vterminal^[i + k] - Vterminal^[i + Fnconds];
end;
- end; {CASE}
+ end;
except
On E: Exception do
@@ -1918,7 +1523,6 @@ procedure TAutoTransObj.GeTAutoWindingVoltages(iWind: Integer; VBuffer: pComplex
end;
end;
-
function TAutoTransObj.Get_BaseVoltage(i: Integer): Double;
begin
if (i < 1) or (i > NumWindings) then
@@ -1927,24 +1531,15 @@ function TAutoTransObj.Get_BaseVoltage(i: Integer): Double;
Result := Winding^[i].VBase;
end;
-{============================== GetLosses Override ===============================}
-
procedure TAutoTransObj.GetCurrents(Curr: pComplexArray);
var
i: Integer;
-
begin
inherited GetCurrents(Curr);
- // Combine Series (wdg 1) and Common winding (2) Currents to get X Terminal Currents
-
+ // Combine Series (wdg 1) and Common winding (2) Currents to get X Terminal Currents
for i := 1 to Fnphases do
- begin
-
- Caccum(Curr^[i + FnConds], Curr^[i + Fnphases]);
- end;
-
-
+ Curr^[i + FnConds] += Curr^[i + Fnphases];
end;
procedure TAutoTransObj.GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Complex);
@@ -1952,220 +1547,21 @@ procedure TAutoTransObj.GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Com
cTempIterminal: pComplexArray;
i: Integer;
begin
- {inherited;}
-
- {Calculates losses in watts, vars}
+ // Calculates losses in watts, vars
TotalLosses := Losses; // Side effect: computes Iterminal
- {Compute No load losses in Yprim_Shunt}
+ // Compute No load losses in Yprim_Shunt
cTempIterminal := AllocMem(Sizeof(Complex) * Yorder);
ComputeVterminal;
Yprim_Shunt.MVmult(cTempIterminal, Vterminal);
- {No Load Losses are sum of all powers coming into YPrim_Shunt from each terminal}
+ // No Load Losses are sum of all powers coming into YPrim_Shunt from each terminal
NoLoadLosses := CZERO;
for i := 1 to Yorder do
- Caccum(NoLoadLosses, Cmul(VTerminal^[i], conjg(cTempIterminal^[i])));
+ NoLoadLosses += VTerminal^[i] * cong(cTempIterminal^[i]);
- LoadLosses := CSub(TotalLosses, NoLoadLosses);
+ LoadLosses := TotalLosses - NoLoadLosses;
Reallocmem(cTempIterminal, 0);
-
-end;
-
-function TAutoTransObj.GetPropertyValue(Index: Integer): String;
-
-{ gets the property for the active winding ; Set the active winding before calling}
-
-var
- i: Integer;
-
-begin
- case Index of
- 12..16, 20, 37:
- Result := '[';
- else
- Result := '';
- end;
-
- case Index of
- 1:
- Result := IntToStr(nPhases);
- 2:
- Result := IntToStr(NumWindings);
- 3:
- Result := IntToStr(ActiveWinding); // return active winding
- 4:
- Result := Getbus(ActiveWinding); // return bus spec for active winding
- 5:
- case Winding^[ActiveWinding].Connection of
- TAutoTransConnection.Wye:
- Result := 'wye ';
- TAutoTransConnection.Delta:
- Result := 'Delta ';
- TAutoTransConnection.Series:
- Result := 'Series';
- else
- end;
- 6:
- Result := Format('%.7g', [Winding^[ActiveWinding].kVLL]);
- 7:
- Result := Format('%.7g', [Winding^[ActiveWinding].kVA]);
- 8:
- Result := Format('%.7g', [Winding^[ActiveWinding].puTap]);
- 9:
- Result := Format('%.7g', [Winding^[ActiveWinding].Rpu * 100.0]); // %R
- 10:
- Result := Format('%.7g', [Winding^[ActiveWinding].Rdcohms]);
- 11:
- case CoreType of
- 0:
- Result := 'shell';
- 1:
- Result := '1-phase';
- 3:
- Result := '3-leg';
- 5:
- Result := '5-Leg';
- end;
- 12:
- for i := 1 to NumWindings do
- Result := Result + GetBus(i) + ', ';
- 13:
- for i := 1 to NumWindings do
- case Winding^[i].Connection of
- TAutoTransConnection.Wye:
- Result := Result + 'wye, ';
- TAutoTransConnection.Delta:
- Result := Result + 'delta, ';
- TAutoTransConnection.Series:
- Result := Result + 'Series, ';
- else
- end;
- 14:
- for i := 1 to NumWindings do
- Result := Result + Format('%.7g, ', [Winding^[i].kVLL]);
- 15:
- for i := 1 to NumWindings do
- Result := Result + Format('%.7g, ', [Winding^[i].kVA]);
- 16:
- for i := 1 to NumWindings do
- Result := Result + Format('%.7g, ', [Winding^[i].puTap]);// InterpretAllTaps(Param);
- 17:
- Result := Format('%.7g', [puXHX * 100.0]);
- 18:
- Result := Format('%.7g', [puXHT * 100.0]);
- 19:
- Result := Format('%.7g', [puXXT * 100.0]);
- 20:
- for i := 1 to (NumWindings - 1) * NumWindings div 2 do
- Result := Result + Format('%-g, ', [puXSC^[i] * 100.0]);// Parser.ParseAsVector(((NumWindings - 1)*NumWindings div 2), Xsc);
- 26:
- Result := Format('%.7g', [pctLoadLoss]);
- 27:
- Result := Format('%.7g', [pctNoLoadLoss]);
- 28:
- Result := Format('%.7g', [NormMaxHkVA]);
- 29:
- Result := Format('%.7g', [EmergMaxHkVA]);
- 31:
- Result := Format('%.7g', [Winding^[ActiveWinding].MaxTap]);
- 32:
- Result := Format('%.7g', [Winding^[ActiveWinding].MinTap]);
- 33:
- Result := Format('%-d', [Winding^[ActiveWinding].NumTaps]);
- 35:
- Result := Format('%.7g', [pctImag]);
- 36:
- Result := Format('%.7g', [ppm_FloatFactor / 1.0e-6]);
- 37:
- for i := 1 to NumWindings do
- Result := Result + Format('%.7g, ', [Winding^[i].rpu * 100.0]);
- 40:
- Result := StrYorN(XRconst);
- 42:
- Result := GetAutoWindingCurrentsResult;
-
- else
- Result := inherited GetPropertyValue(index);
- end;
-
- // Overrides
- case (Index - NumPropsThisClass) of
- 1:
- Result := Format('%-.5g', [normamps]); //Normamps
- 2:
- Result := Format('%-.5g', [emergamps]); //emergamps
- end;
-
- case Index of
- 12..16, 20, 37:
- Result := Result + ']';
- else
- end;
-
-end;
-
-procedure TAutoTransObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := '3'; //'phases';
- PropertyValue[2] := '2'; //'windings';
- // Winding Definition
- PropertyValue[3] := '1'; //'wdg';
- PropertyValue[4] := Getbus(1); //'bus';
- PropertyValue[5] := 'Series'; // 'conn';
- PropertyValue[6] := '115'; // IF 2or 3-phase: phase-phase ELSE actual winding
- PropertyValue[7] := '100000';
- PropertyValue[8] := '1.0';
- PropertyValue[9] := '0.2';
- PropertyValue[10] := '0.2645'; // 0.002 pu @ 115 kV, 100 MVA
- PropertyValue[11] := 'shell';
-
- // General Data
- PropertyValue[12] := '';
- PropertyValue[13] := '';
- PropertyValue[14] := ''; // IF 1-phase: actual winding rating; ELSE phase-phase
- PropertyValue[15] := ''; // IF 1-phase: actual winding rating; ELSE phase-phase
- PropertyValue[16] := '';
- PropertyValue[17] := '7';
- PropertyValue[18] := '35';
- PropertyValue[19] := '30';
- PropertyValue[20] := ''; // x12 13 14... 23 24.. 34 ..
- PropertyValue[21] := '2';
- PropertyValue[22] := '.8';
- PropertyValue[23] := '.8';
- PropertyValue[24] := '65';
- PropertyValue[25] := '15';
- PropertyValue[26] := Format('%.7g', [pctLoadLoss]);
- PropertyValue[27] := Format('%.7g', [pctNoLoadLoss]); // Defaults to zero
- PropertyValue[28] := '';
- PropertyValue[29] := '';
- PropertyValue[30] := 'n'; // =y/n
- PropertyValue[31] := '1.10';
- PropertyValue[32] := '0.90';
- PropertyValue[33] := '32';
- PropertyValue[34] := '';
- PropertyValue[35] := '0';
- PropertyValue[36] := '1';
- PropertyValue[37] := '';
- PropertyValue[38] := '';
- PropertyValue[39] := '';
- PropertyValue[40] := 'NO';
- PropertyValue[41] := 'Lag';
- PropertyValue[42] := '0';
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
- // Override some Inherited properties
- PropertyValue[NumPropsThisClass + 1] := '400'; //Normamps
- PropertyValue[NumPropsThisClass + 2] := '600'; //emergamps
- PropertyValue[NumPropsThisClass + 3] := '0.007'; //Fault rate
- PropertyValue[NumPropsThisClass + 4] := '100'; // Pct Perm
- PropertyValue[NumPropsThisClass + 5] := '36'; // Hrs to repair
-
- ClearPropSeqArray; // so the overrides don't show up on save
-
end;
function TAutoTransObj.RotatePhases(iPhs: Integer): Integer;
@@ -2188,30 +1584,25 @@ function TAutoTransObj.RotatePhases(iPhs: Integer): Integer;
end;
-procedure TAutoTransObj.MakePosSequence;
-{
- Converts default 3-phase AutoTrans model into equivalent positive-sequence
- using scripting
-}
+procedure TAutoTransObj.MakePosSequence();
+// Converts default 3-phase AutoTrans model into equivalent positive-sequence
var
- iW,
- i,
- N: Integer;
- S: String;
+ iW, i, N: Integer;
Nodes: array[1..50] of Integer; // big integer buffer
OnPhase1: Boolean;
+ new_norm, new_emerg: Double;
+ new_conns: Array of Integer;
+ new_buses: Array of String;
+ new_kVs, new_kVAs: Array of Double;
begin
-
- {First, determine if we can convert this one.}
+ // First, determine if we can convert this one.
if (FnPhases = 1) or (FNphases = 2) then
begin //disable if any terminal not connected to phase one
for iW := 1 to NumWindings do
begin
OnPhase1 := FALSE;
- {Load up auxiliary parser}
- AuxParser.CmdString := GetBus(iW);
- AuxParser.NextParam;
- S := AuxParser.ParseAsBusName(N, pIntegerArray(@Nodes));
+ // Load up auxiliary parser
+ DSS.AuxParser.ParseAsBusName(GetBus(iW), N, pIntegerArray(@Nodes));
if N = 0 then
OnPhase1 := TRUE;
for i := 1 to N do
@@ -2225,36 +1616,41 @@ procedure TAutoTransObj.MakePosSequence;
end;
end;
- {Construct AutoTrans definition string }
- S := 'Phases=1 Conns=(';
- for i := 1 to NumWindings do
- S := S + 'Wye ';
- S := S + ') buses=(';
+ // Construct AutoTrans definition string
+ SetLength(new_conns, NumWindings);
+ SetLength(new_buses, NumWindings);
+ SetLength(new_kVs, NumWindings);
+ SetLength(new_kVAs, NumWindings);
for i := 1 to NumWindings do
- S := S + Getbus(i) + ' ';
- S := S + ') kVS=(';
+ new_conns[i - 1] := 0;
+ for i := 1 to NumWindings do
+ new_buses[i - 1] := Getbus(i);
for i := 1 to NumWindings do
with Winding^[i] do
if (NPhases > 1) or (Connection <> TAutoTransConnection.Wye) then
- S := S + Format(' %-.5g', [kVLL / SQRT3])
+ new_kVs[i - 1] := kVLL / SQRT3
else
- S := S + Format(' %-.5g', [kVLL]);
- S := S + ') kVAs=(';
+ new_kVs[i - 1] := kVLL;
for i := 1 to NumWindings do
with Winding^[i] do
- S := S + Format(' %-.5g', [kVA / FNPhases]);
- S := S + ')';
-
- S := S + ' NormHkVA=' + Format(' %-.5g %-.5g', [NormMaxHkVA / FNPhases, EmergMaxHkVA / FNPhases]);
-
- Parser.CmdString := S;
- Edit;
-
+ new_kVAs[i - 1] := kVA / FNPhases;
+
+ new_norm := NormMaxHkVA / FNPhases;
+ new_emerg := EmergMaxHkVA / FNPhases;
+
+ BeginEdit(True);
+ SetInteger(ord(TProp.Phases), 1);
+ SetIntegers(ord(TProp.Conns), new_conns);
+ SetStrings(ord(TProp.Buses), new_buses);
+ SetDoubles(ord(TProp.kVs), new_kVs);
+ SetDoubles(ord(TProp.kVAs), new_kVAs);
+ SetDouble(ord(TProp.NormHkVA), new_norm);
+ SetDouble(ord(TProp.EmergHkVA), new_emerg);
+ EndEdit(7);
inherited;
-
end;
@@ -2265,7 +1661,6 @@ procedure TAutoTransObj.BuildYPrimComponent(YPrim_Component, Y_Terminal: TCMatri
k: Integer;
Value: complex;
j: Integer;
-
begin
with YPrim_Component do
begin
@@ -2293,14 +1688,11 @@ function TAutoTransObj.Get_BasekVLL(i: Integer): Double;
procedure TAutoTransObj.GICBuildYTerminal;
// Build YTerminal considering only resistance and no coupling to other winding.
-
var
i, j, idx: Integer;
yR: Complex;
Yadder: Complex;
-
begin
-
Y_Term.Clear;
Y_Term_NL.Clear;
@@ -2312,27 +1704,25 @@ procedure TAutoTransObj.GICBuildYTerminal;
idx := 2 * i - 1;
SetElement(idx, idx, yR);
SetElement(idx + 1, idx + 1, yR);
- SetElemSym(idx, idx + 1, Cnegate(yR)); // set off-diagonals
+ SetElemSym(idx, idx + 1, -yR); // set off-diagonals
end;
end;
- {For GIC add a small *Conductance* to both conductors of each winding so that
- the matrix will always invert even if the user neglects to define a voltage
- reference on all sides}
+ // For GIC add a small *Conductance* to both conductors of each winding so that
+ // the matrix will always invert even if the user neglects to define a voltage
+ // reference on all sides
if ppm_FloatFactor <> 0.0 then
with Y_Term do
for i := 1 to NumWindings do
begin
Yadder := cmplx(-Winding^[i].Y_PPM, 0.0); // G + j0
for j := (2 * i - 1) to (2 * i) do
- SetElement(j, j, Cadd(GetElement(j, j), Yadder));
-{ SetElement(j, j, CmulReal_im(GetElement(j, j) , ppm_FloatFactorPlusOne));}
+ SetElement(j, j, GetElement(j, j) + Yadder);
+ // SetElement(j, j, CmulReal_im(GetElement(j, j) , ppm_FloatFactorPlusOne));
end;
-
end;
procedure TAutoTransObj.CalcY_Terminal(FreqMult: Double);
-
var
i,
j,
@@ -2345,8 +1735,9 @@ procedure TAutoTransObj.CalcY_Terminal(FreqMult: Double);
Yadder: Complex;
Rmult: Double;
ZCorrected: Double;
- {Function to fix a specification of a pu tap of 0.0}
- {Regcontrol can attempt to force zero tap position in some models}
+ puXst, Vs, Vc: Double;
+ // Function to fix a specification of a pu tap of 0.0
+ // Regcontrol can attempt to force zero tap position in some models
function ZeroTapFix(const tapvalue: Double): Double;
begin
if TapValue = 0.0 then
@@ -2356,37 +1747,48 @@ procedure TAutoTransObj.CalcY_Terminal(FreqMult: Double);
end;
begin
-
// check for GIC build
if ActiveCircuit.Solution.Frequency < 0.51 then
- {Build Yterminal for GIC ~dc simulation}
-
+ // Build Yterminal for GIC ~dc simulation
GICBuildYTerminal
-
else
- begin {Normal Y matrix build}
+ begin //Normal Y matrix build
if XRConst then
RMult := FreqMult
else
RMult := 1.0;
-
- // Construct ZBMatrix;
+ // Construct ZBMatrix;
ZB.Clear;
ZBase := 1.0 / (VABase / Fnphases); // base ohms on 1.0 volt basis
- // Adjust specified XSC by SQR(1 + Vcommon/Vseries)
- ZCorrected := ZBase * SQR(1.0 + Winding^[2].vbase / Winding^[1].Vbase); // Correction factor for Series
+
+ // Adjust specified XSC by SQR(1 + Vcommon/Vseries)
+ ZCorrected := ZBase * SQR(1.0 + Winding^[2].vbase / Winding^[1].Vbase); // Correction factor for Series, as in Dommel (6.45) for Zsc or puXSC^[1]
+ // since the losses are correct as they are, mimic Dommel (6.50) for Zst or puXSC^[2], without disturbing Zbase or Zcorrected
+ // Dommel: Xst = Xhl*Vh*Vl/(Vh-Vl)^2 + Xht*Vh/(Vh-Vl) - Xlt*Vl/(Vh-Vl)
+ // = Xhl*(Vs+Vc)*Vc/Vs^2 + Xht*(Vs+Vc)/Vs - Xlt*Vc/Vs
+ if NumWindings > 2 then
+ begin
+ Vc := Winding^[2].VBase;
+ Vs := Winding^[1].VBase;
+ puXst := puXSC^[1] * (Vs + Vc) * Vc / Vs / Vs + puXSC^[2] * (Vs + Vc) / Vs - puXSC^[3] * Vc / Vs;
+ end
+ else
+ puXst := 0.0;
+
for i := 1 to Numwindings - 1 do
begin
- { convert pu to ohms on one volt base as we go... }
if i = 1 then
- ZB.SetElement(i, i, CmulReal(Cmplx(Rmult * (Winding^[1].Rpu + Winding^[i + 1].Rpu), Freqmult * puXSC^[i]), ZCorrected))
+ ZB.SetElement(i, i, Cmplx(Rmult * (Winding^[1].Rpu + Winding^[i + 1].Rpu), Freqmult * puXSC^[i]) * ZCorrected)
+ else
+ if i = 2 then
+ ZB.SetElement(i, i, Cmplx(Rmult * (Winding^[1].Rpu + Winding^[i + 1].Rpu), Freqmult * puXst) * Zbase)
else
- ZB.SetElement(i, i, CmulReal(Cmplx(Rmult * (Winding^[1].Rpu + Winding^[i + 1].Rpu), Freqmult * puXSC^[i]), Zbase));
+ ZB.SetElement(i, i, Cmplx(Rmult * (Winding^[1].Rpu + Winding^[i + 1].Rpu), Freqmult * puXSC^[i]) * Zbase);
end;
- // Off diagonals
+ // Off diagonals
k := NumWindings;
with ZB do
for i := 1 to Numwindings - 1 do
@@ -2394,10 +1796,12 @@ procedure TAutoTransObj.CalcY_Terminal(FreqMult: Double);
for j := i + 1 to Numwindings - 1 do
begin
SetElemSym(i, j,
- CmulReal(
- Csub(CAdd(GetElement(i, i), GetElement(j, j)),
- CmulReal(Cmplx(Rmult * (Winding^[i + 1].Rpu + Winding^[j + 1].Rpu), Freqmult * puXSC^[k]), ZBase))
- , 0.5));
+ (
+ GetElement(i, i)
+ + GetElement(j, j)
+ - Cmplx(Rmult * (Winding^[i + 1].Rpu + Winding^[j + 1].Rpu), Freqmult * puXSC^[k]) * ZBase
+ ) * 0.5
+ );
Inc(k);
end;
end;
@@ -2406,27 +1810,26 @@ procedure TAutoTransObj.CalcY_Terminal(FreqMult: Double);
if ZB.InvertError > 0 then
begin
- DoErrorMsg('TAutoTransObj.CalcYPrim', 'Matrix Inversion Error for AutoTrans "' + Name + '"',
- 'Invalid impedance specified. Replaced with tiny conductance to ground.', 117);
+ DoErrorMsg('TAutoTransObj.CalcYPrim',
+ Format(_('Matrix Inversion Error for AutoTrans "%s"'), [Name]),
+ _('Invalid impedance specified. Replaced with tiny conductance to ground.'), 117);
ZB.Clear;
for i := 1 to ZB.Order do
ZB.SetElement(i, i, Cmplx(EPSILON, 0.0));
end;
-
- // Now construct Y_Oneturn = AT * ZB.Invert * A
- { -1 1 0 ...
- A = -1 0 1 .. order: N-1 x N N = NumWindings
- ...
- -1 -1 ...
- AT = Transpose of A = 1 0 ... N X N-1
- 0 1 ..
- }
-
+ // Now construct Y_Oneturn = AT * ZB.Invert * A
+ // -1 1 0 ...
+ // A = -1 0 1 .. order: N-1 x N N = NumWindings
+ // ...
+ // -1 -1 ...
+ // AT = Transpose of A = 1 0 ... N X N-1
+ // 0 1 ..
+ //
Y_1Volt.Clear;
Y_1Volt_NL.Clear;
- {Allocate temp complex arrays}
+ // Allocate temp complex arrays
ctempArray1 := AllocMem(SizeOf(Complex) * NumWindings * 2);
ctempArray2 := AllocMem(SizeOf(Complex) * NumWindings * 2);
@@ -2449,22 +1852,22 @@ procedure TAutoTransObj.CalcY_Terminal(FreqMult: Double);
A^[k] := cONE
else
A^[k] := cZERO;
- ZB.MVmult(ctemparray1, A); {Zb.invert * A}
- AT.MVmult(ctempArray2, ctemparray1); {AT * Result}
+ ZB.MVmult(ctemparray1, A); // Zb.invert * A
+ AT.MVmult(ctempArray2, ctemparray1); // AT * Result
for j := 1 to NumWindings do
Y_1Volt.SetElement(j, i, ctempArray2^[j]);
end;
- {Add magnetizing Reactance to 2nd winding, assuming it is closest to the core
- Add both resistive element representing core losses and a reactive element representing
- magnetizing current
- }
+ // Add magnetizing Reactance to 2nd winding, assuming it is closest to the core
+ // Add both resistive element representing core losses and a reactive element representing
+ // magnetizing current
+
Y_1Volt_NL.AddElement(2, 2, Cmplx((pctNoLoadLoss / 100.0 / Zbase), -pctImag / 100.0 / Zbase / Freqmult));
- // should have admittance of one phase of the AutoTrans on a one-volt, wye-connected base
+ // should have admittance of one phase of the AutoTrans on a one-volt, wye-connected base
- // Now make into terminal admittance matrix and correct for actual voltage ratings
- // Y_Terminal = AT * Y_onevolt * A where V_onevolt = A * V_terminal
+ // Now make into terminal admittance matrix and correct for actual voltage ratings
+ // Y_Terminal = AT * Y_onevolt * A where V_onevolt = A * V_terminal
AT.Free;
@@ -2472,7 +1875,7 @@ procedure TAutoTransObj.CalcY_Terminal(FreqMult: Double);
Y_Term_NL.Clear;
AT := TcMatrix.Creatematrix(NumWindings * 2);
- // 8/22/2013 Added ZeroTapFix so that regcontrol can set a tap to zero
+ // 8/22/2013 Added ZeroTapFix so that regcontrol can set a tap to zero
for i := 1 to NumWindings do
with Winding^[i] do
@@ -2496,29 +1899,29 @@ procedure TAutoTransObj.CalcY_Terminal(FreqMult: Double);
else
A^[k] := cZERO;
end;
- {Main AutoTrans part}
+ // Main AutoTrans part
Y_1Volt.MVmult(ctemparray1, A);
- AT.MVmult(ctemparray2, ctemparray1); {AT * Result}
+ AT.MVmult(ctemparray2, ctemparray1); // AT * Result
for j := 1 to 2 * NumWindings do
Y_Term.SetElement(j, i, ctemparray2^[j]);
- {No Load part}
+ // No Load part
Y_1Volt_NL.MVmult(ctemparray1, A);
- AT.MVmult(ctemparray2, ctemparray1); {AT * Result}
+ AT.MVmult(ctemparray2, ctemparray1); // AT * Result
for j := 1 to 2 * NumWindings do
Y_Term_NL.SetElement(j, i, ctemparray2^[j]);
end;
- {Add a small Admittance to both conductors of each winding so that
- the matrix will always invert even if the user neglects to define a voltage
- reference on all sides}
+ // Add a small Admittance to both conductors of each winding so that
+ // the matrix will always invert even if the user neglects to define a voltage
+ // reference on all sides
if ppm_FloatFactor <> 0.0 then
with Y_Term do
for i := 1 to NumWindings do
begin
Yadder := cmplx(0.0, Winding^[i].Y_PPM);
for j := (2 * i - 1) to (2 * i) do
- SetElement(j, j, Cadd(GetElement(j, j), Yadder));
-{ SetElement(j, j, CmulReal_im(GetElement(j, j) , ppm_FloatFactorPlusOne));}
+ SetElement(j, j, GetElement(j, j) + Yadder);
+ // SetElement(j, j, CmulReal_im(GetElement(j, j) , ppm_FloatFactorPlusOne));
end;
AT.Free;
@@ -2529,71 +1932,7 @@ procedure TAutoTransObj.CalcY_Terminal(FreqMult: Double);
end;
Y_Terminal_FreqMult := Freqmult;
-
-end;
-
-procedure TAutoTransObj.FetchXfmrCode(const Code: String);
-// Uses strandard Xfmrcode, but forces connection of first two windings.
-var
- Obj: TXfmrCodeObj;
- i: Integer;
-begin
- if DSS.XfmrCodeClass.SetActive(Code) then
- begin
- Obj := DSS.XfmrCodeClass.GetActiveObj;
- XfmrCode := LowerCase(Code);
- // set sizes and copy parameters
- Nphases := Obj.Fnphases;
- SetNumWindings(Obj.NumWindings);
- NConds := Fnphases + 1; // forces reallocation of terminals and conductors
- for i := 1 to NumWindings do
- with Winding^[i] do
- begin
- case i of
- 1:
- Connection := TAutoTransConnection.Series; // No Choice for 1st two
- 2:
- Connection := TAutoTransConnection.Wye; // Wye
- else
- Connection := TAutoTransConnection(Obj.Winding^[i].Connection);
- end;
- kVLL := Obj.Winding^[i].kVLL;
- VBase := Obj.Winding^[i].VBase;
- kVA := Obj.Winding^[i].kVA;
- puTAP := Obj.Winding^[i].puTAP;
- Rpu := Obj.Winding^[i].Rpu;
- RdcOhms := Obj.Winding^[i].RdcOhms;
- RdcSpecified := TRUE; //This will force calc of Rdcpu
- TapIncrement := Obj.Winding^[i].TapIncrement;
- MinTap := Obj.Winding^[i].MinTap;
- MaxTap := Obj.Winding^[i].MaxTap;
- NumTaps := Obj.Winding^[i].NumTaps;
- end;
- SetTermRef;
- puXHX := Obj.XHL;
- puXHT := Obj.XHT;
- puXXT := Obj.XLT;
- for i := 1 to (NumWindings * (NumWindings - 1) div 2) do
- puXSC^[i] := Obj.XSC^[i];
- ThermalTimeConst := Obj.ThermalTimeConst;
- n_thermal := Obj.n_thermal;
- m_thermal := Obj.m_thermal;
- FLrise := Obj.FLrise;
- HSrise := Obj.HSrise;
- pctLoadLoss := Obj.pctLoadLoss;
- pctNoLoadLoss := Obj.pctNoLoadLoss;
- pctImag := Obj.pctImag; // Omission corrected 12-14-18
- NormMaxHkVA := Obj.NormMaxHkVA;
- EmergMaxHkVA := Obj.EmergMaxHkVA;
- ppm_FloatFactor := Obj.ppm_FloatFactor;
- Yorder := fNConds * fNTerms;
- YPrimInvalid := TRUE;
- Y_Terminal_FreqMult := 0.0;
-
- RecalcElementData
- end
- else
- DoSimpleMsg('Xfmr Code:' + Code + ' not found.', 100180);
end;
+finalization AutoTransConnectionEnum.Free;
end.
diff --git a/src/PDElements/Capacitor.pas b/src/PDElements/Capacitor.pas
index 842b3acae..e40f31d25 100644
--- a/src/PDElements/Capacitor.pas
+++ b/src/PDElements/Capacitor.pas
@@ -7,42 +7,37 @@
----------------------------------------------------------
}
-{ 4-17-00 Made IsShunt Public
- 12-7-04 Added Reactance in series with each capacitor
+// Basic capacitor
+//
+// Implemented as a two-terminal constant impedance (Power Delivery Element)
+//
+// Bus2 connection defaults to 0 node of Bus1 (if Bus2 has the default bus connection
+// at the time Bus1 is defined. Therefore, if only Bus1 is specified, a shunt capacitor results.
+// If delta connected, Bus2 is set to node zero of Bus1 and nothing is returned in the lower
+// half of YPrim - all zeroes.
+//
+// If an ungrounded wye is desired, explicitly set Bus2= and set all nodes the same,
+// e.g. Bus1.4.4.4 (uses 4th node of Bus1 as neutral point)
+// or BusNew.1.1.1 (makes a new bus for the neutral point)
+// You must specify the nodes or you will get a series capacitor!
+//
+// A series capacitor is specified simply by setting bus2 and declaring the connection
+// to be Wye. If the connection is specified as delta, nothing will be connected to Bus2.
+// In fact the number of terminals is set to 1.
+//
+// Capacitance may be specified as:
+//
+// 1. kvar and kv ratings at base frequency. impedance. Specify kvar as total for
+// all phases (all cans assumed equal). For 1-phase, kV = capacitor can kV rating.
+// For 2 or 3-phase, kV is line-line three phase. For more than 3 phases, specify
+// kV as actual can voltage.
+// 2. Capacitance in uF to be used in each phase. If specified in this manner,
+// the given value is always used whether wye or delta.
+// 3. A nodal C matrix (like a nodal admittance matrix).
+// If conn=wye then 2-terminal through device
+// If conn=delta then 1-terminal.
+// Microfarads.
-}
-{Basic capacitor
-
- Implemented as a two-terminal constant impedance (Power Delivery Element)
-
- Bus2 connection defaults to 0 node of Bus1 (if Bus2 has the default bus connection
- at the time Bus1 is defined. Therefore, if only Bus1 is specified, a shunt capacitor results.
- If delta connected, Bus2 is set to node zero of Bus1 and nothing is returned in the lower
- half of YPrim - all zeroes.
-
- If an ungrounded wye is desired, explicitly set Bus2= and set all nodes the same,
- e.g. Bus1.4.4.4 (uses 4th node of Bus1 as neutral point)
- or BusNew.1.1.1 (makes a new bus for the neutral point)
- You must specify the nodes or you will get a series capacitor!
-
- A series capacitor is specified simply by setting bus2 and declaring the connection
- to be Wye. If the connection is specified as delta, nothing will be connected to Bus2.
- In fact the number of terminals is set to 1.
-
- Capacitance may be specified as:
-
- 1. kvar and kv ratings at base frequency. impedance. Specify kvar as total for
- all phases (all cans assumed equal). For 1-phase, kV = capacitor can kV rating.
- For 2 or 3-phase, kV is line-line three phase. For more than 3 phases, specify
- kV as actual can voltage.
- 2. Capacitance in uF to be used in each phase. If specified in this manner,
- the given value is always used whether wye or delta.
- 3. A nodal C matrix (like a nodal admittance matrix).
- If conn=wye then 2-terminal through device
- If conn=delta then 1-terminal.
- Microfarads.
-
-}
interface
uses
@@ -55,30 +50,39 @@ interface
ArrayDef;
type
+{$SCOPEDENUMS ON}
+ TCapacitorProp = (
+ INVALID = 0,
+ bus1 = 1,
+ bus2 = 2,
+ phases = 3,
+ kvar = 4,
+ kv = 5,
+ conn = 6,
+ cmatrix = 7,
+ cuf = 8,
+ R = 9,
+ XL = 10,
+ Harm = 11,
+ Numsteps = 12,
+ states = 13
+ );
+
+ TCapacitorConnection = TGeneralConnection;
+{$SCOPEDENUMS OFF}
TCapacitor = class(TPDClass)
- PRIVATE
- procedure DoCmatrix;
-
- procedure InterpretConnection(const S: String);
- procedure CapSetBus1(const s: String);
PROTECTED
- function MakeLike(const CapacitorName: String): Integer; OVERRIDE;
- procedure DefineProperties; // Add Properties of this class to propName
+ procedure DefineProperties; override; // Add Properties of this class to propName
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TCapacitorObj = class(TPDElement)
-{$IFDEF DSS_CAPI}
PUBLIC
-{$ELSE}
- PRIVATE
-{$ENDIF}
FC,
FXL,
Fkvarrating,
@@ -102,33 +106,29 @@ TCapacitorObj = class(TPDElement)
procedure set_States(Idx: Integer; const Value: Integer);
procedure set_LastStepInService(const Value: Integer);
- procedure ProcessHarmonicSpec(const Param: String);
- procedure ProcessStatesSpec(const Param: String);
procedure MakeYprimWork(YprimWork: TcMatrix; iStep: Integer);
procedure set_NumSteps(const Value: Integer); // 1=kvar, 2=Cuf, 3=Cmatrix
{$IFDEF DSS_CAPI_INCREMENTAL_Y}
- PROTECTED
procedure Set_ConductorClosed(Index: Integer; Value: Boolean); OVERRIDE;
// procedure Set_Enabled(Value: Boolean); OVERRIDE;
{$ENDIF}
PUBLIC
-
- Connection: Integer; // 0 or 1 for wye (default) or delta, respectively
+ Connection: TCapacitorConnection;
constructor Create(ParClass: TDSSClass; const CapacitorName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
function AddStep: Boolean;
function SubtractStep: Boolean;
@@ -147,424 +147,332 @@ TCapacitorObj = class(TPDElement)
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Sysutils,
- Ucomplex,
+ UComplex, DSSUcomplex,
DSSObjectHelper,
Utilities,
Solution,
DSSHelper,
TypInfo;
+type
+ TObj = TCapacitorObj;
+ TProp = TCapacitorProp;
const
- NumPropsThisClass = 13;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TCapacitor.Create(dssContext: TDSSContext); // Creates superstructure for all Capacitor objects
+constructor TCapacitor.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'Capacitor';
- DSSClassType := DSSClassType + CAP_ELEMENT;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- ActiveElement := 0;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, CAP_ELEMENT, 'Capacitor');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TCapacitor.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TCapacitor.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
- PropertyName^[1] := 'bus1';
- PropertyName^[2] := 'bus2';
- PropertyName^[3] := 'phases';
- PropertyName^[4] := 'kvar';
- PropertyName^[5] := 'kv';
- PropertyName^[6] := 'conn';
- PropertyName^[7] := 'cmatrix';
- PropertyName^[8] := 'cuf';
- PropertyName^[9] := 'R';
- PropertyName^[10] := 'XL';
- PropertyName^[11] := 'Harm';
- PropertyName^[12] := 'Numsteps';
- PropertyName^[13] := 'states';
-
- // define Property help values
-
- PropertyHelp^[1] := 'Name of first bus of 2-terminal capacitor. Examples:' + CRLF +
- 'bus1=busname' + CRLF + 'bus1=busname.1.2.3' + CRLF + CRLF +
- 'If only one bus specified, Bus2 will default to this bus, Node 0, ' +
- 'and the capacitor will be a Yg shunt bank.';
- PropertyHelp^[2] := 'Name of 2nd bus. Defaults to all phases connected ' +
- 'to first bus, node 0, (Shunt Wye Connection) ' +
- 'except when Bus2 explicitly specified. ' + CRLF + CRLF +
- 'Not necessary to specify for delta (LL) connection.';
- PropertyHelp^[3] := 'Number of phases.';
- PropertyHelp^[4] := 'Total kvar, if one step, or ARRAY of kvar ratings for each step. Evenly divided among phases. See rules for NUMSTEPS.';
- PropertyHelp^[5] := 'For 2, 3-phase, kV phase-phase. Otherwise specify actual can rating.';
- PropertyHelp^[6] := '={wye | delta |LN |LL} Default is wye, which is equivalent to LN';
- PropertyHelp^[7] := 'Nodal cap. matrix, lower triangle, microfarads, of the following form:' + CRLF + CRLF +
- 'cmatrix="c11 | -c21 c22 | -c31 -c32 c33"' + CRLF + CRLF +
- 'All steps are assumed the same if this property is used.';
- PropertyHelp^[8] := 'ARRAY of Capacitance, each phase, for each step, microfarads.' + CRLF +
- 'See Rules for NumSteps.';
- PropertyHelp^[9] := 'ARRAY of series resistance in each phase (line), ohms. Default is 0.0';
- PropertyHelp^[10] := 'ARRAY of series inductive reactance(s) in each phase (line) for filter, ohms at base frequency. Use this OR "h" property to define filter. Default is 0.0.';
- PropertyHelp^[11] := 'ARRAY of harmonics to which each step is tuned. Zero is interpreted as meaning zero reactance (no filter). Default is zero.';
- PropertyHelp^[12] := 'Number of steps in this capacitor bank. Default = 1. Forces reallocation of the capacitance, reactor, and states array. Rules: ' +
- 'If this property was previously =1, the value in the kvar property is divided equally among the steps. The kvar property ' +
- 'does not need to be reset if that is accurate. If the Cuf or Cmatrix property was used previously, all steps are set to the value of the first step. ' +
- 'The states property is set to all steps on. All filter steps are set to the same harmonic. ' +
- 'If this property was previously >1, the arrays are reallocated, but no values are altered. You must SUBSEQUENTLY assign all array properties.';
- PropertyHelp^[13] := 'ARRAY of integers {1|0} states representing the state of each step (on|off). Defaults to 1 when reallocated (on). ' +
- 'Capcontrol will modify this array as it turns steps on or off.';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // real matrix
+ PropertyType[ord(TProp.cmatrix)] := TPropertyType.DoubleSymMatrixProperty;
+ PropertyOffset[ord(TProp.cmatrix)] := ptruint(@obj.Cmatrix);
+ PropertyOffset2[ord(TProp.cmatrix)] := ptruint(@obj.Fnphases);
+ PropertyScale[ord(TProp.cmatrix)] := 1.0e-6;
+
+ // array of doubles
+ PropertyType[ord(TProp.kvar)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.kvar)] := ptruint(@obj.FkvarRating);
+ PropertyOffset2[ord(TProp.kvar)] := ptruint(@obj.FNumSteps);
+
+ PropertyType[ord(TProp.R)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.R)] := ptruint(@obj.FR);
+ PropertyOffset2[ord(TProp.R)] := ptruint(@obj.FNumSteps);
+
+ PropertyType[ord(TProp.XL)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.XL)] := ptruint(@obj.FXL);
+ PropertyOffset2[ord(TProp.XL)] := ptruint(@obj.FNumSteps);
+
+ PropertyType[ord(TProp.cuf)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.cuf)] := ptruint(@obj.FC);
+ PropertyOffset2[ord(TProp.cuf)] := ptruint(@obj.FNumSteps);
+ PropertyScale[ord(TProp.cuf)] := 1.0e-6;
+
+ PropertyType[ord(TProp.Harm)] := TPropertyType.DoubleArrayProperty;
+ PropertyOffset[ord(TProp.Harm)] := ptruint(@obj.FHarm);
+ PropertyOffset2[ord(TProp.Harm)] := ptruint(@obj.FNumSteps);
+
+ // array of ints
+ PropertyType[ord(TProp.states)] := TPropertyType.IntegerArrayProperty;
+ PropertyOffset[ord(TProp.states)] := ptruint(@obj.FStates);
+ PropertyOffset2[ord(TProp.states)] := ptruint(@obj.FNumSteps);
+
+ // enum properties
+ PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.conn)] := ptruint(@obj.Connection);
+ PropertyOffset2[ord(TProp.conn)] := PtrInt(DSS.ConnectionEnum);
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyType[ord(TProp.bus2)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+ PropertyOffset[ord(TProp.bus2)] := 2;
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.kv)] := ptruint(@obj.kvrating);
+
+ // integer properties
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ PropertyType[ord(TProp.NumSteps)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.NumSteps)] := ptruint(@obj.FNumSteps);
+ PropertyFlags[ord(TProp.NumSteps)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ inherited DefineProperties;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TCapacitor.NewObject(const ObjName: String): Integer;
-begin
- // create a new object of this class and add to list
- with ActiveCircuit do
- begin
- ActiveCktElement := TCapacitorObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
-end;
-
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TCapacitor.DoCmatrix;
+function TCapacitor.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- OrderFound, j: Integer;
- MatBuffer: pDoubleArray;
-
+ Obj: TObj;
begin
- with DSS.ActiveCapacitorObj do
- begin
- MatBuffer := Allocmem(Sizeof(Double) * Fnphases * Fnphases);
- OrderFound := Parser.ParseAsSymMatrix(Fnphases, MatBuffer);
-
- if OrderFound > 0 then // Parse was successful
- begin {C}
- Reallocmem(Cmatrix, Sizeof(Cmatrix^[1]) * Fnphases * Fnphases);
- for j := 1 to Fnphases * Fnphases do
- Cmatrix^[j] := 1.0e-6 * MatBuffer^[j];
- end;
-
- Freemem(MatBuffer, Sizeof(Double) * Fnphases * Fnphases);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TCapacitor.InterpretConnection(const S: String);
-
-// Accepts
-// delta or LL (Case insensitive)
-// Y, wye, or LN
+procedure TCapacitorObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- TestS: String;
-
-begin
- with DSS.ActiveCapacitorObj do
- begin
- TestS := lowercase(S);
- case TestS[1] of
- 'y', 'w':
- Connection := 0; {Wye}
- 'd':
- Connection := 1; {Delta or line-Line}
- 'l':
- case Tests[2] of
- 'n':
- Connection := 0;
- 'l':
- Connection := 1;
- end;
-
- end;
- case Connection of
- 1:
- Nterms := 1; // Force reallocation of terminals
- 0:
- if Fnterms <> 2 then
- Nterms := 2;
- end;
- end;
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TCapacitor.CapSetBus1(const s: String);
-
-var
- s2: String;
+ S, S2: String;
+ StepSize, RStep, XLstep: Double;
i, dotpos: Integer;
-
- // Special handling for Bus 1
- // Set Bus2 = Bus1.0.0.0
-
begin
- with DSS.ActiveCapacitorObj do
- begin
- SetBus(1, S);
-
- // Default Bus2 to zero node of Bus1 unless it is previously defined. (Grounded-Y connection)
+ case Idx of
+ ord(TProp.bus1):
+ // Special handling for Bus 1
+ // Set Bus2 = Bus1.0.0.0
+ // Default Bus2 to zero node of Bus1 unless it is previously defined. (Grounded-Y connection)
+ if not Bus2Defined and (Fnterms = 2) then // only for WYE connection
+ begin
+ // Strip node designations from S
+ S := GetBus(1);
+ dotpos := Pos('.', S);
+ if dotpos > 0 then
+ S2 := Copy(S, 1, dotpos - 1)
+ else
+ S2 := Copy(S, 1, Length(S)); // copy up to Dot
+ for i := 1 to Fnphases do
+ S2 := S2 + '.0'; // append series of ".0"'s
- if not Bus2Defined and (Fnterms = 2) then // only for WYE connection
+ SetBus(2, S2); // default setting for Bus2
+ IsShunt := TRUE;
+ PrpSequence^[2] := 0; // Reset this for save function
+ end;
+ ord(TProp.conn):
+ case Connection of
+ TCapacitorConnection.Delta:
+ Nterms := 1; // Force reallocation of terminals
+ TCapacitorConnection.Wye:
+ if Fnterms <> 2 then
+ Nterms := 2;
+ end;
+ 2:
begin
- // Strip node designations from S
- dotpos := Pos('.', S);
- if dotpos > 0 then
- S2 := Copy(S, 1, dotpos - 1)
- else
- S2 := Copy(S, 1, Length(S)); // copy up to Dot
- for i := 1 to Fnphases do
- S2 := S2 + '.0'; // append series of ".0"'s
-
- SetBus(2, S2); // default setting for Bus2
- IsShunt := TRUE;
+ NumTerm := 2; // Specifies that the capacitor is not connected to ground
+ if AnsiCompareText(StripExtension(GetBus(1)), StripExtension(GetBus(2))) <> 0 then
+ begin
+ IsShunt := FALSE;
+ Bus2Defined := TRUE;
+ end;
end;
- end;
-end;
-
+ ord(TProp.phases):
+ if Fnphases <> previousIntVal then
+ begin
+ NConds := Fnphases; // Force Reallocation of terminal info
+ Yorder := Fnterms * Fnconds;
+ end;
+ 4:
+ SpecType := 1;
+ 7:
+ SpecType := 3;
+ 8:
+ SpecType := 2;
+ ord(TProp.Numsteps):
+ begin
+ // reallocate all arrays associated with steps
+ Rstep := 0.0;
+ XLstep := 0.0;
+ if previousIntVal = 1 then
+ begin
+ // Save total values to be divided up
+ FTotalkvar := Fkvarrating^[1];
+ Rstep := FR^[1] * FNumSteps;
+ XLstep := FXL^[1] * FNumSteps;
+ end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TCapacitor.Edit: Integer;
+ // Reallocate arrays (Must be initialized to nil for first call)
+ Reallocmem(FC, Sizeof(FC^[1]) * FNumSteps);
+ Reallocmem(FXL, Sizeof(FXL^[1]) * FNumSteps);
+ Reallocmem(Fkvarrating, Sizeof(Fkvarrating^[1]) * FNumSteps);
+ Reallocmem(FR, Sizeof(FR^[1]) * FNumSteps);
+ Reallocmem(FHarm, Sizeof(FHarm^[1]) * FNumSteps);
+ Reallocmem(FStates, Sizeof(FStates^[1]) * FNumSteps);
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
- i: Integer;
+ // Special case for FNumSteps=1
+ if previousIntVal = 1 then
+ begin
+ case SpecType of
-begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveCapacitorObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveCapacitorObj; // use property to set this value
+ 1:
+ begin // kvar // We'll make a multi-step bank of same net size as at present
+ StepSize := FTotalkvar / FNumSteps;
+ for i := 1 to FNumSteps do
+ FkvarRating^[i] := StepSize;
+ end;
- with DSS.ActiveCapacitorObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
+ 2:
+ begin // Cuf // We'll make a multi-step bank with all the same as first
+ for i := 2 to FNumSteps do
+ FC^[i] := FC^[1]; // Make same as first step
+ end;
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
+ 3:
+ begin // Cmatrix // We'll make a multi-step bank with all the same as first
+ // Nothing to do since all will be the same
+ end;
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "Capacitor.' + Name + '"', 450);
- 1:
- CapSetbus1(param);
- 2:
- begin
- Setbus(2, param);
- NumTerm := 2; // Specifies that the capacitor is not connected to ground
end;
- 3:
-{ Numphases := Parser.IntValue}; // see below
- 4:
- FNumSteps := InterpretDblArray(Param, FNumSteps, FkvarRating);
- 5:
- kvRating := Parser.Dblvalue;
- 6:
- InterpretConnection(Param);
- 7:
- DoCMatrix;
- 8:
- FNumSteps := InterpretDblArray(Param, FNumSteps, FC);
- 9:
- FNumSteps := InterpretDblArray(Param, FNumSteps, FR);
- 10:
- FNumSteps := InterpretDblArray(Param, FNumSteps, FXL);
- 11:
- ProcessHarmonicSpec(Param);
- 12:
- NumSteps := Parser.IntValue;
- 13:
- ProcessStatesSpec(Param);
- else
- // Inherited Property Edits
- ClassEdit(DSS.ActiveCapacitorObj, ParamPointer - NumPropsThisClass)
- end;
- // Some specials ...
- case ParamPointer of
- 1:
- begin
- PropertyValue[2] := GetBus(2); // this gets modified
- PrpSequence^[2] := 0; // Reset this for save function
- end;
- 2:
- if CompareText(StripExtension(GetBus(1)), StripExtension(GetBus(2))) <> 0 then
+ case SpecType of
+
+ 1:
begin
- IsShunt := FALSE;
- Bus2Defined := TRUE;
+ for i := 1 to FNumSteps do
+ FR^[i] := Rstep;
+ for i := 1 to FNumSteps do
+ FXL^[i] := XLstep;
end;
- 3:
- if Fnphases <> Parser.IntValue then
- begin
- Nphases := Parser.IntValue;
- NConds := Fnphases; // Force Reallocation of terminal info
- Yorder := Fnterms * Fnconds;
+
+ 2, 3:
+ begin // Make R and XL same as first step
+ for i := 2 to FNumSteps do
+ FR^[i] := FR^[1];
+ for i := 2 to FNumSteps do
+ FXL^[i] := FXL^[1];
end;
- 4:
- SpecType := 1;
- 7:
- SpecType := 3;
- 8:
- begin
- SpecType := 2;
- for i := 1 to Fnumsteps do
- FC^[i] := FC^[i] * 1.0e-6;
- end;
- 10:
- begin
- for i := 1 to Fnumsteps do
- if FXL^[i] <> 0.0 then
- if FR^[i] = 0.0 then
- FR^[i] := Abs(FXL^[i]) / 1000.0; // put in something so it doesn't fail
- DoHarmonicRecalc := FALSE; // XL is specified
+
end;
- else
- end;
- //YPrim invalidation on anything that changes impedance values
- case ParamPointer of
- 3..8:
- YprimInvalid := TRUE;
- 12, 13:
- // Numsteps, states:
-{$IFDEF DSS_CAPI_INCREMENTAL_Y}
- // For changes in NumSteps and States, try to handle it incrementally
- if ((ActiveCircuit.Solution.SolverOptions and $FFFFFFFF) <> ord(TSolverOptions.ReuseNothing)) and
- (not ActiveCircuit.Solution.SystemYChanged) and
- (YPrim <> NIL) and
- (not YPrimInvalid)
- then
- ActiveCircuit.IncrCktElements.Add(DSS.ActiveCapacitorObj)
- else
-{$ENDIF}
- YprimInvalid := TRUE;
- else
+ for i := 1 to FNumSteps do
+ Fstates^[i] := 1; // turn 'em all ON
+ LastStepInService := FNumSteps;
+ for i := 2 to FNumSteps do
+ FHarm^[i] := FHarm^[1]; // tune 'em all the same as first
end;
-
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
end;
- RecalcElementData;
+ ord(TProp.XL):
+ begin
+ for i := 1 to Fnumsteps do
+ if FXL^[i] <> 0.0 then
+ if FR^[i] = 0.0 then
+ FR^[i] := Abs(FXL^[i]) / 1000.0; // put in something so it doesn't fail
+ DoHarmonicRecalc := FALSE; // XL is specified
+ end;
+ 11:
+ DoHarmonicRecalc := TRUE;
+ 13:
+ FindLastStepInService;
end;
+ //YPrim invalidation on anything that changes impedance values
+ case Idx of
+ 3..8:
+ YprimInvalid := TRUE;
+ 12, 13:
+ // Numsteps, states:
+{$IFDEF DSS_CAPI_INCREMENTAL_Y}
+ // For changes in NumSteps and States, try to handle it incrementally
+ if ((ActiveCircuit.Solution.SolverOptions and $FFFFFFFF) <> ord(TSolverOptions.ReuseNothing)) and
+ (not ActiveCircuit.Solution.SystemYChanged) and
+ (YPrim <> NIL) and
+ (not YPrimInvalid)
+ then
+ ActiveCircuit.IncrCktElements.Add(self)
+ else
+{$ENDIF}
+ YprimInvalid := TRUE;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TCapacitor.MakeLike(const CapacitorName: String): Integer;
+procedure TCapacitorObj.MakeLike(OtherPtr: Pointer);
var
- OtherCapacitor: TCapacitorObj;
+ Other: TObj;
i: Integer;
begin
- Result := 0;
- {See if we can find this Capacitor name in the present collection}
- OtherCapacitor := Find(CapacitorName);
- if OtherCapacitor <> NIL then
- with DSS.ActiveCapacitorObj do
- begin
-
- if Fnphases <> OtherCapacitor.Fnphases then
- begin
- NPhases := OtherCapacitor.Fnphases;
- NConds := Fnphases; // force reallocation of terminals and conductors
-
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
-
- end;
-
- NumSteps := OtherCapacitor.NumSteps;
+ inherited MakeLike(OtherPtr); // Take care of inherited class properties
+ Other := TObj(OtherPtr);
+ if Fnphases <> Other.Fnphases then
+ begin
+ FNPhases := Other.Fnphases;
+ NConds := Fnphases; // force reallocation of terminals and conductors
- for i := 1 to FNumSteps do
- begin
- FC^[i] := OtherCapacitor.FC^[i];
- Fkvarrating^[i] := OtherCapacitor.Fkvarrating^[i];
- FR^[i] := OtherCapacitor.FR^[i];
- FXL^[i] := OtherCapacitor.FXL^[i];
- FXL^[i] := OtherCapacitor.FXL^[i];
- FHarm^[i] := OtherCapacitor.FHarm^[i];
- Fstates^[i] := OtherCapacitor.Fstates^[i];
- end;
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
+ end;
- kvrating := OtherCapacitor.kvrating;
- Connection := OtherCapacitor.Connection;
- SpecType := OtherCapacitor.SpecType;
+ NumSteps := Other.NumSteps;
- if OtherCapacitor.Cmatrix = NIL then
- Reallocmem(Cmatrix, 0)
- else
- begin
- Reallocmem(Cmatrix, SizeOf(Cmatrix^[1]) * Fnphases * Fnphases);
- for i := 1 to Fnphases * Fnphases do
- Cmatrix^[i] := OtherCapacitor.Cmatrix^[i];
- end;
+ for i := 1 to FNumSteps do
+ begin
+ FC^[i] := Other.FC^[i];
+ Fkvarrating^[i] := Other.Fkvarrating^[i];
+ FR^[i] := Other.FR^[i];
+ FXL^[i] := Other.FXL^[i];
+ FXL^[i] := Other.FXL^[i];
+ FHarm^[i] := Other.FHarm^[i];
+ Fstates^[i] := Other.Fstates^[i];
+ end;
- ClassMakeLike(OtherCapacitor); // Take care of inherited class properties
+ kvrating := Other.kvrating;
+ Connection := Other.Connection;
+ SpecType := Other.SpecType;
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherCapacitor.PropertyValue[i];
- Result := 1;
- end
+ if Other.Cmatrix = NIL then
+ Reallocmem(Cmatrix, 0)
else
- DoSimpleMsg('Error in Capacitor MakeLike: "' + CapacitorName + '" Not Found.', 451);
-
-
+ begin
+ Reallocmem(Cmatrix, SizeOf(Cmatrix^[1]) * Fnphases * Fnphases);
+ for i := 1 to Fnphases * Fnphases do
+ Cmatrix^[i] := Other.Cmatrix^[i];
+ end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TCapacitor Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TCapacitorObj.Create(ParClass: TDSSClass; const CapacitorName: String);
-
-
begin
inherited Create(ParClass);
- Name := LowerCase(CapacitorName);
+ Name := AnsiLowerCase(CapacitorName);
DSSObjType := ParClass.DSSClassType;
- NPhases := 3; // Directly set conds and phases
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 2; // Force allocation of terminals and conductors
@@ -574,7 +482,7 @@ constructor TCapacitorObj.Create(ParClass: TDSSClass; const CapacitorName: Strin
Cmatrix := NIL;
- {Initialize these pointers to Nil so reallocmem will work reliably}
+ // Initialize these pointers to Nil so reallocmem will work reliably
FC := NIL;
FXL := NIL;
Fkvarrating := NIL;
@@ -595,7 +503,7 @@ constructor TCapacitorObj.Create(ParClass: TDSSClass; const CapacitorName: Strin
kvrating := 12.47;
InitDblArray(FNumSteps, FC, 1.0 / (TwoPi * BaseFrequency * SQR(kvrating) * 1000.0 / Fkvarrating^[1]));
- Connection := 0; // 0 or 1 for wye (default) or delta, respectively
+ Connection := TCapacitorConnection.Wye;
SpecType := 1; // 1=kvar, 2=Cuf, 3=Cmatrix
NormAmps := FkvarRating^[1] * SQRT3 / kvrating * 1.35; // 135%
@@ -610,11 +518,8 @@ constructor TCapacitorObj.Create(ParClass: TDSSClass; const CapacitorName: Strin
RecalcElementData;
NumTerm := 1;
-
- InitPropertyValues(0);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TCapacitorObj.Destroy;
begin
ReallocMem(Cmatrix, 0);
@@ -629,7 +534,6 @@ destructor TCapacitorObj.Destroy;
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TCapacitorObj.RecalcElementData;
var
KvarPerPhase, PhasekV, w: Double;
@@ -643,12 +547,9 @@ procedure TCapacitorObj.RecalcElementData;
1:
begin // kvar
-
case Connection of
- 1:
- begin // Line-to-Line
+ TCapacitorConnection.Delta:
PhasekV := kVRating;
- end;
else
begin // line-to-neutral
case Fnphases of
@@ -668,20 +569,20 @@ procedure TCapacitorObj.RecalcElementData;
2:
begin // Cuf
case Connection of
- 1:
- begin // Line-to-Line
+ TCapacitorConnection.Delta:
+ begin
PhasekV := kVRating;
end;
else
- begin // line-to-neutral
- case Fnphases of
- 2, 3:
- PhasekV := kVRating / SQRT3; // Assume three phase system
- else
- PhasekV := kVRating;
+ begin // line-to-neutral
+ case Fnphases of
+ 2, 3:
+ PhasekV := kVRating / SQRT3; // Assume three phase system
+ else
+ PhasekV := kVRating;
+ end;
end;
end;
- end;
for i := 1 to FNumSteps do
Ftotalkvar := Ftotalkvar + w * FC^[i] * SQR(PhasekV) / 1000.0;
end;
@@ -703,27 +604,18 @@ procedure TCapacitorObj.RecalcElementData;
FR^[i] := FXL^[i] / 1000.0;
end;
-
kvarPerPhase := Ftotalkvar / Fnphases;
NormAmps := kvarPerPhase / PhasekV * 1.35;
EmergAmps := NormAmps * 1.8 / 1.35;
-
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TCapacitorObj.CalcYPrim;
-
var
i: Integer;
YPrimTemp, YPrimWork: TCMatrix;
-
begin
-
-// Normally build only Yprim Shunt, but if there are 2 terminals and
-// Bus1 <> Bus 2
-
-
+ // Normally build only Yprim Shunt, but if there are 2 terminals and
+ // Bus1 <> Bus 2
if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Shunt = NIL) OR (Yprim_Series = NIL) {YPrimInvalid} then
begin // Reallocate YPrim if something has invalidated old allocation
if YPrim_Shunt <> NIL then
@@ -762,170 +654,87 @@ procedure TCapacitorObj.CalcYPrim;
// Set YPrim_Series based on diagonals of YPrim_shunt so that CalcVoltages doesn't fail
if IsShunt then
for i := 1 to Yorder do
- Yprim_Series.SetElement(i, i, CmulReal(Yprim_Shunt.Getelement(i, i), 1.0e-10));
+ Yprim_Series.SetElement(i, i, Yprim_Shunt.Getelement(i, i) * 1.0e-10);
Yprim.Copyfrom(YPrimTemp);
- {Don't Free YPrimTemp - It's just a pointer to an existing complex matrix}
+ // Don't Free YPrimTemp - It's just a pointer to an existing complex matrix
inherited CalcYPrim;
YprimInvalid := FALSE;
end;
-procedure TCapacitorObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
+procedure TCapacitorObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
- i, j: Integer;
-
+ i: Integer;
begin
inherited DumpProperties(F, Complete);
with ParentClass do
- begin
- FSWriteln(F, Format('~ %s=%s', [PropertyName^[1], firstbus]));
- FSWriteln(F, Format('~ %s=%s', [PropertyName^[2], nextbus]));
- FSWriteln(F, Format('~ %s=%d', [PropertyName^[3], Fnphases]));
- FSWriteln(F, Format('~ %s=%s', [PropertyName^[4], GetPropertyValue(4)]));
- FSWriteln(F, Format('~ %s=%.3f', [PropertyName^[5], '=', kVRating]));
- case Connection of
- 0:
- FSWriteln(F, '~ ', PropertyName^[6], '=wye');
- 1:
- FSWriteln(F, '~ ', PropertyName^[6], '=delta');
- end;
- if Cmatrix <> NIL then
- begin
- FSWrite(F, PropertyName^[7], '= (');
- for i := 1 to Fnphases do
- begin
- for j := 1 to i do
- FSWrite(F, Format('%.3f ', [(CMatrix^[(i - 1) * Fnphases + j] * 1.0e6)]));
- if i <> Fnphases then
- FSWrite(F, '|');
- end;
- FSWriteln(F, ')');
- end;
-
- FSWriteln(F, '~ ' + PropertyName^[8], '=', GetPropertyValue(8));
- FSWriteln(F, '~ ' + PropertyName^[9], '=', GetPropertyValue(9));
- FSWriteln(F, '~ ' + PropertyName^[10], '=', GetPropertyValue(10));
- FSWriteln(F, '~ ' + PropertyName^[11], '=', GetPropertyValue(11));
- FSWriteln(F, '~ ' + PropertyName^[12], '=', IntToStr(FNumSteps));
- FSWriteln(F, '~ ' + PropertyName^[13], '=', GetPropertyValue(13));
-
- for i := NumPropsthisClass + 1 to NumProperties do
+ for i := 1 to NumProperties do
begin
FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
end;
- if Complete then
- begin
- FSWriteln(F, 'SpecType=', IntToStr(SpecType));
- end;
+ if Complete then
+ begin
+ FSWriteln(F, 'SpecType=', IntToStr(SpecType));
end;
-
-end;
-
-
-procedure TCapacitorObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := GetBus(1);
- PropertyValue[2] := GetBus(2);
- PropertyValue[3] := '3';
- PropertyValue[4] := '1200';
- PropertyValue[5] := '12.47';
- PropertyValue[6] := 'wye';
- PropertyValue[7] := '';
- PropertyValue[8] := '';
- PropertyValue[9] := '0';
- PropertyValue[10] := '0';
- PropertyValue[11] := '0';
- PropertyValue[12] := '1';
- PropertyValue[13] := '1'; // states
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
- // Override Inherited properties
- // Override Inherited properties
- PropertyValue[NumPropsThisClass + 1] := Format('%g', [Normamps]);
- PropertyValue[NumPropsThisClass + 2] := Format('%g', [Emergamps]);
- PropertyValue[NumPropsThisClass + 3] := Str_Real(FaultRate, 0);
- PropertyValue[NumPropsThisClass + 4] := Str_Real(PctPerm, 0);
- PropertyValue[NumPropsThisClass + 5] := Str_Real(HrsToRepair, 0);
- ClearPropSeqArray;
end;
-procedure TCapacitorObj.MakePosSequence;
+procedure TCapacitorObj.MakePosSequence();
var
- S: String;
kvarperphase, phasekV, Cs, Cm: Double;
i, j: Integer;
-
+ newkvars: Array of Double;
begin
- {If FnPhases>1 Then -- do same for 1-phase, too}
- begin
-
- S := ' ';
- case SpecType of
-
- 1:
- begin // kvar
-
- if (FnPhases > 1) or (Connection <> 0) then
- PhasekV := kVRating / SQRT3
- else
- PhasekV := kVRating;
-
- S := 'Phases=1 ' + Format(' kV=%-.5g kvar=(', [PhasekV]);
-
- // 1-6-16 do caps Like load ...
- for i := 1 to FNumSteps do
- begin
- kvarPerPhase := FkvarRating^[i] / 3.0; // divide the total kvar equally among3 phases.../Fnphases;
- S := S + Format(' %-.5g', [kvarPerPhase]);
- end;
-
- S := S + ')';
-
- {Leave R as specified}
+ // If FnPhases>1 Then -- do same for 1-phase, too
+ case SpecType of
+ 1: // kvar
+ begin
+ if (FnPhases > 1) or (Connection <> TCapacitorConnection.Wye) then
+ PhasekV := kVRating / SQRT3
+ else
+ PhasekV := kVRating;
+ // 1-6-16 do caps Like load ...
+ SetLength(newkvars, FNumSteps);
+ for i := 1 to FNumSteps do
+ begin
+ kvarPerPhase := FkvarRating^[i] / 3.0; // divide the total kvar equally among3 phases.../Fnphases;
+ newkvars[i - 1] := kvarPerPhase;
end;
- 2:
- begin //
- S := 'Phases=1 ';
- end;
- 3:
- if FnPhases > 1 then
- begin // C Matrix
- S := 'Phases=1 ';
- // R1
- Cs := 0.0; // Avg Self
- for i := 1 to FnPhases do
- Cs := Cs + Cmatrix^[(i - 1) * Fnphases + i];
- Cs := Cs / FnPhases;
-
- Cm := 0.0; //Avg mutual
- for i := 2 to FnPhases do
- for j := i to FnPhases do
- Cm := Cm + Cmatrix^[(i - 1) * Fnphases + j];
- Cm := Cm / (FnPhases * (Fnphases - 1.0) / 2.0);
-
- S := S + Format(' Cuf=%-.5g', [(Cs - Cm)]);
-
- end;
+ BeginEdit(True);
+ SetInteger(ord(TProp.Phases), 1);
+ SetDouble(ord(TProp.kV), PhasekV);
+ SetDoubles(ord(TProp.kvar), newkvars);
+ EndEdit(3);
+ // Leave R as specified
end;
-
- Parser.CmdString := S;
- Edit;
-
+ 2: //
+ SetInteger(ord(TProp.Phases), 1);
+ 3:
+ if FnPhases > 1 then
+ begin // C Matrix
+ Cs := 0.0; // Avg Self
+ for i := 1 to FnPhases do
+ Cs := Cs + Cmatrix^[(i - 1) * Fnphases + i];
+ Cs := Cs / FnPhases;
+
+ Cm := 0.0; //Avg mutual
+ for i := 2 to FnPhases do
+ for j := i to FnPhases do
+ Cm := Cm + Cmatrix^[(i - 1) * Fnphases + j];
+ Cm := Cm / (FnPhases * (Fnphases - 1.0) / 2.0);
+ BeginEdit(True);
+ SetInteger(ord(TProp.Phases), 1);
+ SetDouble(ord(TProp.Cuf), (Cs - Cm));
+ EndEdit(2);
+ end;
end;
-
inherited;
-
end;
@@ -957,101 +766,15 @@ procedure TCapacitorObj.set_States(Idx: Integer; const Value: Integer);
end;
procedure TCapacitorObj.set_NumSteps(const Value: Integer);
-
-{
- Special case for changing from 1 to more .. Automatically make a new bank
-}
-
+// Special case for changing from 1 to more .. Automatically make a new bank
var
- StepSize, RStep, XLstep: Double;
- i: Integer;
-begin
- {reallocate all arrays associated with steps }
-
- if (FNumSteps <> Value) and (Value > 0) then
- begin
- Rstep := 0.0;
- XLstep := 0.0;
- if FNumSteps = 1 then
- begin
- {Save total values to be divided up}
- FTotalkvar := Fkvarrating^[1];
- Rstep := FR^[1] * Value;
- XLstep := FXL^[1] * Value;
- end;
-
- // Reallocate arrays (Must be initialized to nil for first call)
- Reallocmem(FC, Sizeof(FC^[1]) * Value);
- Reallocmem(FXL, Sizeof(FXL^[1]) * Value);
- Reallocmem(Fkvarrating, Sizeof(Fkvarrating^[1]) * Value);
- Reallocmem(FR, Sizeof(FR^[1]) * Value);
- Reallocmem(FHarm, Sizeof(FHarm^[1]) * Value);
- Reallocmem(FStates, Sizeof(FStates^[1]) * Value);
-
- // Special case for FNumSteps=1
-
- if FNumSteps = 1 then
- begin
- case SpecType of
-
- 1:
- begin // kvar {We'll make a multi-step bank of same net size as at present}
- StepSize := FTotalkvar / Value;
- for i := 1 to Value do
- FkvarRating^[i] := StepSize;
- end;
-
- 2:
- begin // Cuf {We'll make a multi-step bank with all the same as first}
- for i := 2 to Value do
- FC^[i] := FC^[1]; // Make same as first step
- end;
-
- 3:
- begin // Cmatrix {We'll make a multi-step bank with all the same as first}
- // Nothing to do since all will be the same
- end;
-
- end;
-
- case SpecType of
-
- 1:
- begin
- for i := 1 to Value do
- FR^[i] := Rstep;
- for i := 1 to Value do
- FXL^[i] := XLstep;
- end;
-
- 2, 3:
- begin // Make R and XL same as first step
- for i := 2 to Value do
- FR^[i] := FR^[1];
- for i := 2 to Value do
- FXL^[i] := FXL^[1];
- end;
-
- end;
-
- for i := 1 to Value do
- Fstates^[i] := 1; // turn 'em all ON
- LastStepInService := Value;
- for i := 2 to Value do
- FHarm^[i] := FHarm^[1]; // tune 'em all the same as first
-
- end;
-
- end;
-
- FNumSteps := Value;
-end;
-
-procedure TCapacitorObj.ProcessHarmonicSpec(const Param: String);
+ prev: Integer;
begin
- FNumsteps := InterpretDblArray(Param, FNumsteps, FHarm);
+ if (Value <= 0) or (FNumSteps = Value) then Exit;
- DoHarmonicRecalc := TRUE;
+ prev := FNumSteps;
+ FNumSteps := value;
+ PropertySideEffects(ord(TProp.numsteps), prev);
end;
procedure TCapacitorObj.FindLastStepInService;
@@ -1076,7 +799,6 @@ procedure TCapacitorObj.set_LastStepInService(const Value: Integer);
var
i: Integer;
begin
-
for i := 1 to Value do
FStates^[i] := 1;
// set remainder steps, if any, to 0
@@ -1099,29 +821,17 @@ procedure TCapacitorObj.set_LastStepInService(const Value: Integer);
FLastStepInService := Value;
end;
-procedure TCapacitorObj.ProcessStatesSpec(const Param: String);
-
-begin
- FNumsteps := InterpretIntArray(Param, FNumsteps, FStates);
- FindLastStepInService;
-end;
-
procedure TCapacitorObj.MakeYprimWork(YprimWork: TcMatrix; iStep: Integer);
-
-{ call this routine only if step is energized}
-
+// call this routine only if step is energized
var
Value, Value2,
ZL: Complex;
i, j, ioffset: Integer;
w, FreqMultiple: Double;
HasZL: Boolean;
-
begin
-
with YprimWork do
begin
-
FYprimFreq := ActiveCircuit.Solution.Frequency;
FreqMultiple := FYprimFreq / BaseFrequency;
w := TwoPi * FYprimFreq;
@@ -1136,19 +846,18 @@ procedure TCapacitorObj.MakeYprimWork(YprimWork: TcMatrix; iStep: Integer);
ZL := Cmplx(FR^[iSTep], FXL^[iSTep] * FreqMultiple);
end;
- { Now, Put C into in Yprim matrix }
+ // Now, Put C into in Yprim matrix
case SpecType of
1, 2:
begin
-
Value := Cmplx(0.0, FC^[iSTep] * w);
case Connection of
- 1:
+ TCapacitorConnection.Delta:
begin // Line-Line
- Value2 := CmulReal(Value, 2.0);
- Value := cnegate(Value);
+ Value2 := Value * 2.0;
+ Value := -Value;
for i := 1 to Fnphases do
begin
SetElement(i, i, Value2);
@@ -1160,8 +869,8 @@ procedure TCapacitorObj.MakeYprimWork(YprimWork: TcMatrix; iStep: Integer);
else
begin // Wye
if HasZL then
- Value := Cinv(Cadd(ZL, Cinv(Value))); // add in ZL
- Value2 := cnegate(Value);
+ Value := Cinv(ZL + Cinv(Value)); // add in ZL
+ Value2 := -Value;
for i := 1 to Fnphases do
begin
SetElement(i, i, Value); // Elements are only on the diagonals
@@ -1181,36 +890,36 @@ procedure TCapacitorObj.MakeYprimWork(YprimWork: TcMatrix; iStep: Integer);
Value := Cmplx(0.0, Cmatrix^[(iOffset + j)] * w);
SetElement(i, j, Value);
SetElement(i + Fnphases, j + Fnphases, Value);
- Value := cnegate(Value);
+ Value := -Value;
SetElemSym(i, j + Fnphases, Value);
end;
end;
end;
end;
- {Add line reactance for filter reactor, if any}
+ // Add line reactance for filter reactor, if any
if HasZL then
case SpecType of
1, 2:
case Connection of
- 1: {Line-Line}
+ TCapacitorConnection.Delta: // Line-Line
begin
- {Add a little bit to each phase so it will invert}
+ // Add a little bit to each phase so it will invert
for i := 1 to Fnphases do
begin
- SetElement(i, i, CmulReal(GetElement(i, i), 1.000001));
+ SetElement(i, i, GetElement(i, i) * 1.000001);
end;
Invert;
for i := 1 to Fnphases do
begin
- Value := Cadd(ZL, GetElement(i, i));
+ Value := ZL + GetElement(i, i);
SetElement(i, i, Value);
end;
Invert;
end;
- else {WYE - just put ZL in series}
- {DO Nothing; Already in - see above}
+ else // WYE - just put ZL in series
+ // DO Nothing; Already in - see above
end;
3:
@@ -1218,65 +927,19 @@ procedure TCapacitorObj.MakeYprimWork(YprimWork: TcMatrix; iStep: Integer);
Invert;
for i := 1 to Fnphases do
begin
- Value := Cadd(ZL, GetElement(i, i));
+ Value := ZL + GetElement(i, i);
SetElement(i, i, Value);
end;
Invert;
end;
end;
- end; {With YPRIM}
-
-
-end;
-
-function TCapacitorObj.GetPropertyValue(Index: Integer): String;
-
-var
- i: Integer;
- FTemp: pDoubleArray;
-begin
-
- Result := '';
- case Index of // Special cases
- 1:
- Result := GetBus(1);
- 2:
- Result := GetBus(2);
- 4:
- Result := GetDSSArray_Real(FNumSteps, Fkvarrating);
- 5:
- Result := Format('%-g', [kvRating]);
- 8:
- begin
- FTemp := Allocmem(SizeOf(Double) * FNumSteps);
- for i := 1 to FNumSteps do
- FTemp^[i] := FC^[i] * 1.0e6; // To microfarads
- Result := GetDSSArray_Real(FNumSteps, FTemp);
- Reallocmem(FTemp, 0); // throw away temp storage
- end;
- 9:
- Result := GetDSSArray_Real(FNumSteps, FR);
- 10:
- Result := GetDSSArray_Real(FNumSteps, FXL);
- 11:
- Result := GetDSSArray_Real(FNumSteps, Fharm);
- 13:
- Result := GetDSSArray_Integer(FNumSteps, FStates);
- 14:
- Result := Format('%g', [Normamps]);
- 15:
- Result := Format('%g', [Emergamps]);
- else
- Result := inherited GetPropertyValue(index);
end;
-
end;
function TCapacitorObj.AddStep: Boolean;
begin
- // Start with last step in service and see if we can add more. If not return FALSE
-
+ // Start with last step in service and see if we can add more. If not return FALSE
if LastStepInService = FNumSteps then
Result := FALSE
else
@@ -1300,7 +963,6 @@ function TCapacitorObj.SubtractStep: Boolean;
else
Result := TRUE; // signify bank OPEN
end;
-
end;
function TCapacitorObj.AvailableSteps: Integer;
@@ -1365,4 +1027,4 @@ procedure TCapacitorObj.Set_ConductorClosed(Index: Integer; Value: Boolean);
{$ENDIF}
-end.
+end.
\ No newline at end of file
diff --git a/src/PDElements/Fault.pas b/src/PDElements/Fault.pas
index 545325401..6b80e9cdd 100644
--- a/src/PDElements/Fault.pas
+++ b/src/PDElements/Fault.pas
@@ -7,37 +7,26 @@
----------------------------------------------------------
}
-{
- 3-1-00 Restored old Dump
- Removed 1.e6 multiplier (where did this come from???)
- 9-??-00 Added Temporary fault logic
- 9-22-00 Revised Is_ON logic
- 7-2-01 Corrected default bus2 phase designation
- Force rebuilding of bus lists if num phases changed
-}
-
-{
- Fault object:
-
- One or more faults can be placed across any two buses in the circuit.
- Like the capacitor, the second bus defaults to the ground node of the
- same bus that bus1 is connected to.
-
- The fault is basically an uncoupled, multiphase resistance branch. however,
- you may also specify it as NODAL CONDUCTANCE (G) matrix, which will give you
- complete control of a complex fault situation.
-
- To eliminate a fault from the system after it has been defined, disable it.
-
- In Monte Carlo Fault mode, the fault resistance is varied by the % std dev specified
- If %Stddev is specified as zero (default), the resistance is varied uniformly.
-
- Fault may have its "ON" time specified (defaults to 0). When Time (t) exceeds this value, the
- fault will be enabled. Else it is disabled.
-
- Fault may be designated as Temporary. That is, after it is enabled, it will disable itself
- if the fault current drops below the MinAmps value.
-}
+// Fault object:
+//
+// One or more faults can be placed across any two buses in the circuit.
+// Like the capacitor, the second bus defaults to the ground node of the
+// same bus that bus1 is connected to.
+//
+// The fault is basically an uncoupled, multiphase resistance branch. however,
+// you may also specify it as NODAL CONDUCTANCE (G) matrix, which will give you
+// complete control of a complex fault situation.
+//
+// To eliminate a fault from the system after it has been defined, disable it.
+//
+// In Monte Carlo Fault mode, the fault resistance is varied by the % std dev specified
+// If %Stddev is specified as zero (default), the resistance is varied uniformly.
+//
+// Fault may have its "ON" time specified (defaults to 0). When Time (t) exceeds this value, the
+// fault will be enabled. Else it is disabled.
+//
+// Fault may be designated as Temporary. That is, after it is enabled, it will disable itself
+// if the fault current drops below the MinAmps value.
interface
@@ -52,28 +41,36 @@ interface
ArrayDef;
type
+{$SCOPEDENUMS ON}
+ TFaultProp = (
+ INVALID = 0,
+ bus1 = 1,
+ bus2 = 2,
+ phases = 3,
+ r = 4,
+ pctstddev = 5,
+ Gmatrix = 6,
+ ONtime = 7,
+ temporary = 8,
+ MinAmps = 9
+ );
+{$SCOPEDENUMS OFF}
TFault = class(TPDClass)
- PRIVATE
- procedure DoGmatrix;
-
- procedure FltSetBus1(const s: String);
PROTECTED
- procedure DefineProperties;
- function MakeLike(const FaultName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ function BeginEdit(ptr: Pointer; SetActive: Boolean=True): Pointer; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TFaultObj = class(TPDElement)
PRIVATE
MinAmps: Double;
- IsTemporary,
+ IsTemporary: LongBool;
Cleared,
Is_ON: Boolean;
Bus2Defined: Boolean;
@@ -81,7 +78,7 @@ TFaultObj = class(TPDElement)
RandomMult: Double;
function FaultStillGoing: Boolean;
PROTECTED
- G: Double; // single G per phase (line rating) if Gmatrix not specified
+ G: Double; // single G per phase (line rating) if Gmatrix not specified
Gmatrix: pDoubleArray; // If not nil then overrides G
Stddev: Double; // per unit stddev
@@ -90,6 +87,8 @@ TFaultObj = class(TPDElement)
PUBLIC
constructor Create(ParClass: TDSSClass; const FaultName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
@@ -98,354 +97,211 @@ TFaultObj = class(TPDElement)
procedure CheckStatus(ControlMode: Integer);
procedure Reset;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
-
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
end;
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
dynamics,
Sysutils,
- Ucomplex,
+ UComplex, DSSUcomplex,
MathUtil,
Utilities,
DSSHelper,
DSSObjectHelper,
TypInfo;
+type
+ TObj = TFaultObj;
+ TProp = TFaultProp;
const
- NumPropsthisclass = 9;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TFault.Create(dssContext: TDSSContext); // Creates superstructure for all Fault objects
+constructor TFault.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'Fault';
- DSSClassType := FAULTOBJECT + NON_PCPD_ELEM; // Only in Fault object class
-
- ActiveElement := 0;
-
- DefineProperties;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, FAULTOBJECT or NON_PCPD_ELEM, 'Fault');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TFault.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TFault.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
-
- PropertyName^[1] := 'bus1';
- PropertyName^[2] := 'bus2';
- PropertyName^[3] := 'phases';
- PropertyName^[4] := 'r';
- PropertyName^[5] := '%stddev';
- PropertyName^[6] := 'Gmatrix';
- PropertyName^[7] := 'ONtime';
- PropertyName^[8] := 'temporary';
- PropertyName^[9] := 'MinAmps';
-
- // define Property help values
- PropertyHelp[1] := 'Name of first bus. Examples:' + CRLF + CRLF +
- 'bus1=busname' + CRLF +
- 'bus1=busname.1.2.3' + CRLF + CRLF +
- 'Bus2 automatically defaults to busname.0,0,0 unless it was previously defined. ';
- PropertyHelp[2] := 'Name of 2nd bus of the 2-terminal Fault object. Defaults to all phases connected ' +
- 'to first bus, node 0, if not specified. (Shunt Wye Connection to ground reference)' + CRLF + CRLF +
- 'That is, the Fault defaults to a ground fault unless otherwise specified.';
- PropertyHelp[3] := 'Number of Phases. Default is 1.';
- PropertyHelp[4] := 'Resistance, each phase, ohms. Default is 0.0001. Assumed to be Mean value if gaussian random mode.' +
- 'Max value if uniform mode. A Fault is actually a series resistance ' +
- 'that defaults to a wye connection to ground on the second terminal. You ' +
- 'may reconnect the 2nd terminal to achieve whatever connection. Use ' +
- 'the Gmatrix property to specify an arbitrary conductance matrix.';
- PropertyHelp[5] := 'Percent standard deviation in resistance to assume for Monte Carlo fault (MF) solution mode for GAUSSIAN distribution. Default is 0 (no variation from mean).';
- PropertyHelp[6] := 'Use this to specify a nodal conductance (G) matrix to represent some arbitrary resistance network. ' +
- 'Specify in lower triangle form as usual for DSS matrices.';
- PropertyHelp[7] := 'Time (sec) at which the fault is established for time varying simulations. Default is 0.0 ' +
- '(on at the beginning of the simulation)';
- PropertyHelp[8] := '{Yes | No} Default is No. Designate whether the fault is temporary. For Time-varying simulations, ' +
- 'the fault will be removed if the current through the fault drops below the MINAMPS criteria.';
- PropertyHelp[9] := 'Minimum amps that can sustain a temporary fault. Default is 5.';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+ // real matrix
+ PropertyType[ord(TProp.Gmatrix)] := TPropertyType.DoubleSymMatrixProperty;
+ PropertyOffset[ord(TProp.Gmatrix)] := ptruint(@obj.Gmatrix);
+ PropertyOffset2[ord(TProp.Gmatrix)] := ptruint(@obj.Fnphases);
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
-end;
+ // integer properties
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TFault.NewObject(const ObjName: String): Integer;
-begin
- // create a new object of this class and add to list
- with ActiveCircuit do
- begin
- ActiveCktElement := TFaultObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyType[ord(TProp.bus2)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+ PropertyOffset[ord(TProp.bus2)] := 2;
-end;
+ // boolean properties
+ PropertyType[ord(TProp.temporary)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.temporary)] := ptruint(@obj.IsTemporary);
+ // percent properties
+ PropertyScale[ord(TProp.pctstddev)] := 0.01;
+ PropertyOffset[ord(TProp.pctstddev)] := ptruint(@obj.StdDev);
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TFault.DoGmatrix;
-var
- OrderFound, j: Integer;
- MatBuffer: pDoubleArray;
+ // double properties (default type)
+ PropertyOffset[ord(TProp.ONtime)] := ptruint(@obj.ON_Time);
+ PropertyOffset[ord(TProp.MinAmps)] := ptruint(@obj.MinAmps);
-begin
- with DSS.ActiveFaultObj do
- begin
- MatBuffer := Allocmem(Sizeof(Double) * Fnphases * Fnphases);
- OrderFound := Parser.ParseAsSymMatrix(Fnphases, MatBuffer);
-
- if OrderFound > 0 then // Parse was successful
- begin {X}
- Reallocmem(Gmatrix, Sizeof(Gmatrix^[1]) * Fnphases * Fnphases);
- for j := 1 to Fnphases * Fnphases do
- Gmatrix^[j] := MatBuffer^[j];
- end;
+ PropertyOffset[ord(TProp.r)] := ptruint(@obj.G);
+ PropertyInverse[ord(TProp.r)] := True;
- Freemem(MatBuffer, Sizeof(Double) * Fnphases * Fnphases);
- end;
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TFault.FltSetBus1(const s: String);
-
+function TFault.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- s2: String;
- dotpos: Integer;
-
- // Special handling for Bus 1
- // Set Bus2 = Bus1.0.0.0
-
+ Obj: TObj;
begin
- with DSS.ActiveFaultObj do
- begin
-
- SetBus(1, S);
-
- // Default Bus2 to zero node of Bus1 unless previously defined explicitly. (Wye Grounded connection)
-
- if not Bus2Defined then
- begin
- // Strip node designations from S
- dotpos := Pos('.', S);
- if dotpos > 0 then
- S2 := Copy(S, 1, dotpos - 1) // copy up to Dot
- else
- S2 := Copy(S, 1, Length(S));
-
- S2 := S2 + '.0.0.0'; // Set Default for up to 3 phases
-
- SetBus(2, S2);
- IsShunt := TRUE;
- end;
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TFault.Edit: Integer;
-
+procedure TFaultObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
- PhasesTemp: Integer;
-
+ dotpos: Integer;
+ S, S2: string;
begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveFaultObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveFaultObj; // use property to set this value
+ case Idx of
+ ord(TProp.phases):
+ if Fnphases <> previousIntVal then
+ begin
+ NConds := Fnphases; // Force Reallocation of terminal info
+ ActiveCircuit.BusNameRedefined := TRUE; // Set Global Flag to signal circuit to rebuild busdefs
+ end;
+ ord(TProp.bus1):
+ if not Bus2Defined then
+ begin
+ // Default Bus2 to zero node of Bus1 unless previously defined explicitly. (Wye Grounded connection)
- with DSS.ActiveFaultObj do
- begin
+ // Strip node designations from S
+ S := GetBus(1);
+ dotpos := Pos('.', S);
+ if dotpos > 0 then
+ S2 := Copy(S, 1, dotpos - 1) // copy up to Dot
+ else
+ S2 := Copy(S, 1, Length(S));
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 350);
- 1:
- FltSetbus1(param);
- 2:
- Setbus(2, param);
- 3: ;{Numphases := Parser.IntValue;} // see below
- 4:
- begin
- G := Parser.Dblvalue;
- if G <> 0.0 then
- G := 1.0 / G
- else
- G := 10000.0; // Default to a low resistance
- end;
- 5:
- StdDev := Parser.Dblvalue * 0.01;
- 6:
- DoGmatrix;
- 7:
- ON_Time := Parser.Dblvalue;
- 8:
- IsTemporary := InterpretYesNo(Param);
- 9:
- MinAmps := Parser.DblValue;
- else
- // Inherited
- ClassEdit(DSS.ActiveFaultObj, ParamPointer - NumPropsThisClass)
- end;
+ S2 := S2 + '.0.0.0'; // Set Default for up to 3 phases
- // Some specials ...
- case ParamPointer of
- 1:
- PropertyValue[2] := GetBus(2); // Bus2 gets modified if bus1 is
- 2:
- if CompareText(StripExtension(GetBus(1)), StripExtension(GetBus(2))) <> 0 then
- begin
- IsShunt := FALSE;
- Bus2Defined := TRUE;
- end;
- 3:
- begin
- PhasesTemp := Parser.IntValue;
- if Fnphases <> PhasesTemp then
- begin
- Nphases := PhasesTemp;
- NConds := Fnphases; // Force Reallocation of terminal info
- ActiveCircuit.BusNameRedefined := TRUE; // Set Global Flag to signal circuit to rebuild busdefs
- end;
- end;
- 4:
- SpecType := 1;
- 6:
- SpecType := 2;
- 7:
- if ON_Time > 0.0 then
- Is_ON := FALSE; // Assume fault will be on later
- else
+ SetBus(2, S2);
+ IsShunt := TRUE;
+ SetAsNextSeq(ord(TProp.bus2));
end;
-
- //YPrim invalidation on anything that changes impedance values
- case ParamPointer of
- 3, 4, 6:
- YprimInvalid := TRUE;
- else
+ ord(TProp.bus2):
+ if AnsiCompareText(StripExtension(GetBus(1)), StripExtension(GetBus(2))) <> 0 then
+ begin
+ IsShunt := FALSE;
+ Bus2Defined := TRUE;
end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ ord(TProp.r):
+ begin
+ SpecType := 1;
+ if G = 0 then
+ G := 10000.0; // Default to a low resistance
end;
+ ord(TProp.Gmatrix):
+ SpecType := 2;
+ ord(TProp.ONtime):
+ if ON_Time > 0.0 then
+ Is_ON := FALSE; // Assume fault will be on later
+ end;
- RecalcElementData;
+ //YPrim invalidation on anything that changes impedance values
+ case Idx of
+ 3, 4, 6:
+ YprimInvalid := TRUE;
end;
+ inherited PropertySideEffects(Idx, previousIntVal);
+end;
+function TFault.BeginEdit(ptr: Pointer; SetActive: Boolean): Pointer;
+var
+ Obj: TObj;
+begin
+ Obj := TObj(inherited BeginEdit(ptr, SetActive));
+ if SetActive then
+ DSS.ActiveFaultObj := Obj;
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TFault.MakeLike(const FaultName: String): Integer;
+procedure TFaultObj.MakeLike(OtherPtr: Pointer);
var
- OtherFault: TFaultObj;
+ Other: TObj;
i: Integer;
begin
- Result := 0;
- {See if we can find this Fault name in the present collection}
- OtherFault := Find(FaultName);
- if OtherFault <> NIL then
- with DSS.ActiveFaultObj do
- begin
-
- if Fnphases <> OtherFault.Fnphases then
- begin
- Fnphases := OtherFault.Fnphases;
- NConds := Fnphases; // force reallocation of terminals and conductors
-
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
-
- end;
-
- BaseFrequency := OtherFault.BaseFrequency;
- G := OtherFault.G;
- SpecType := OtherFault.SpecType;
-
- MinAmps := OtherFault.MinAmps;
- IsTemporary := OtherFault.IsTemporary;
- Cleared := OtherFault.Cleared;
- Is_ON := OtherFault.Is_ON;
- On_Time := OtherFault.On_Time;
-
-
- if OtherFault.Gmatrix = NIL then
- Reallocmem(Gmatrix, 0)
- else
- begin
- Reallocmem(Gmatrix, SizeOf(Gmatrix^[1]) * Fnphases * Fnphases);
- for i := 1 to Fnphases * Fnphases do
- Gmatrix^[i] := OtherFault.Gmatrix^[i];
- end;
-
-
- ClassMakeLike(OtherFault);
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ if Fnphases <> Other.Fnphases then
+ begin
+ Fnphases := Other.Fnphases;
+ NConds := Fnphases; // force reallocation of terminals and conductors
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherFault.PropertyValue[i];
- Result := 1;
- end
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
+ end;
+ BaseFrequency := Other.BaseFrequency;
+ G := Other.G;
+ SpecType := Other.SpecType;
+
+ MinAmps := Other.MinAmps;
+ IsTemporary := Other.IsTemporary;
+ Cleared := Other.Cleared;
+ Is_ON := Other.Is_ON;
+ On_Time := Other.On_Time;
+
+ if Other.Gmatrix = NIL then
+ Reallocmem(Gmatrix, 0)
else
- DoSimpleMsg('Error in Fault MakeLike: "' + FaultName + '" Not Found.', 351);
-
-
+ begin
+ Reallocmem(Gmatrix, SizeOf(Gmatrix^[1]) * Fnphases * Fnphases);
+ for i := 1 to Fnphases * Fnphases do
+ Gmatrix^[i] := Other.Gmatrix^[i];
+ end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TFault Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
constructor TFaultObj.Create(ParClass: TDSSClass; const FaultName: String);
-
begin
inherited Create(ParClass);
DSSObjType := ParClass.DSSClassType; //FAULTOBJECT + NON_PCPD_ELEM; // Only in Fault object class
- Name := LowerCase(FaultName);
+ Name := AnsiLowerCase(FaultName);
// Default to SLG fault
- NPhases := 1; // Directly set conds and phases
+ FNPhases := 1; // Directly set conds and phases
Fnconds := 1;
Nterms := 2; // Force allocation of terminals and conductors
@@ -463,7 +319,6 @@ constructor TFaultObj.Create(ParClass: TDSSClass; const FaultName: String);
Is_ON := TRUE;
On_Time := 0.0; // Always enabled at the start of a solution.
-
RandomMult := 1;
NormAmps := 0.0;
@@ -472,39 +327,23 @@ constructor TFaultObj.Create(ParClass: TDSSClass; const FaultName: String);
PctPerm := 100.0;
HrsToRepair := 0.0;
- InitPropertyValues(0);
-
-
Yorder := Fnterms * Fnconds;
RecalcElementData;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TFaultObj.Destroy;
begin
ReallocMem(Gmatrix, 0);
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TFaultObj.RecalcElementData;
-
begin
-
// Nothing to do
-
end;
-// function Cube(const X: Double): Double;
-// begin
-// Result := X * X * X;
-// end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TFaultObj.Randomize;
-
// called from solveMontefault Procedure
-
begin
with activeCircuit.Solution do
begin
@@ -526,10 +365,7 @@ procedure TFaultObj.Randomize;
YPrimInvalid := TRUE; // force rebuilding of matrix
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TFaultObj.CalcYPrim;
-
var
Value, Value2: Complex;
i,
@@ -537,9 +373,7 @@ procedure TFaultObj.CalcYPrim;
ioffset: Integer;
YPrimTemp: TCMatrix;
-
begin
-
if YPrimInvalid then
begin // Reallocate YPrim if something has invalidated old allocation
if YPrim_Series <> NIL then
@@ -559,13 +393,12 @@ procedure TFaultObj.CalcYPrim;
Yprim.Clear;
end;
-
if IsShunt then
YPrimTemp := YPrim_Shunt
else
YPrimTemp := Yprim_Series;
- // make sure randommult is 1.0 if not solution mode MonteFault
+ // make sure randommult is 1.0 if not solution mode MonteFault
if ActiveCircuit.Solution.Mode <> TSolveMode.MONTEFAULT then
RandomMult := 1.0;
@@ -575,21 +408,17 @@ procedure TFaultObj.CalcYPrim;
with YPrimTemp do
begin
-
- { Now, Put in Yprim matrix }
-
- {If the fault is not ON, the set zero conductance}
-
+ // Now, Put in Yprim matrix
+ // If the fault is not ON, the set zero conductance
case SpecType of
1:
begin
-
if Is_ON then
Value := Cmplx(G / RandomMult, 0.0)
else
Value := CZERO;
- Value2 := cnegate(Value);
+ Value2 := -Value;
for i := 1 to Fnphases do
begin
SetElement(i, i, Value); // Elements are only on the diagonals
@@ -610,14 +439,14 @@ procedure TFaultObj.CalcYPrim;
Value := CZERO;
SetElement(i, j, Value);
SetElement(i + Fnphases, j + Fnphases, Value);
- Value := cnegate(Value);
+ Value := -Value;
SetElemSym(i, j + Fnphases, Value);
end;
end;
end;
end;
- end; {With YPRIM}
+ end;
YPrim.CopyFrom(YPrimTemp);
@@ -625,15 +454,12 @@ procedure TFaultObj.CalcYPrim;
YprimInvalid := FALSE;
end;
-procedure TFaultObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
+procedure TFaultObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i, j: Integer;
-
begin
inherited DumpProperties(F, complete);
-
with ParentClass do
begin
FSWriteln(F, '~ ' + PropertyName^[1] + '=' + firstbus);
@@ -661,7 +487,6 @@ procedure TFaultObj.DumpProperties(F: TFileStream; Complete: Boolean);
FSWriteln(F, '~ ' + PropertyName^[8] + '= No');
FSWriteln(F, Format('~ %s=%.1f', [PropertyName^[9], Minamps]));
-
for i := NumPropsthisClass to NumProperties do
begin
FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
@@ -672,15 +497,17 @@ procedure TFaultObj.DumpProperties(F: TFileStream; Complete: Boolean);
FSWriteln(F, Format('// SpecType=%d', [SpecType]));
end;
end;
-
end;
+function PresentTimeInSec(DSS: TDSSContext): Double;
+begin
+ with DSS.ActiveCircuit.Solution do
+ Result := Dynavars.t + DynaVars.intHour * 3600.0;
+end;
procedure TFaultObj.CheckStatus(ControlMode: Integer);
begin
-
case ControlMode of
-
CTRLSTATIC: {Leave it however it is defined by other processes}
begin
end;
@@ -710,14 +537,12 @@ procedure TFaultObj.CheckStatus(ControlMode: Integer);
end;
end;
end;
-
end;
function TFaultObj.FaultStillGoing: Boolean;
var
i: Integer;
begin
-
ComputeIterminal;
Result := FALSE;
for i := 1 to FNphases do
@@ -728,7 +553,6 @@ function TFaultObj.FaultStillGoing: Boolean;
Exit;
end;
end;
-
end;
procedure TFaultObj.Reset;
@@ -736,69 +560,11 @@ procedure TFaultObj.Reset;
Cleared := FALSE;
end;
-procedure TFaultObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := getbus(1);
- PropertyValue[2] := getbus(2);
- PropertyValue[3] := '1';
- PropertyValue[4] := '0.0001';
- PropertyValue[5] := '0';
- PropertyValue[6] := '';
- PropertyValue[7] := '0.0';
- PropertyValue[8] := 'no';
- PropertyValue[9] := '5.0';
-
- inherited InitPropertyValues(NumPropsThisClass);
-
- // Override Inherited Properties
- PropertyValue[NumPropsThisClass + 1] := '0'; //Normamps
- PropertyValue[NumPropsThisClass + 2] := '0'; //emergamps
- PropertyValue[NumPropsThisClass + 3] := '0'; //Fault rate
- PropertyValue[NumPropsThisClass + 4] := '0'; // Pct Perm
- PropertyValue[NumPropsThisClass + 5] := '0'; // Hrs to repair
-
-
-end;
-
-function TFaultObj.GetPropertyValue(Index: Integer): String;
-var
- i, j: Integer;
-begin
- case Index of
-
- 6:
- begin
- Result := '(';
- if Assigned(Gmatrix) then
- for i := 1 to Fnphases do // G matrix
- begin
- for j := 1 to i do
- begin
- Result := Result + Format('%-g', [Gmatrix^[(i - 1) * Fnphases + j]]) + ' ';
- end;
- if i < Fnphases then
- Result := Result + '|';
- end;
-
- Result := Result + ')';
- end;
- else
- Result := inherited GetPropertyValue(Index);
- end;
-end;
-
-procedure TFaultObj.MakePosSequence;
-
-
+procedure TFaultObj.MakePosSequence();
begin
- if FnPhases <> 1 then
- begin
- Parser.CmdString := 'Phases=1';
- Edit;
- end;
+ if Fnphases > 1 then
+ SetInteger(ord(TProp.Phases), 1);
inherited;
-
end;
-end.
+end.
\ No newline at end of file
diff --git a/src/PDElements/GICTransformer.pas b/src/PDElements/GICTransformer.pas
index f85250ea9..c9cf58c3b 100644
--- a/src/PDElements/GICTransformer.pas
+++ b/src/PDElements/GICTransformer.pas
@@ -7,14 +7,7 @@
----------------------------------------------------------
}
-{
- 6-21-2011 Created from Fault Object
-}
-
-{
-
- Special resistance-only model of transformers for geomagnetically-induced current (GIC) studies
-}
+// Special resistance-only model of transformers for geomagnetically-induced current (GIC) studies
interface
@@ -30,22 +23,35 @@ interface
XYCurve;
type
+{$SCOPEDENUMS ON}
+ TGICTransformerProp = (
+ INVALID = 0,
+ BusH=1,
+ BusNH=2,
+ BusX=3,
+ BusNX=4,
+ phases=5,
+ Typ=6,
+ R1=7,
+ R2=8,
+ KVLL1=9,
+ KVLL2=10,
+ MVA=11,
+ VarCurve=12,
+ pctR1=13,
+ pctR2=14,
+ K=15
+ );
+{$SCOPEDENUMS OFF}
TGICTransformer = class(TPDClass)
- PRIVATE
-
- procedure GICTransSetBusH(const s: String);
- procedure GICTransSetBusX(const s: String);
PROTECTED
- procedure DefineProperties;
- function MakeLike(const GICTransName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TGICTransformerObj = class(TPDElement)
@@ -54,7 +60,6 @@ TGICTransformerObj = class(TPDElement)
SpecType: Integer;
FMVARating: Double;
- FVarCurve: String;
FVarCurveObj: TXYCurveObj;
FpctR1,
FpctR2: Double;
@@ -70,15 +75,13 @@ TGICTransformerObj = class(TPDElement)
PUBLIC
constructor Create(ParClass: TDSSClass; const FaultName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
-
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
procedure WriteVarOutputRecord(F: TFileStream); // Add a record to the ouput file based on present GIC
end;
@@ -86,413 +89,254 @@ TGICTransformerObj = class(TPDElement)
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
- dynamics,
Sysutils,
- Ucomplex,
+ UComplex, DSSUcomplex,
MathUtil,
Utilities,
DSSHelper,
DSSObjectHelper,
TypInfo;
+type
+ TObj = TGICTransformerObj;
+ TProp = TGICTransformerProp;
const
- NumPropsthisclass = 15;
+ NumPropsThisClass = Ord(High(TProp));
SPEC_GSU = 1;
SPEC_AUTO = 2;
SPEC_YY = 3;
+var
+ PropInfo: Pointer = NIL;
+ TypeEnum: TDSSEnum;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TGICTransformer.Create(dssContext: TDSSContext); // Creates superstructure for all Fault objects
+constructor TGICTransformer.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'GICTransformer';
- DSSClassType := GIC_TRANSFORMER + PD_ELEMENT;
-
- ActiveElement := 0;
-
- DefineProperties;
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ TypeEnum := TDSSEnum.Create('GICTransformer: Type', True, 0, 0, ['GSU', 'Auto', 'YY'], [SPEC_GSU, SPEC_AUTO, SPEC_YY]);
+ end;
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, GIC_TRANSFORMER, 'GICTransformer');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TGICTransformer.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TGICTransformer.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
- // Define Property names
-
- PropertyName^[1] := 'BusH';
- PropertyName^[2] := 'BusNH';
- PropertyName^[3] := 'BusX';
- PropertyName^[4] := 'BusNX';
- PropertyName^[5] := 'phases';
- PropertyName^[6] := 'Type';
- PropertyName^[7] := 'R1';
- PropertyName^[8] := 'R2';
- PropertyName^[9] := 'KVLL1';
- PropertyName^[10] := 'KVLL2';
- PropertyName^[11] := 'MVA';
- PropertyName^[12] := 'VarCurve';
- PropertyName^[13] := '%R1';
- PropertyName^[14] := '%R2';
- PropertyName^[15] := 'K';
-
- // define Property help values
- PropertyHelp[1] := 'Name of High-side(H) bus. Examples:' + CRLF +
- 'BusH=busname' + CRLF +
- 'BusH=busname.1.2.3';
- PropertyHelp[2] := 'Name of Neutral bus for H, or first, winding. Defaults to all phases connected ' +
- 'to H-side bus, node 0, if not specified and transformer type is either GSU or YY. ' +
- '(Shunt Wye Connection to ground reference)' +
- 'For Auto, this is automatically set to the X bus.';
- PropertyHelp[3] := 'Name of Low-side(X) bus, if type=Auto or YY. ';
- PropertyHelp[4] := 'Name of Neutral bus for X, or Second, winding. Defaults to all phases connected ' +
- 'to X-side bus, node 0, if not specified. (Shunt Wye Connection to ground reference)';
- PropertyHelp[5] := 'Number of Phases. Default is 3.';
- PropertyHelp[6] := 'Type of transformer: {GSU* | Auto | YY}. Default is GSU.';
- PropertyHelp[7] := 'Resistance, each phase, ohms for H winding, (Series winding, if Auto). Default is 0.0001. If ';
- PropertyHelp[8] := 'Resistance, each phase, ohms for X winding, (Common winding, if Auto). Default is 0.0001. ';
- PropertyHelp[9] := 'Optional. kV LL rating for H winding (winding 1). Default is 500. Required if you are going to export vars for power flow analysis ' +
- 'or enter winding resistances in percent.';
- PropertyHelp[10] := 'Optional. kV LL rating for X winding (winding 2). Default is 138. Required if you are going to export vars for power flow analysis ' +
- 'or enter winding resistances in percent..';
- PropertyHelp[11] := 'Optional. MVA Rating assumed Transformer. Default is 100. Used for computing vars due to GIC and ' +
- 'winding resistances if kV and MVA ratings are specified.';
- PropertyHelp[12] := 'Optional. XYCurve object name. Curve is expected as TOTAL pu vars vs pu GIC amps/phase. Vars are in pu of the MVA property. No Default value. ' +
- 'Required only if you are going to export vars for power flow analysis. ' +
- 'See K property.';
-
- PropertyHelp[13] := 'Optional. Percent Resistance, each phase, for H winding (1), (Series winding, if Auto). Default is 0.2. ' + CRLF + CRLF +
- 'Alternative way to enter R1 value. It is the actual resistances in ohmns that matter. MVA and kV should be specified.';
- PropertyHelp[14] := 'Optional. Percent Resistance, each phase, for X winding (2), (Common winding, if Auto). Default is 0.2. ' + CRLF + CRLF +
- 'Alternative way to enter R2 value. It is the actual resistances in ohms that matter. MVA and kV should be specified.';
- PropertyHelp[15] := 'Mvar K factor. Default way to convert GIC Amps in H winding (winding 1) to Mvar. Default is 2.2. ' +
- 'Commonly-used simple multiplier for estimating Mvar losses for power flow analysis. ' + CRLF + CRLF +
- 'Mvar = K * kvLL * GIC per phase / 1000 ' + CRLF + CRLF +
- 'Mutually exclusive with using the VarCurve property and pu curves.' +
- 'If you specify this (default), VarCurve is ignored.';
-
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // enum properties
+ // PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumProperty;
+ // PropertyOffset[ord(TProp.conn)] := ptruint(@obj.Connection);
+ // PropertyOffset2[ord(TProp.conn)] := PtrInt(DSS.ConnectionEnum);
+
+ PropertyType[ord(TProp.typ)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.typ)] := ptruint(@obj.SpecType);
+ PropertyOffset2[ord(TProp.typ)] := PtrInt(TypeEnum);
+
+ // integer properties
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // bus properties
+ PropertyType[ord(TProp.BusH)] := TPropertyType.BusProperty;
+ PropertyType[ord(TProp.BusNH)] := TPropertyType.BusProperty;
+ PropertyType[ord(TProp.BusX)] := TPropertyType.BusProperty;
+ PropertyType[ord(TProp.BusNX)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.BusH)] := 1;
+ PropertyOffset[ord(TProp.BusNH)] := 2;
+ PropertyOffset[ord(TProp.BusX)] := 3;
+ PropertyOffset[ord(TProp.BusNX)] := 4;
+
+ // object properties
+ PropertyType[ord(TProp.VarCurve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.VarCurve)] := ptruint(@obj.FVarCurveObj);
+ PropertyOffset2[ord(TProp.VarCurve)] := ptruint(DSS.XYCurveClass);
+
+ // double properties
+ PropertyOffset[ord(TProp.KVLL1)] := ptruint(@obj.FkV1);
+ PropertyOffset[ord(TProp.KVLL2)] := ptruint(@obj.FkV2);
+ PropertyOffset[ord(TProp.MVA)] := ptruint(@obj.FMVArating);
+ PropertyOffset[ord(TProp.pctR1)] := ptruint(@obj.FpctR1);
+ PropertyOffset[ord(TProp.pctR2)] := ptruint(@obj.FpctR2);
+ PropertyOffset[ord(TProp.k)] := ptruint(@obj.FKFactor);
+
+ // adv doubles
+ PropertyOffset[ord(TProp.R1)] := ptruint(@obj.G1);
+ PropertyInverse[ord(TProp.R1)] := True;
+
+ PropertyOffset[ord(TProp.R2)] := ptruint(@obj.G2);
+ PropertyInverse[ord(TProp.R2)] := True;
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGICTransformer.NewObject(const ObjName: String): Integer;
-begin
- // create a new object of this class and add to list
- with ActiveCircuit do
- begin
- ActiveCktElement := TGICTransformerObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TGICTransformer.GICTransSetBusH(const s: String);
-
+function TGICTransformer.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- s2: String;
- dotpos: Integer;
-
- // Set Bus2 = BusH1.0.0.0
-
+ Obj: TObj;
begin
- with DSS.ActiveGICTransformerObj do
- begin
-
- SetBus(1, S);
-
- // Default Bus2 to zero node of Bus1. (Wye Grounded connection)
-
- // Strip node designations from S
- dotpos := Pos('.', S);
- if dotpos > 0 then
- S2 := Copy(S, 1, dotpos - 1) // copy up to Dot
- else
- S2 := Copy(S, 1, Length(S));
-
- S2 := S2 + '.0.0.0'; // Set Default for up to 3 phases
-
- SetBus(2, S2);
- IsShunt := TRUE;
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TGICTransformer.GICTransSetBusX(const s: String);
-
+procedure TGICTransformerObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- s2: String;
+ S, S2: String;
dotpos: Integer;
-
- // Special handling for Bus X
- // Make sure we have enough terminals defined
- // Set Bus2 = Bus1.0.0.0
-
begin
- with DSS.ActiveGICTransformerObj do
- begin
-
- if Nterms <> 4 then // have to have 4 terminals to set this property
+ case Idx of
+ ord(TProp.BusH):
begin
- Nterms := 4;
- NConds := Fnphases; // force reallocation of terminals and conductors
- end;
-
- SetBus(3, S);
-
- // Default Bus4 to zero node of Bus3. (Wye Grounded connection)
-
- // Strip node designations from S
- dotpos := Pos('.', S);
- if dotpos > 0 then
- S2 := Copy(S, 1, dotpos - 1) // copy up to Dot
- else
- S2 := Copy(S, 1, Length(S));
-
- S2 := S2 + '.0.0.0'; // Set Default for up to 3 phases
-
- SetBus(4, S2);
- IsShunt := TRUE;
- end;
-end;
-
+ // Set Bus2 = BusH1.0.0.0
+ // Default Bus2 to zero node of Bus1. (Wye Grounded connection)
+ // Strip node designations from S
+ S := GetBus(1);
+ dotpos := Pos('.', S);
+ if dotpos > 0 then
+ S2 := Copy(S, 1, dotpos - 1) // copy up to Dot
+ else
+ S2 := Copy(S, 1, Length(S));
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGICTransformer.Edit: Integer;
+ S2 := S2 + '.0.0.0'; // Set Default for up to 3 phases
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
+ SetBus(2, S2);
+ IsShunt := TRUE;
+ end;
+ ord(TProp.BusX):
+ begin
+ // Special handling for Bus X
+ // Make sure we have enough terminals defined
+ // Set Bus2 = Bus1.0.0.0
+ if Nterms <> 4 then // have to have 4 terminals to set this property
+ begin
+ Nterms := 4;
+ NConds := Fnphases; // force reallocation of terminals and conductors
+ end;
-begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveGICTransformerObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveGICTransformerObj; // use property to set this value
+ // Strip node designations from S
+ S := GetBus(3);
+ dotpos := Pos('.', S);
+ if dotpos > 0 then
+ S2 := Copy(S, 1, dotpos - 1) // copy up to Dot
+ else
+ S2 := Copy(S, 1, Length(S));
- with DSS.ActiveGICTransformerObj do
- begin
+ // Default Bus4 to zero node of Bus3. (Wye Grounded connection)
+ S2 := S2 + '.0.0.0'; // Set Default for up to 3 phases
+ SetBus(4, S2);
+ IsShunt := TRUE;
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 350);
- 1:
- GICTransSetBusH(param);
- 2:
- Setbus(2, param);
- 3:
- GICTransSetBusX(param);
- 4:
- Setbus(4, param);
- 5: ; // see below
- 6:
- case Uppercase(param)[1] of
- 'G':
- SpecType := SPEC_GSU;
- 'A':
- SpecType := SPEC_AUTO;
- 'Y':
- SpecType := SPEC_YY;
- end;
- 7:
- begin
- G1 := Parser.Dblvalue;
- if G1 <> 0.0 then
- G1 := 1.0 / G1
- else
- G1 := 10000.0; // Default to a low resistance
- end;
- 8:
- begin
- G2 := Parser.Dblvalue;
- if G2 <> 0.0 then
- G2 := 1.0 / G2
- else
- G2 := 10000.0; // Default to a low resistance
- end;
- 9:
- FkV1 := Parser.DblValue;
- 10:
- FkV2 := Parser.DblValue;
- 11:
- FMVArating := Parser.DblValue;
- 12:
- FVarCurve := Param;
- 13:
- FpctR1 := Parser.DblValue;
- 14:
- FpctR2 := Parser.DblValue;
- 15:
- FKFactor := Parser.DblValue;
- else
- // Inherited
- ClassEdit(DSS.ActiveGICTransformerObj, ParamPointer - NumPropsThisClass)
+ if SpecType = SPEC_AUTO then
+ begin // automatically make up series-to-common connection
+ SetBus(2, GetBus(3));
end;
-
- // Some specials ...
- case ParamPointer of
- 1:
- PropertyValue[2] := GetBus(2); // Bus2 gets modified if bus1 is set
- 3:
+ end;
+ ord(TProp.phases):
+ if Fnphases <> previousIntVal then
+ begin
+ NConds := Fnphases; // Force Reallocation of terminal info if different size
+ ActiveCircuit.BusNameRedefined := TRUE; // Set Global Flag to signal circuit to rebuild busdefs
+ end;
+ 6:
+ case Spectype of
+ SPEC_AUTO:
begin
- PropertyValue[4] := GetBus(4); // Bus4 gets modified if bus3(X) is set
- if SpecType = SPEC_AUTO then
- begin // automatically make up series-to-common connection
- SetBus(2, GetBus(3));
- PropertyValue[2] := GetBus(2);
- end;
- end;
- 5:
- if Fnphases <> Parser.IntValue then
+ if Nterms = 2 then
begin
- Nphases := Parser.IntValue;
- NConds := Fnphases; // Force Reallocation of terminal info if different size
- ActiveCircuit.BusNameRedefined := TRUE; // Set Global Flag to signal circuit to rebuild busdefs
+ Nterms := 4;
+ NConds := Fnphases;
end;
- 6:
- case Spectype of
- SPEC_AUTO:
- begin
- if Nterms = 2 then
- begin
- Nterms := 4;
- NConds := Fnphases;
- end;
- SetBus(2, GetBus(3));
- end;
- end;
- 7..8:
- FpctRSpecified := FALSE;
- 9..10:
- FkVSpecified := TRUE;
- 12:
- begin
- FVarCurveObj := DSS.XYCurveClass.Find(FVarCurve);
- Kspecified := FALSE;
+ SetBus(2, GetBus(3));
end;
- 13..14:
- FpctRSpecified := TRUE;
- 15:
- KSpecified := TRUE;
- else
end;
-
- //YPrim invalidation on anything that changes impedance values or no. of terminals
- case ParamPointer of
- 3..8:
- YprimInvalid := TRUE;
- else
- end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ 7:
+ begin
+ if G1 = 0.0 then
+ G1 := 10000.0; // Default to a low resistance
+ FpctRSpecified := FALSE;
end;
-
- RecalcElementData;
+ 8:
+ begin
+ if G2 = 0.0 then
+ G2 := 10000.0; // Default to a low resistance
+ FpctRSpecified := FALSE;
+ end;
+ 9..10:
+ FkVSpecified := TRUE;
+ 12:
+ if FVarCurveObj <> NIL then
+ Kspecified := FALSE;
+ 13..14:
+ FpctRSpecified := TRUE;
+ 15:
+ KSpecified := TRUE;
end;
+ //YPrim invalidation on anything that changes impedance values or no. of terminals
+ case Idx of
+ 3..8:
+ YprimInvalid := TRUE;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TGICTransformer.MakeLike(const GICTransName: String): Integer;
+procedure TGICTransformerObj.MakeLike(OtherPtr: Pointer);
var
- OtherGICTrans: TGICTransformerObj;
- i: Integer;
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this Fault name in the present collection}
- OtherGICTrans := Find(GICTransName);
- if OtherGICTrans <> NIL then
- with DSS.ActiveGICTransformerObj do
- begin
-
- if Fnphases <> OtherGICTrans.Fnphases then
- begin
- Fnphases := OtherGICTrans.Fnphases;
- FnTerms := OtherGICTrans.FnTerms;
- NConds := Fnphases; // force reallocation of terminals and conductors
-
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
-
- end;
-
- BaseFrequency := OtherGICTrans.BaseFrequency;
- G1 := OtherGICTrans.G1;
- G2 := OtherGICTrans.G2;
- SpecType := OtherGICTrans.SpecType;
- FMVARating := OtherGICTrans.FMVARating;
- FVarcurve := OtherGICTrans.FVarcurve;
- FVarcurveObj := OtherGICTrans.FVarcurveObj;
- FkV1 := OtherGICTrans.FkV1;
- FkV2 := OtherGICTrans.FkV2;
- FpctR1 := OtherGICTrans.FpctR1;
- FpctR2 := OtherGICTrans.FpctR2;
- FpctRSpecified := OtherGICTrans.FpctRSpecified;
- FkVSpecified := OtherGICTrans.FkVSpecified;
- FZBase1 := OtherGICTrans.FZBase1;
- FZBase2 := OtherGICTrans.FZBase2;
- FKfactor := OtherGICTrans.FKfactor;
- KSpecified := OtherGICTrans.KSpecified;
-
- ClassMakeLike(OtherGICTrans);
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherGICTrans.PropertyValue[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in GICTransformer MakeLike: "' + GICTransName + '" Not Found.', 351);
+ inherited MakeLike(OtherPtr);
+ Other := TObj(OtherPtr);
+ if Fnphases <> Other.Fnphases then
+ begin
+ Fnphases := Other.Fnphases;
+ FnTerms := Other.FnTerms;
+ NConds := Fnphases; // force reallocation of terminals and conductors
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
+ end;
+ BaseFrequency := Other.BaseFrequency;
+ G1 := Other.G1;
+ G2 := Other.G2;
+ SpecType := Other.SpecType;
+ FMVARating := Other.FMVARating;
+ FVarcurveObj := Other.FVarcurveObj;
+ FkV1 := Other.FkV1;
+ FkV2 := Other.FkV2;
+ FpctR1 := Other.FpctR1;
+ FpctR2 := Other.FpctR2;
+ FpctRSpecified := Other.FpctRSpecified;
+ FkVSpecified := Other.FkVSpecified;
+ FZBase1 := Other.FZBase1;
+ FZBase2 := Other.FZBase2;
+ FKfactor := Other.FKfactor;
+ KSpecified := Other.KSpecified;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TGICTransformer Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
constructor TGICTransformerObj.Create(ParClass: TDSSClass; const FaultName: String);
-
begin
inherited Create(ParClass);
DSSObjType := ParClass.DSSClassType;
- Name := LowerCase(FaultName);
+ Name := AnsiLowerCase(FaultName);
- NPhases := 3; // Directly set conds and phases
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 2; // Force allocation of terminals and conductors
@@ -504,7 +348,6 @@ constructor TGICTransformerObj.Create(ParClass: TDSSClass; const FaultName: Stri
SpecType := SPEC_GSU;
FMVARating := 100.0;
- FVarCurve := '';
FVarCurveObj := NIL;
FkVSpecified := FALSE;
@@ -522,8 +365,6 @@ constructor TGICTransformerObj.Create(ParClass: TDSSClass; const FaultName: Stri
PctPerm := 100.0;
HrsToRepair := 0.0;
- InitPropertyValues(0);
-
Yorder := Fnterms * Fnconds;
FpctRSpecified := TRUE; // Force computation of G1, G2
@@ -531,17 +372,13 @@ constructor TGICTransformerObj.Create(ParClass: TDSSClass; const FaultName: Stri
FpctRSpecified := FALSE; // Turn flag off
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TGICTransformerObj.Destroy;
begin
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TGICTransformerObj.RecalcElementData;
-
begin
-
FZbase1 := SQR(FkV1) / FMVArating;
FZbase2 := SQR(FkV2) / FMVArating;
@@ -555,10 +392,8 @@ procedure TGICTransformerObj.RecalcElementData;
FPctR1 := 100.0 / (FZBase1 * G1);
FPctR2 := 100.0 / (FZBase2 * G2);
end;
-
end;
-//- - - - - - - - VAR EXPORT - - - - - - - - - - - - - - - - - - - -
procedure TGICTransformerObj.WriteVarOutputRecord(F: TFileStream);
var
Curr: Complex;
@@ -566,12 +401,11 @@ procedure TGICTransformerObj.WriteVarOutputRecord(F: TFileStream);
GICperPhase: Double;
puCurrMag: Double;
i: Integer;
-
begin
ComputeIterminal;
Curr := CZERO;
for i := 1 to Fnphases do
- Caccum(Curr, Iterminal^[i]);
+ Curr += Iterminal^[i];
GICperPhase := Cabs(Curr) / Fnphases;
if Kspecified then
begin
@@ -593,17 +427,12 @@ procedure TGICTransformerObj.WriteVarOutputRecord(F: TFileStream);
FSWriteln(F, Format('%s, %.8g, %.8g', [GetBus(1), MVarMag, (GICperPhase)]));
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TGICTransformerObj.CalcYPrim;
-
var
Value, Value2: Complex;
i: Integer;
-
YPrimTemp: TCMatrix;
-
begin
-
if YPrimInvalid then
begin // Reallocate YPrim if something has invalidated old allocation
if YPrim_Series <> NIL then
@@ -634,7 +463,6 @@ procedure TGICTransformerObj.CalcYPrim;
with YPrimTemp do
begin
-
{ Now, Put in Yprim matrix }
{If the fault is not ON, the set zero conductance}
@@ -644,7 +472,7 @@ procedure TGICTransformerObj.CalcYPrim;
SPEC_GSU:
begin
Value := Cmplx(G1, 0.0);
- Value2 := cnegate(Value);
+ Value2 := -Value;
for i := 1 to Fnphases do
begin
SetElement(i, i, Value); // Elements are only on the diagonals
@@ -657,7 +485,7 @@ procedure TGICTransformerObj.CalcYPrim;
begin
// Terminals 1 and 2
Value := Cmplx(G1, 0.0);
- Value2 := cnegate(Value);
+ Value2 := -Value;
for i := 1 to Fnphases do
begin
SetElement(i, i, Value); // Elements are only on the diagonals
@@ -666,7 +494,7 @@ procedure TGICTransformerObj.CalcYPrim;
end; {For}
// Terminals 3 and 4
Value := Cmplx(G2, 0.0);
- Value2 := cnegate(Value);
+ Value2 := -Value;
for i := (2 * Fnphases + 1) to 3 * Fnphases do
begin
SetElement(i, i, Value); // Elements are only on the diagonals
@@ -679,7 +507,7 @@ procedure TGICTransformerObj.CalcYPrim;
begin
// Terminals 1 and 2
Value := Cmplx(G1, 0.0);
- Value2 := cnegate(Value);
+ Value2 := -Value;
for i := 1 to Fnphases do
begin
SetElement(i, i, Value); // Elements are only on the diagonals
@@ -688,7 +516,7 @@ procedure TGICTransformerObj.CalcYPrim;
end; {For}
// Terminals 3 and 4
Value := Cmplx(G2, 0.0);
- Value2 := cnegate(Value);
+ Value2 := -Value;
for i := (2 * Fnphases + 1) to 3 * Fnphases do
begin
SetElement(i, i, Value); // Elements are only on the diagonals
@@ -707,129 +535,12 @@ procedure TGICTransformerObj.CalcYPrim;
YprimInvalid := FALSE;
end;
-procedure TGICTransformerObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, complete);
-
-
- with ParentClass do
- begin
- FSWriteln(F, '~ ' + PropertyName^[1] + '=' + firstbus);
- FSWriteln(F, '~ ' + PropertyName^[2] + '=' + nextbus);
- FSWriteln(F, '~ ' + PropertyName^[3] + '=' + nextbus);
- FSWriteln(F, '~ ' + PropertyName^[4] + '=' + nextbus);
-
- FSWriteln(F, '~ ' + PropertyName^[5] + '=' + IntToStr(Fnphases));
- case Spectype of
- SPEC_GSU:
- FSWriteln(F, '~ ', PropertyName^[6], '= GSU');
- SPEC_AUTO:
- FSWriteln(F, '~ ', PropertyName^[6], '= AUTO');
- SPEC_YY:
- FSWriteln(F, '~ ', PropertyName^[6], '= YY');
- end;
- FSWriteln(F, Format('~ %s=%.8g', [PropertyName^[7], 1.0 / G1]));
- FSWriteln(F, Format('~ %s=%.8g', [PropertyName^[8], 1.0 / G2]));
- FSWriteln(F, Format('~ %s=%.2f', [PropertyName^[9], FkV1]));
- FSWriteln(F, Format('~ %s=%.2f', [PropertyName^[10], FkV2]));
- FSWriteln(F, Format('~ %s=%.2f', [PropertyName^[11], FMVARating]));
- FSWriteln(F, Format('~ %s=%s', [PropertyName^[12], FVarcurve]));
- FSWriteln(F, Format('~ %s=%.8g', [PropertyName^[13], FpctR1]));
- FSWriteln(F, Format('~ %s=%.8g', [PropertyName^[14], FpctR2]));
-
- for i := NumPropsthisClass + 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
- end;
-
- end;
-
-end;
-
-
-procedure TGICTransformerObj.InitPropertyValues(ArrayOffset: Integer);
+procedure TGICTransformerObj.MakePosSequence();
begin
-
- PropertyValue[1] := getbus(1);
- PropertyValue[2] := getbus(2);
- PropertyValue[3] := getbus(3);
- PropertyValue[4] := getbus(4);
- PropertyValue[5] := '3';
- PropertyValue[6] := 'GSU';
- PropertyValue[7] := '0.0001';
- PropertyValue[8] := '0.0001';
- PropertyValue[9] := '500';
- PropertyValue[10] := '138';
- PropertyValue[11] := '100';
- PropertyValue[12] := '';
- PropertyValue[13] := '0.2';
- PropertyValue[14] := '0.2';
- PropertyValue[15] := '2.2';
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
- // Override Inherited Properties
- PropertyValue[NumPropsThisClass + 1] := '0'; //Normamps
- PropertyValue[NumPropsThisClass + 2] := '0'; //emergamps
- PropertyValue[NumPropsThisClass + 3] := '0'; //Fault rate
- PropertyValue[NumPropsThisClass + 4] := '0'; // Pct Perm
- PropertyValue[NumPropsThisClass + 5] := '0'; // Hrs to repair
-
-end;
-
-function TGICTransformerObj.GetPropertyValue(Index: Integer): String;
-
-begin
- case INdex of
- 1:
- Result := GetBus(1);
- 2:
- Result := GetBus(2);
- 3:
- Result := GetBus(3);
- 4:
- Result := GetBus(4);
- 5:
- Result := Format('%d', [Nphases]);
- 7:
- Result := Format('%.8g', [1.0 / G1]);
- 8:
- Result := Format('%.8g', [1.0 / G2]);
- 9:
- Result := Format('%.8g', [FkV1]);
- 10:
- Result := Format('%.8g', [FkV2]);
- 11:
- Result := Format('%.8g', [FMVARating]);
- 12:
- Result := Format('%s', [Fvarcurve]);
- 13:
- Result := Format('%.8g', [FpctR1]);
- 14:
- Result := Format('%.8g', [FpctR2]);
- 15:
- Result := Format('%.8g', [FKFactor]);
- else
- Result := inherited GetPropertyValue(Index);
- end;
-end;
-
-procedure TGICTransformerObj.MakePosSequence;
-
-
-begin
- if FnPhases <> 1 then
- begin
- Parser.CmdString := 'Phases=1';
- Edit;
- end;
+ if Fnphases > 1 then
+ SetInteger(ord(TProp.Phases), 1);
inherited;
-
end;
+finalization TypeEnum.Free;
end.
diff --git a/src/PDElements/Line.pas b/src/PDElements/Line.pas
index 62a0b0003..ca6ef4d28 100644
--- a/src/PDElements/Line.pas
+++ b/src/PDElements/Line.pas
@@ -6,16 +6,6 @@
----------------------------------------------------------
}
-{ 3-1-00 Reactivated line dump
- 3-13-03 Fixed bug where terminal quantities were not getting reallocated in FetchCondCode
- 2018 Added GIC stuff
-}
-
-// TODO:
-// - remember to add explicit enum for LineType
-// - remove duplicated code across Line.pas, LineCode.pas, LineGeometry.pas
-
-
interface
uses
@@ -31,52 +21,95 @@ interface
LineSpacing,
ConductorData,
PDClass,
- Ucomplex;
+ UComplex, DSSUcomplex,
+ DSSObject;
type
+{$SCOPEDENUMS ON}
+ TLineProp = (
+ INVALID = 0,
+ bus1 = 1,
+ bus2 = 2,
+ linecode = 3,
+ length = 4,
+ phases = 5,
+ r1 = 6,
+ x1 = 7,
+ r0 = 8,
+ x0 = 9,
+ C1 = 10,
+ C0 = 11,
+ rmatrix = 12,
+ xmatrix = 13,
+ cmatrix = 14,
+ Switch = 15,
+ Rg = 16,
+ Xg = 17,
+ rho = 18,
+ geometry = 19,
+ units = 20,
+ spacing = 21,
+ wires = 22,
+ EarthModel = 23,
+ cncables = 24,
+ tscables = 25,
+ B1 = 26,
+ B0 = 27,
+ Seasons = 28,
+ Ratings = 29,
+ LineType = 30
+ );
+{$SCOPEDENUMS OFF}
TLine = class(TPDClass)
- PRIVATE
- procedure DoRmatrix;
- procedure DoXmatrix;
- procedure DoCmatrix;
-
PROTECTED
- procedure DefineProperties; // Add Properties of this class to propName
- function MakeLike(const LineName: String): Integer; OVERRIDE;
-
+ procedure DefineProperties; override;
PUBLIC
- LineTypeList: TCommandList;
-
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
-
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ function EndEdit(ptr: Pointer; const NumChanges: integer): Boolean; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TLineObj = class(TPDElement)
-{$IFDEF DSS_CAPI}
PUBLIC
-{$ELSE}
- PRIVATE
-{$ENDIF}
FZFrequency: Double; // keep track of last frequency computed for geometry
FLineCodeUnits: Integer;
FUnitsConvert: Double; // conversion factor
- FLineGeometryObj: TLineGeometryObj;
- FLineSpacingObj: TLineSpacingObj;
- FLineWireData: pConductorDataArray;
FWireDataSize: Integer;
FPhaseChoice: ConductorChoice;
- FrhoSpecified: Boolean;
- FLineCodeSpecified: Boolean;
FEarthModel: Integer;
+
+ FrhoSpecified: Boolean;
FCapSpecified: Boolean; // To make sure user specifies C in some form
+ SymComponentsChanged: Boolean;
+ SymComponentsModel: LongBool;
+ IsSwitch: LongBool;
+
FLineType: Integer; // Pointer to code for type of line
UserLengthUnits: Integer; // keep track of the user's input length units
+ Zinv: TCMatrix;
+ Z: TCMatrix; // Base Frequency Series Z matrix per unit length
+ Yc: TCMatrix;
+
+ R1: Double;
+ X1: Double;
+ R0: Double;
+ X0: Double;
+ C1: Double;
+ C0: Double;
+ Len: Double;
+ LengthUnits: Integer;
+
+ Rg, Xg, KXg, rho: Double;
+ GeneralPlotQuantity: Double; // For general circuit plotting
+
+ LineCodeObj: TLineCodeObj;
+ LineGeometryObj: TLineGeometryObj;
+ LineSpacingObj: TLineSpacingObj;
+
+ FLineWireData: pConductorDataArray;
procedure FMakeZFromGeometry(f: Double); // make new Z, Zinv, Yc, etc
procedure KillGeometrySpecified;
@@ -86,7 +119,6 @@ TLineObj = class(TPDElement)
procedure ClearYPrim;
procedure ResetLengthUnits;
- procedure UpdatePDProperties; // update inherited properties
function NumConductorData: Integer;
function FetchConductorData(i: Integer): TConductorDataObj;
@@ -96,64 +128,36 @@ TLineObj = class(TPDElement)
procedure DoLongLine(Frequency: Double); // Long Line Correction for 1=phase
procedure ConvertZinvToPosSeqR; // for GIC analysis, primarily
- PROTECTED
- Zinv: TCMatrix;
-
- PUBLIC // Moved to make values available to the COM interface
-
- Z: TCMatrix; // Base Frequency Series Z matrix per unit length
- Yc: TCMatrix;
-
- R1: Double;
- X1: Double;
- R0: Double;
- X0: Double;
- C1: Double;
- C0: Double;
- Len: Double;
- LengthUnits: Integer;
-
- Rg, Xg, KXg, rho: Double;
- GeneralPlotQuantity: Double; // For general circuit plotting
- CondCode: String;
- GeometryCode: String;
- SpacingCode: String;
- GeometrySpecified: Boolean;
- SpacingSpecified: Boolean;
- SymComponentsChanged: Boolean;
- SymComponentsModel: Boolean;
- IsSwitch: Boolean;
-
- procedure GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Complex); OVERRIDE;
+ // procedure GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Complex); OVERRIDE;
procedure GetSeqLosses(var PosSeqLosses, NegSeqLosses, ZeroSeqLosses: complex); OVERRIDE;
constructor Create(ParClass: TDSSClass; const LineName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
- function MergeWith(var OtherLine: TLineObj; Series: Boolean): Boolean;
- procedure UpdateControlElements(const NewName, OldName: String);
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
+ function MergeWith(var Other: TLineObj; Series: Boolean): Boolean;
+ procedure UpdateControlElements(NewLine, OldLine: TLineObj);
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
// Public for the COM Interface
- procedure FetchLineCode(const Code: String);
- procedure FetchGeometryCode(const Code: String);
- procedure FetchLineSpacing(const Code: String);
- procedure FetchWireList(const Code: String);
- procedure FetchCNCableList(const Code: String);
- procedure FetchTSCableList(const Code: String);
+ procedure FetchLineCode();
+ procedure FetchGeometryCode();
+ procedure FetchLineSpacing();
// Reliability calcs
procedure CalcFltRate; OVERRIDE; // Calc failure rates for section and buses
// CIM XML access
- property LineCodeSpecified: Boolean READ FLineCodeSpecified;
+ function LineCodeSpecified: Boolean;
+ function GeometrySpecified: Boolean;
+ function SpacingSpecified: Boolean;
+
property PhaseChoice: ConductorChoice READ FPhaseChoice;
property UnitsConvert: Double READ FUnitsConvert; // conversion to present Line units
@@ -165,7 +169,6 @@ TLineObj = class(TPDElement)
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Sysutils,
@@ -177,711 +180,575 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TLineObj;
+ TProp = TLineProp;
const
- NumPropsThisClass = 30;
- // MaxPhases = 20; // for fixed buffers
-
- LINE_TYPES: Array of String = [
- 'oh', 'ug', 'ug_ts', 'ug_cn', 'swt_ldbrk', 'swt_fuse',
- 'swt_sect', 'swt_rec', 'swt_disc', 'swt_brk', 'swt_elbow'
- ];
-
+ NumPropsThisClass = Ord(High(TProp));
+ CAP_EPSILON: Complex = (re: 0.0; im: 4.2e-8); // 5 kvar of capacitive reactance at 345 kV to avoid open line problem
var
- CAP_EPSILON: Complex;
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TLine.Create(dssContext: TDSSContext); // Creates superstructure for all Line objects
+constructor TLine.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'Line';
- DSSClassType := DSSClassType + LINE_ELEMENT; // in both PDElement list and Linesection lists
-
- ActiveElement := 0;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
-
- LineTypeList := TCommandList.Create(LINE_TYPES);
- LineTypeList.Abbrev := TRUE; // Allow abbreviations for line type code
+ inherited Create(dssContext, LINE_ELEMENT, 'Line');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TLine.Destroy;
begin
- LineTypeList.Free();
-
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLine.DefineProperties;
+function GetZSeqScale(obj: TLineObj; getter: Boolean): Double;
begin
+ if getter then
+ Result := obj.FUnitsConvert
+ else
+ Result := 1;
+end;
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
- PropertyName[1] := 'bus1';
- PropertyName[2] := 'bus2';
- PropertyName[3] := 'linecode';
- PropertyName[4] := 'length';
- PropertyName[5] := 'phases';
- PropertyName[6] := 'r1';
- PropertyName[7] := 'x1';
- PropertyName[8] := 'r0';
- PropertyName[9] := 'x0';
- PropertyName[10] := 'C1';
- PropertyName[11] := 'C0';
- PropertyName[12] := 'rmatrix';
- PropertyName[13] := 'xmatrix';
- PropertyName[14] := 'cmatrix';
- PropertyName[15] := 'Switch';
- PropertyName[16] := 'Rg';
- PropertyName[17] := 'Xg';
- PropertyName[18] := 'rho';
- PropertyName[19] := 'geometry';
- PropertyName[20] := 'units';
- PropertyName[21] := 'spacing';
- PropertyName[22] := 'wires';
- PropertyName[23] := 'EarthModel';
- PropertyName[24] := 'cncables';
- PropertyName[25] := 'tscables';
- PropertyName[26] := 'B1';
- PropertyName[27] := 'B0';
- PropertyName[28] := 'Seasons';
- PropertyName[29] := 'Ratings';
- PropertyName[30] := 'LineType';
-
- // define Property help values
-
- PropertyHelp[1] := 'Name of bus to which first terminal is connected.' + CRLF +
- 'Example:' + CRLF +
- 'bus1=busname (assumes all terminals connected in normal phase order)' + CRLF +
- 'bus1=busname.3.1.2.0 (specify terminal to node connections explicitly)';
- PropertyHelp[2] := 'Name of bus to which 2nd terminal is connected.';
- PropertyHelp[3] := 'Name of linecode object describing line impedances.' + CRLF +
- 'If you use a line code, you do not need to specify the impedances here. ' +
- 'The line code must have been PREVIOUSLY defined. ' +
- 'The values specified last will prevail over those specified earlier (left-to-right ' +
- 'sequence of properties). You can subsequently change the number of phases if symmetrical component quantities are specified.' +
- 'If no line code or impedance data are specified, the line object ' +
- 'defaults to 336 MCM ACSR on 4 ft spacing.';
- PropertyHelp[4] := 'Length of line. Default is 1.0. If units do not match the impedance data, specify "units" property. ';
- PropertyHelp[5] := 'Number of phases, this line.';
- PropertyHelp[6] := 'Positive-sequence Resistance, ohms per unit length. Setting any of R1, R0, X1, X0, C1, C0 forces ' +
- 'the program to use the symmetrical component line definition. See also Rmatrix.';
- PropertyHelp[7] := 'Positive-sequence Reactance, ohms per unit length. Setting any of R1, R0, X1, X0, C1, C0 forces ' +
- 'the program to use the symmetrical component line definition. See also Xmatrix';
- PropertyHelp[8] := 'Zero-sequence Resistance, ohms per unit length. Setting any of R1, R0, X1, X0, C1, C0 forces ' +
- 'the program to use the symmetrical component line definition.';
- PropertyHelp[9] := 'Zero-sequence Reactance, ohms per unit length. Setting any of R1, R0, X1, X0, C1, C0 forces ' +
- 'the program to use the symmetrical component line definition.';
- PropertyHelp[10] := 'Positive-sequence capacitance, nf per unit length. Setting any of R1, R0, X1, X0, C1, C0 forces ' +
- 'the program to use the symmetrical component line definition. See also Cmatrix and B1.';
- PropertyHelp[11] := 'Zero-sequence capacitance, nf per unit length. Setting any of R1, R0, X1, X0, C1, C0 forces ' +
- 'the program to use the symmetrical component line definition.See also B0.';
- PropertyHelp[12] := 'Resistance matrix, lower triangle, ohms per unit length. Order of the matrix is the number of phases. ' +
- 'May be used to specify the impedance of any line configuration. Using any of Rmatrix, Xmatrix, Cmatrix ' +
- 'forces program to use the matrix values for line impedance definition. For balanced line models, you may ' +
- 'use the standard symmetrical component data definition instead.';
- PropertyHelp[13] := 'Reactance matrix, lower triangle, ohms per unit length. Order of the matrix is the number of phases. ' +
- 'May be used to specify the impedance of any line configuration. Using any of Rmatrix, Xmatrix, Cmatrix ' +
- 'forces program to use the matrix values for line impedance definition. For balanced line models, you may ' +
- 'use the standard symmetrical component data definition instead.';
- PropertyHelp[14] := 'Nodal Capacitance matrix, lower triangle, nf per unit length.Order of the matrix is the number of phases. ' +
- 'May be used to specify the shunt capacitance of any line configuration. Using any of Rmatrix, Xmatrix, Cmatrix ' +
- 'forces program to use the matrix values for line impedance definition. For balanced line models, you may ' +
- 'use the standard symmetrical component data definition instead.';
- PropertyHelp[15] := '{y/n | T/F} Default= no/false. Designates this line as a switch for graphics and algorithmic purposes. ' + CRLF +
- 'SIDE EFFECT: Sets r1 = 1.0; x1 = 1.0; r0 = 1.0; x0 = 1.0; c1 = 1.1 ; c0 = 1.0; length = 0.001; You must reset if you want something different.';
- PropertyHelp[16] := 'Carson earth return resistance per unit length used to compute impedance values at base frequency. ' +
- 'Default is 0.01805 = 60 Hz value in ohms per kft (matches default line impedances). ' +
- 'This value is required for harmonic solutions if you wish to adjust the earth return impedances for frequency. ' +
- 'If not, set both Rg and Xg = 0.';
- PropertyHelp[17] := 'Carson earth return reactance per unit length used to compute impedance values at base frequency. For making better frequency adjustments. ' +
- 'Default is 0.155081 = 60 Hz value in ohms per kft (matches default line impedances). ' +
- 'This value is required for harmonic solutions if you wish to adjust the earth return impedances for frequency. ' +
- 'If not, set both Rg and Xg = 0.';
- PropertyHelp[18] := 'Default=100 meter ohms. Earth resitivity used to compute earth correction factor. Overrides Line geometry definition if specified.';
- PropertyHelp[19] := 'Geometry code for LineGeometry Object. Supercedes any previous definition of line impedance. ' +
- 'Line constants are computed for each frequency change or rho change. CAUTION: may alter number of phases. ' +
- 'You cannot subsequently change the number of phases unless you change how the line impedance is defined.';
- PropertyHelp[20] := 'Length Units = {none | mi|kft|km|m|Ft|in|cm } Default is None - assumes length units match impedance units.';
- PropertyHelp[21] := 'Reference to a LineSpacing for use in a line constants calculation.' + CRLF +
- 'Must be used in conjunction with the Wires property.' + CRLF +
- 'Specify this before the wires property.';
- PropertyHelp[22] := 'Array of WireData names for use in an overhead line constants calculation.' + CRLF +
- 'Must be used in conjunction with the Spacing property.' + CRLF +
- 'Specify the Spacing first, and "ncond" wires.' + CRLF +
- 'May also be used to specify bare neutrals with cables, using "ncond-nphase" wires.';
- PropertyHelp[23] := 'One of {Carson | FullCarson | Deri}. Default is the global value established with the Set EarthModel command. ' +
- 'See the Options Help on EarthModel option. This is used to override the global value for this line. This ' +
- 'option applies only when the "geometry" property is used.';
- PropertyHelp[24] := 'Array of CNData names for use in a cable constants calculation.' + CRLF +
- 'Must be used in conjunction with the Spacing property.' + CRLF +
- 'Specify the Spacing first, using "nphases" cncables.' + CRLF +
- 'You may later specify "nconds-nphases" wires for separate neutrals';
- PropertyHelp[25] := 'Array of TSData names for use in a cable constants calculation.' + CRLF +
- 'Must be used in conjunction with the Spacing property.' + CRLF +
- 'Specify the Spacing first, using "nphases" tscables.' + CRLF +
- 'You may later specify "nconds-nphases" wires for separate neutrals';
- PropertyHelp[26] := 'Alternate way to specify C1. MicroS per unit length';
- PropertyHelp[27] := 'Alternate way to specify C0. MicroS per unit length';
- PropertyHelp[28] := 'Defines the number of ratings to be defined for the wire, to be used only when defining seasonal ratings using the "Ratings" property.';
- PropertyHelp[29] := 'An array of ratings to be used when the seasonal ratings flag is True. It can be used to insert' +
- CRLF + 'multiple ratings to change during a QSTS simulation to evaluate different ratings in lines.';
- PropertyHelp[30] := 'Code designating the type of line. ' + CRLF +
- 'One of: OH, UG, UG_TS, UG_CN, SWT_LDBRK, SWT_FUSE, SWT_SECT, SWT_REC, SWT_DISC, SWT_BRK, SWT_ELBOW' + CRLF + CRLF +
- 'OpenDSS currently does not use this internally. For whatever purpose the user defines. Default is OH.' ;
-
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
- PropertyHelp[NumPropsThisClass + 3] := 'Failure rate PER UNIT LENGTH per year. Length must be same units as LENGTH property. Default is 0.1 fault per unit length per year.';
- PropertyHelp[NumPropsThisClass + 4] := PropertyHelp[NumPropsThisClass + 4] + ' Default is 20.';
- PropertyHelp[NumPropsThisClass + 5] := PropertyHelp[NumPropsThisClass + 5] + ' Default is 3 hr.';
+function GetCSeqScale(obj: TLineObj; getter: Boolean): Double;
+begin
+ if getter then
+ Result := obj.FUnitsConvert * 1.0e-9
+ else
+ Result := 1.0e-9;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLine.NewObject(const ObjName: String): Integer;
+function GetZmatScale(obj: TLineObj; getter: Boolean): Double;
begin
- // create a new object of this class and add to list
- with ActiveCircuit do
+ Result := 1;
+ if getter then
begin
- ActiveCktElement := TLineObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
+ if obj.GeometrySpecified or obj.SpacingSpecified then
+ Result := Result * obj.Len
+ else
+ Result := Result * obj.FUnitsConvert
end;
end;
-procedure TLineObj.UpdatePDProperties;
-var
- TempStr: String;
- j: Integer;
+function GetYCScale(obj: TLineObj; getter: Boolean): Double;
begin
- PropertyValue[28] := Format('%-d', [NumAmpRatings]);
- TempStr := '[';
- for j := 1 to NumAmpRatings do
- TempStr := TempStr + floattoStrf(AmpRatings[j - 1], ffgeneral, 8, 4) + ',';
- TempStr := TempStr + ']';
- PropertyValue[29] := TempStr;
-
- PropertyValue[NumPropsThisClass + 1] := Format('%-g', [Normamps]);
- PropertyValue[NumPropsThisClass + 2] := Format('%-g', [EmergAmps]);
- // commented out 8/26/2014
- // PropertyValue[NumPropsThisClass + 3] := Format('%-g', [FaultRate]);
- // PropertyValue[NumPropsThisClass + 4] := Format('%-g', [PctPerm]);
- // PropertyValue[NumPropsThisClass + 5] := Format('%-g', [HrsToRepair]);
+ Result := TwoPi * obj.BaseFrequency * 1.0e-9;
+ if getter then
+ begin
+ if obj.GeometrySpecified or obj.SpacingSpecified then
+ Result := Result * obj.Len
+ else
+ Result := Result * obj.FUnitsConvert
+ end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLineObj.FetchLineCode(const Code: String);
-var
- LineCodeObj: TLineCodeObj;
- i: Integer;
-
+function GetB1B0Scale(obj: TLineObj): Double; //TODO: why no FUnitsConvert here?
begin
- if DSS.LineCodeClass.SetActive(Code) then
- begin
-
- LineCodeObj := DSS.LineCodeClass.GetActiveObj;
-
- CondCode := LowerCase(Code);
-
- // Frequency compensation takes place in calcYPrim.
- BaseFrequency := LineCodeObj.BaseFrequency;
- {Copy impedances from line code, but do not recalc because symmetrical
- component z's may not match what's in matrix}
- if LineCodeObj.SymComponentsModel then
- begin
- R1 := LineCodeObj.R1;
- X1 := LineCodeObj.X1;
- R0 := LineCodeObj.R0;
- X0 := LineCodeObj.X0;
- C1 := LineCodeObj.C1;
- C0 := LineCodeObj.C0;
- SymComponentsModel := TRUE;
- end
- else
- SymComponentsModel := FALSE;
+ Result := 1 / (TwoPi * obj.BaseFrequency) * 1.0e-6;
+end;
+procedure SetWires(Obj: TObj; Value: TDSSObjectPtr; ValueCount: Integer); forward;
- // Earth return impedances used to compensate for frequency
- Rg := LineCodeObj.Rg;
- Xg := LineCodeObj.Xg;
- rho := LineCodeObj.rho;
- Kxg := Xg / ln(658.5 * sqrt(rho / BaseFrequency));
+procedure TLine.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
+begin
+ Numproperties := NumPropsThisClass;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // list of objects
+ PropertyStructArrayCountOffset := ptruint(@obj.FWireDataSize);
+ //PropertyStructArrayIndexOffset := ptruint(@obj.FActiveCond);
+
+ PropertyType[ord(TProp.tscables)] := TPropertyType.DSSObjectReferenceArrayProperty;
+ PropertyOffset[ord(TProp.tscables)] := ptruint(@obj.FLineWireData);
+ PropertyOffset2[ord(TProp.tscables)] := ptruint(DSS.TSDataClass);
+ PropertyFlags[ord(TProp.tscables)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.tscables)] := ord(TProp.wires);
+
+ PropertyType[ord(TProp.cncables)] := TPropertyType.DSSObjectReferenceArrayProperty;
+ PropertyOffset[ord(TProp.cncables)] := ptruint(@obj.FLineWireData);
+ PropertyOffset2[ord(TProp.cncables)] := ptruint(DSS.CNDataClass);
+ PropertyFlags[ord(TProp.cncables)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.cncables)] := ord(TProp.wires);
+
+ PropertyType[ord(TProp.wires)] := TPropertyType.DSSObjectReferenceArrayProperty;
+ PropertyOffset[ord(TProp.wires)] := ptruint(@obj.FLineWireData);
+ PropertyOffset2[ord(TProp.wires)] := ptruint(DSS.WireDataClass);
+ PropertyWriteFunction[ord(TProp.wires)] := @SetWires;
+ PropertyFlags[ord(TProp.wires)] := [TPropertyFlag.WriteByFunction, TPropertyFlag.FullNameAsArray];
+
+ // matrices
+ PropertyType[ord(TProp.rmatrix)] := TPropertyType.ComplexPartSymMatrixProperty;
+ PropertyOffset[ord(TProp.rmatrix)] := ptruint(@obj.Z);
+ PropertyOffset2[ord(TProp.rmatrix)] := ptruint(@GetZmatScale);
+ PropertyFlags[ord(TProp.rmatrix)] := [TPropertyFlag.ScaledByFunction, TPropertyFlag.RealPart];
+
+ PropertyType[ord(TProp.xmatrix)] := TPropertyType.ComplexPartSymMatrixProperty;
+ PropertyOffset[ord(TProp.xmatrix)] := ptruint(@obj.Z);
+ PropertyOffset2[ord(TProp.xmatrix)] := ptruint(@GetZmatScale);
+ PropertyFlags[ord(TProp.xmatrix)] := [TPropertyFlag.ScaledByFunction, TPropertyFlag.ImagPart];
+
+ PropertyType[ord(TProp.cmatrix)] := TPropertyType.ComplexPartSymMatrixProperty;
+ PropertyOffset[ord(TProp.cmatrix)] := ptruint(@obj.YC);
+ PropertyOffset2[ord(TProp.cmatrix)] := ptruint(@GetYCScale);
+ PropertyFlags[ord(TProp.cmatrix)] := [TPropertyFlag.ScaledByFunction, TPropertyFlag.ImagPart];
+
+ // integer properties
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // enums
+ PropertyType[ord(TProp.units)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.units)] := ptruint(@obj.LengthUnits);
+ PropertyOffset2[ord(TProp.units)] := PtrInt(DSS.UnitsEnum);
+
+ PropertyType[ord(TProp.linetype)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.linetype)] := ptruint(@obj.FLineType);
+ PropertyOffset2[ord(TProp.linetype)] := PtrInt(DSS.LineTypeEnum);
+
+ PropertyType[ord(TProp.EarthModel)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.EarthModel)] := ptruint(@obj.FEarthModel);
+ PropertyOffset2[ord(TProp.EarthModel)] := PtrInt(DSS.EarthModelEnum);
+
+ // object properties
+ PropertyType[ord(TProp.linecode)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.geometry)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.spacing)] := TPropertyType.DSSObjectReferenceProperty;
+
+ PropertyOffset[ord(TProp.linecode)] := ptruint(@obj.LineCodeObj);
+ PropertyOffset[ord(TProp.geometry)] := ptruint(@obj.LineGeometryObj);
+ PropertyOffset[ord(TProp.spacing)] := ptruint(@obj.LineSpacingObj);
+
+ PropertyOffset2[ord(TProp.linecode)] := ptruint(DSS.LineCodeClass);
+ PropertyOffset2[ord(TProp.geometry)] := ptruint(DSS.LineGeometryClass);
+ PropertyOffset2[ord(TProp.spacing)] := ptruint(DSS.LineSpacingClass);
+
+ // double arrays
+ PropertyType[ord(TProp.Ratings)] := TPropertyType.DoubleDArrayProperty;
+ PropertyOffset[ord(TProp.Ratings)] := ptruint(@obj.AmpRatings);
+ PropertyOffset2[ord(TProp.Ratings)] := ptruint(@obj.NumAmpRatings);
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyType[ord(TProp.bus2)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+ PropertyOffset[ord(TProp.bus2)] := 2;
+
+ // boolean properties
+ PropertyType[ord(TProp.Switch)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.Switch)] := ptruint(@obj.IsSwitch);
+
+ PropertyType[ord(TProp.Seasons)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Seasons)] := ptruint(@obj.NumAmpRatings);
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.length)] := ptruint(@obj.Len);
+ PropertyOffset[ord(TProp.r1)] := ptruint(@obj.r1);
+ PropertyOffset[ord(TProp.x1)] := ptruint(@obj.x1);
+ PropertyOffset[ord(TProp.r0)] := ptruint(@obj.r0);
+ PropertyOffset[ord(TProp.x0)] := ptruint(@obj.x0);
+ PropertyOffset[ord(TProp.Rg)] := ptruint(@obj.Rg);
+ PropertyOffset[ord(TProp.Xg)] := ptruint(@obj.Xg);
+ PropertyOffset[ord(TProp.rho)] := ptruint(@obj.Rho);
+ PropertyOffset[ord(TProp.C1)] := ptruint(@obj.C1);
+ PropertyOffset[ord(TProp.C0)] := ptruint(@obj.C0);
+
+ PropertyOffset[ord(TProp.B1)] := ptruint(@obj.C1);
+ PropertyOffset2[ord(TProp.B1)] := ptruint(@GetB1B0Scale);
+ PropertyFlags[ord(TProp.B1)] := [TPropertyFlag.ScaledByFunction, TPropertyFlag.Redundant, TPropertyFlag.ConditionalValue];
+ PropertyRedundantWith[ord(TProp.B1)] := ord(TProp.C1);
+
+ PropertyOffset[ord(TProp.B0)] := ptruint(@obj.C0);
+ PropertyOffset2[ord(TProp.B0)] := ptruint(@GetB1B0Scale);
+ PropertyFlags[ord(TProp.B0)] := [TPropertyFlag.ScaledByFunction, TPropertyFlag.Redundant, TPropertyFlag.ConditionalValue];
+ PropertyRedundantWith[ord(TProp.B0)] := ord(TProp.C0);
+
+ PropertyFlags[ord(TProp.r1)] := [TPropertyFlag.ConditionalValue, TPropertyFlag.ScaledByFunction];
+ PropertyFlags[ord(TProp.x1)] := [TPropertyFlag.ConditionalValue, TPropertyFlag.ScaledByFunction];
+ PropertyFlags[ord(TProp.C1)] := [TPropertyFlag.ConditionalValue, TPropertyFlag.ScaledByFunction];
+ PropertyFlags[ord(TProp.r0)] := [TPropertyFlag.ConditionalValue, TPropertyFlag.ScaledByFunction];
+ PropertyFlags[ord(TProp.x0)] := [TPropertyFlag.ConditionalValue, TPropertyFlag.ScaledByFunction];
+ PropertyFlags[ord(TProp.C0)] := [TPropertyFlag.ConditionalValue, TPropertyFlag.ScaledByFunction];
+
+ // We could also just remove ConditionalValue here and use NaN as
+ // the scale to achieve the same effect
+
+ PropertyOffset3[ord(TProp.r1)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.x1)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.C1)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.B1)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.r0)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.x0)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.C0)] := ptruint(@obj.SymComponentsModel);
+ PropertyOffset3[ord(TProp.B0)] := ptruint(@obj.SymComponentsModel);
+
+ PropertyOffset2[ord(TProp.r1)] := ptruint(@GetZSeqScale);
+ PropertyOffset2[ord(TProp.x1)] := ptruint(@GetZSeqScale);
+ PropertyOffset2[ord(TProp.r0)] := ptruint(@GetZSeqScale);
+ PropertyOffset2[ord(TProp.x0)] := ptruint(@GetZSeqScale);
+ PropertyOffset2[ord(TProp.c1)] := ptruint(@GetCSeqScale);
+ PropertyOffset2[ord(TProp.c0)] := ptruint(@GetCSeqScale);
- FLineCodeUnits := LineCodeObj.Units;
- FLineCodeSpecified := TRUE;
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
+end;
- FUnitsConvert := ConvertLineUnits(FLineCodeUnits, LengthUnits);
+function TLine.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
+begin
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
+end;
- NormAmps := LineCodeObj.NormAmps;
- EmergAmps := LineCodeObj.EmergAmps;
+procedure TLineObj.FetchLineCode();
+var
+ i: Integer;
+begin
+ if LineCodeObj = NIL then
+ Exit;
+
+ // Frequency compensation takes place in calcYPrim.
+ BaseFrequency := LineCodeObj.BaseFrequency;
+ // Copy impedances from line code, but do not recalc because symmetrical
+ // component z's may not match what's in matrix
+ if LineCodeObj.SymComponentsModel then
+ begin
+ R1 := LineCodeObj.R1;
+ X1 := LineCodeObj.X1;
+ R0 := LineCodeObj.R0;
+ X0 := LineCodeObj.X0;
+ C1 := LineCodeObj.C1;
+ C0 := LineCodeObj.C0;
+ SymComponentsModel := TRUE;
+ end
+ else
+ SymComponentsModel := FALSE;
- NumAmpRatings := LineCodeObj.NumAmpRatings;
- setlength(AmpRatings, NumAmpRatings);
- for i := 0 to High(AmpRatings) do
- AmpRatings[i] := LineCodeObj.AmpRatings[i];
+ // Earth return impedances used to compensate for frequency
+ Rg := LineCodeObj.Rg;
+ Xg := LineCodeObj.Xg;
+ rho := LineCodeObj.rho;
+ Kxg := Xg / ln(658.5 * sqrt(rho / BaseFrequency));
- // These three properties should not come from the Linecode
- // But can vary from line section to line section
- // commented out 8/26/2014
- // FaultRate := LineCodeObj.FaultRate;
- // PctPerm := LineCodeObj.PctPerm;
- // HrsToRepair := LineCodeObj.HrsToRepair;
+ FLineCodeUnits := LineCodeObj.Units;
+ FUnitsConvert := ConvertLineUnits(FLineCodeUnits, LengthUnits);
- UpdatePDProperties;
+ NormAmps := LineCodeObj.NormAmps;
+ EmergAmps := LineCodeObj.EmergAmps;
+ NumAmpRatings := LineCodeObj.NumAmpRatings;
+ setlength(AmpRatings, NumAmpRatings);
+ for i := 0 to High(AmpRatings) do
+ AmpRatings[i] := LineCodeObj.AmpRatings[i];
- if Fnphases <> LineCodeObj.FNphases then
- begin
- NPhases := LineCodeObj.FNPhases;
+ // These three properties should not come from the Linecode
+ // But can vary from line section to line section
+ // commented out 8/26/2014
+ // FaultRate := LineCodeObj.FaultRate;
+ // PctPerm := LineCodeObj.PctPerm;
+ // HrsToRepair := LineCodeObj.HrsToRepair;
- ReallocZandYcMatrices;
- end;
+ SetAsNextSeq(ord(TProp.Ratings));
- if not SymComponentsModel then
- begin // Copy matrices
- Z.CopyFrom(LineCodeObj.Z);
- {Zinv.CopyFrom(LineCodeObj.Zinv);} // no need to copy Zinv
- Yc.CopyFrom(LineCodeObj.Yc);
- end
- else
- RecalcElementData; // Compute matrices
+ if Fnphases <> LineCodeObj.FNphases then
+ begin
+ FNPhases := LineCodeObj.FNPhases;
- NConds := Fnphases; // Force Reallocation of terminal info
- //Fnconds := Fnphases;
- Yorder := Fnconds * Fnterms;
- // YPrimInvalid := True; (set in Edit; this is redundant)
+ ReallocZandYcMatrices;
+ end;
- FLineType := LineCodeObj.FLineType;
+ if not SymComponentsModel then
+ begin // Copy matrices
+ Z.CopyFrom(LineCodeObj.Z);
+ // Zinv.CopyFrom(LineCodeObj.Zinv); // no need to copy Zinv
+ Yc.CopyFrom(LineCodeObj.Yc);
end
else
- DoSimpleMsg('Line Code:' + Code + ' not found.', 180);
+ RecalcElementData; // Compute matrices
-end;
+ NConds := Fnphases; // Force Reallocation of terminal info
+ //Fnconds := Fnphases;
+ Yorder := Fnconds * Fnterms;
+ // YPrimInvalid := True; (set in Edit; this is redundant)
+ FLineType := LineCodeObj.FLineType;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLine.DoRmatrix;
-var
- OrderFound, Norder, j: Integer;
- MatBuffer: pDoubleArray;
- Zvalues: pComplexArray;
+ KillSpacingSpecified;
+ KillGeometrySpecified;
+end;
+procedure TLineObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
begin
- with DSS.ActiveLineObj do
- begin
- {Added 3-17-15 in case Z and Yc do not get allocated to the proper value}
- if Z.Order <> Fnphases then
- ReallocZandYcMatrices;
-
- MatBuffer := Allocmem(Sizeof(Double) * Fnphases * Fnphases);
- OrderFound := Parser.ParseAsSymMatrix(Fnphases, MatBuffer);
-
- if OrderFound > 0 then // Parse was successful
- begin {R}
- ZValues := Z.GetValuesArrayPtr(Norder);
- if Norder = Fnphases then
- for j := 1 to Fnphases * Fnphases do
- ZValues^[j].Re := MatBuffer^[j];
+ case Idx of
+ ord(TProp.C1), ord(TProp.C0), ord(TProp.cmatrix), ord(TProp.B1), ord(TProp.B0):
+ FCapSpecified := TRUE;
+ ord(TProp.cncables):
+ begin
+ LineCodeObj := NIL;
+ KillGeometrySpecified;
+ FPhaseChoice := ConcentricNeutral;
+ end;
+ ord(TProp.tscables):
+ begin
+ LineCodeObj := NIL;
+ KillGeometrySpecified;
+ FPhaseChoice := TapeShield;
+ end;
+ ord(TProp.units):
+ begin // Update units conversion factor that might have been changed previously
+ if LineCodeSpecified then
+ FUnitsConvert := ConvertLineUnits(FLineCodeUnits, LengthUnits)
+ else
+ FUnitsConvert := FUnitsConvert * ConvertLineUnits(previousIntVal, LengthUnits);
+
+ UserLengthUnits := LengthUnits;
end;
-
- Freemem(MatBuffer, Sizeof(Double) * Fnphases * Fnphases);
end;
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLine.DoXmatrix;
-var
- OrderFound, Norder, j: Integer;
- MatBuffer: pDoubleArray;
- Zvalues: pComplexArray;
-
-begin
- with DSS.ActiveLineObj do
- begin
- if Z.Order <> Fnphases then
- ReallocZandYcMatrices;
-
- MatBuffer := Allocmem(Sizeof(Double) * Fnphases * Fnphases);
- OrderFound := Parser.ParseAsSymMatrix(Fnphases, MatBuffer);
- if OrderFound > 0 then // Parse was successful
- begin {X}
- ZValues := Z.GetValuesArrayPtr(Norder);
- if Norder = Fnphases then
- for j := 1 to Fnphases * Fnphases do
- ZValues^[j].im := MatBuffer^[j];
+ case Idx of
+ ord(TProp.linecode):
+ FetchLineCode(); // Define line by conductor code
+ ord(TProp.length), ord(TProp.units): // for Reliability calcs -- see PDElement.Pas
+ MilesThisLine := len * ConvertLineUnits(LengthUnits, UNITS_MILES);
+ ord(TProp.phases): // Change the number of phases ... only valid if SymComponentsModel=TRUE
+ if Fnphases <> previousIntVal then
+ begin
+ if (not GeometrySpecified) and SymComponentsModel then
+ begin
+ NConds := Fnphases; // Force Reallocation of terminal info
+ Yorder := Fnterms * Fnconds;
+ // YPrimInvalid := True; // now set below
+ RecalcElementData; // Reallocate Z, etc.
+ end
+ else
+ begin
+ // ignore change of nphases if geometry used
+ FNphases := previousIntVal;
+ DoSimpleMsg('Illegal change of number of phases for "%s"', [FullName], 18101);
+ end;
+ end;
+ ord(TProp.r1),
+ ord(TProp.x1),
+ ord(TProp.r0),
+ ord(TProp.x0),
+ ord(TProp.C1),
+ ord(TProp.C0),
+ ord(TProp.B1),
+ ord(TProp.B0):
+ begin
+ LineCodeObj := NIL;
+ KillGeometrySpecified;
+ KillSpacingSpecified;
+ ResetLengthUnits;
+ SymComponentsChanged := TRUE;
+ SymComponentsModel := TRUE;
end;
-
- Freemem(MatBuffer, Sizeof(Double) * Fnphases * Fnphases);
+ ord(TProp.rmatrix), ord(TProp.xmatrix), ord(TProp.cmatrix):
+ begin
+ LineCodeObj := NIL;
+ SymComponentsModel := FALSE;
+ ResetLengthUnits;
+ KillGeometrySpecified;
+ KillSpacingSpecified;
+ end;
+ ord(TProp.Switch):
+ if IsSwitch then
+ begin
+ SymComponentsChanged := TRUE;
+ YprimInvalid := TRUE;
+ KillGeometrySpecified();
+ KillSpacingSpecified();
+ r1 := 1.0;
+ x1 := 1.0;
+ r0 := 1.0;
+ x0 := 1.0;
+ c1 := 1.1 * 1.0e-9;
+ c0 := 1.0 * 1.0e-9;
+ len := 0.001;
+ ResetLengthUnits;
+ end;
+ ord(TProp.Xg), ord(TProp.rho):
+ Kxg := Xg / ln(658.5 * sqrt(rho / BaseFrequency));
+ ord(TProp.geometry):
+ FetchGeometryCode();
+ ord(TProp.spacing), ord(TProp.wires), ord(TProp.cncables), ord(TProp.tscables):
+ begin
+ if Idx = ord(TProp.spacing) then
+ FetchLineSpacing();
+ if Assigned(LineSpacingObj) and Assigned(FLineWireData) then
+ begin
+ SymComponentsModel := FALSE;
+ SymComponentsChanged := FALSE;
+ KillGeometrySpecified;
+ end;
+ YprimInvalid := TRUE;
+ end;
+ ord(TProp.Seasons):
+ setlength(AmpRatings, NumAmpRatings);
end;
-end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TLine.DoCmatrix;
-var
- OrderFound,
- Norder,
- j: Integer;
- MatBuffer: pDoubleArray;
- Yvalues: pComplexArray;
- Factor: Double;
-
-begin
- with DSS.ActiveLineObj do
- begin
- if Z.Order <> Fnphases then
- ReallocZandYcMatrices;
-
- MatBuffer := Allocmem(Sizeof(Double) * Fnphases * Fnphases);
- OrderFound := Parser.ParseAsSymMatrix(Fnphases, MatBuffer);
-
- if OrderFound > 0 then // Parse was successful
- begin {X}
- Factor := TwoPi * BaseFrequency * 1.0e-9;
- YValues := YC.GetValuesArrayPtr(Norder);
- if Norder = Fnphases then
- for j := 1 to Fnphases * Fnphases do
- YValues^[j].im := Factor * MatBuffer^[j];
+ case Idx of
+ 3..14: // TODO: check -- looks incomplete
+ //YPrim invalidation on anything that changes impedance values
+ YprimInvalid := TRUE;
+ ord(TProp.rho):
+ begin
+ FrhoSpecified := TRUE;
+ if GeometrySpecified then
+ LineGeometryObj.rhoearth := rho; // TODO: This is weird
end;
-
- Freemem(MatBuffer, Sizeof(Double) * Fnphases * Fnphases);
end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLine.Edit: Integer;
-
-// A Line Defaults to 3-phases and some typical symmetrical component data
-{
- Line impedances are specified in per unit length and are multiplied by the length
- when the primitive Y matrix is computed.
-
- You may specify the impedances of the line either by symmetrical components or
- by R, X, and nodal C matrices (also per unit length).
-
- All C's is entered in nano farads.
-
- The ultimate values are in the matrices. If you specify matrices, then the symmetrical
- component values are ignored. However, if you change any of the symmetrical component values
- the matrices will be recomputed. It is assumed you want to use symmetrical component values.
- Don't mix data entry by matrix and by symmetrical components.
-
- Note that if you change the number of phases, the matrices are reallocated and reinitialized
- with whatever is currently in the symmetrical component data.
+function TLine.EndEdit(ptr: Pointer; const NumChanges: integer): Boolean;
+begin
+ // We need to override this one since Line doesn't call ReCalcData in
+ // the original codebase
+ Exclude(TDSSObject(ptr).Flags, Flg.EditionActive);
+ Result := True;
+end;
-}
+procedure SetWires(Obj: TObj; Value: TDSSObjectPtr; ValueCount: Integer);
var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
- NewLengthUnits: Integer;
-
+ RatingsInc: Boolean;
+ NewNumRat, i, istart: Integer;
+ NewRatings: array of Double;
begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveLineObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveLineObj; // use property to set this value
-
- with DSS.ActiveLineObj do
+ with Obj do
begin
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
+ // Previously in "FetchWireList"
+ if not assigned(LineSpacingObj) then
begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
+ DoSimpleMsg('You must assign the LineSpacing before the Wires Property ("%s").', [FullName], 18102);
+ Exit;
+ end;
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "Line.' + Name + '"', 181);
- 1:
- Setbus(1, param);
- 2:
- Setbus(2, param);
- 3:
- FetchLineCode(Param); // Define line by conductor code
- 4:
- Len := Parser.DblValue;
- 5:
-{Nphases: See below};
- 6:
- r1 := Parser.Dblvalue;
- 7:
- x1 := Parser.Dblvalue;
- 8:
- r0 := Parser.Dblvalue;
- 9:
- x0 := Parser.Dblvalue;
- 10:
- begin
- c1 := Parser.Dblvalue * 1.0e-9;
- FCapSpecified := TRUE;
- end; // Convert from nano to farads
- 11:
- begin
- c0 := Parser.Dblvalue * 1.0e-9;
- FCapSpecified := TRUE;
- end;
- 12:
- DoRmatrix;
- 13:
- DoXmatrix;
- 14:
- begin
- DoCMatrix;
- FCapSpecified := TRUE;
- end;
- 15:
- IsSwitch := InterpretYesNo(Param);
- 16:
- Rg := Parser.DblValue;
- 17:
- Xg := Parser.DblValue;
- 18:
- begin
- rho := Parser.DblValue;
- FrhoSpecified := TRUE;
- end;
- 19:
- FetchGeometryCode(Param);
- 20:
- begin // Update units conversion factor that might have been changed previously
- NewLengthUnits := GetUnitsCode(Param);
- if FLineCodeSpecified then
- FUnitsConvert := ConvertLineUnits(FLineCodeUnits, NewLengthUnits)
- else
- FUnitsConvert := FUnitsConvert * ConvertLineUnits(LengthUnits, NewLengthUnits);
- LengthUnits := NewLengthUnits;
- UserLengthUnits := LengthUnits;
- end;
- 21:
- FetchLineSpacing(Param);
- 22:
- FetchWireList(Param);
- 23:
- FEarthModel := InterpretEarthModel(Param);
- 24:
- FetchCNCableList(Param);
- 25:
- FetchTSCableList(Param);
- 26:
- begin
- c1 := Parser.Dblvalue / (twopi * BaseFrequency) * 1.0e-6;
- FCapSpecified := TRUE;
- end;
- 27:
- begin
- c0 := Parser.Dblvalue / (twopi * BaseFrequency) * 1.0e-6;
- FCapSpecified := TRUE;
- end;
- 28:
- begin
- NumAmpRatings := Parser.IntValue;
- setlength(AmpRatings, NumAmpRatings);
- end;
- 29:
- begin
- setlength(AmpRatings, NumAmpRatings);
- Param := Parser.StrValue;
- NumAmpRatings := InterpretDblArray(Param, NumAmpRatings, Pointer(AmpRatings));
- end;
- 30:
- FLineType := LineTypeList.Getcommand(Param);
- else
- // Inherited Property Edits
- ClassEdit(DSS.ActiveLineObj, ParamPointer - NumPropsThisClass)
- end;
+ if FPhaseChoice = Unknown then
+ begin // it's an overhead line
+ LineCodeObj := NIL;
+ KillGeometrySpecified;
+ istart := 1;
+ FPhaseChoice := Overhead;
+ end
+ else
+ begin // adding bare neutrals to an underground line - TODO what about repeat invocation?
+ istart := LineSpacingObj.NPhases + 1;
+ end;
- // Side Effects ...
- case ParamPointer of
- 3:
- begin
- SpacingSpecified := FALSE;
- if GeometrySpecified = TRUE then
- KillGeometrySpecified;
- GeometrySpecified := FALSE;
- end;
- 4, 20: // for Reliability calcs -- see PDElement.Pas
- MilesThisLine := len * ConvertLineUnits(LengthUnits, UNITS_MILES);
-
- 5: {Change the number of phases ... only valid if SymComponentsModel=TRUE}
- if Fnphases <> Parser.IntValue then
- if (not GeometrySpecified) and SymComponentsModel then
- begin // ignore change of nphases if geometry used
- Nphases := Parser.IntValue;
- NConds := Fnphases; // Force Reallocation of terminal info
- Yorder := Fnterms * Fnconds;
- {YPrimInvalid := True;} // now set below
- RecalcElementData; // Reallocate Z, etc.
- end
- else
- begin
- DoSimpleMsg('Illegal change of number of phases for Line.' + Name, 18101);
- end;
- 6..11, 26..27:
- begin
- FLineCodeSpecified := FALSE;
- KillGeometrySpecified;
- KillSpacingSpecified;
- ResetLengthUnits;
- SymComponentsChanged := TRUE;
- SymComponentsModel := TRUE;
- end;
- 12..14:
- begin
- FLineCodeSpecified := FALSE;
- SymComponentsModel := FALSE;
- ResetLengthUnits;
- KillGeometrySpecified;
- KillSpacingSpecified;
- end;
- 15:
- if IsSwitch then
- begin
- SymComponentsChanged := TRUE;
- YprimInvalid := TRUE;
- GeometrySpecified := FALSE;
- SpacingSpecified := FALSE;
- r1 := 1.0;
- x1 := 1.0;
- r0 := 1.0;
- x0 := 1.0;
- c1 := 1.1 * 1.0e-9;
- c0 := 1.0 * 1.0e-9;
- len := 0.001;
- ResetLengthUnits;
- end;
+ NewNumRat := 1;
+ RatingsInc := FALSE; // So far we don't know if there are seasonal ratings
- 17..18:
- Kxg := Xg / ln(658.5 * sqrt(rho / BaseFrequency));
- 19:
- begin
- GeometrySpecified := TRUE;
- SymComponentsModel := FALSE;
- SymComponentsChanged := FALSE;
- end;
- 21..22, 24..25:
- begin
- if Assigned(FLineSpacingObj) and Assigned(FLineWireData) then
- begin
- SpacingSpecified := TRUE;
- SymComponentsModel := FALSE;
- SymComponentsChanged := FALSE;
- KillGeometrySpecified;
- end;
- YprimInvalid := TRUE;
- end;
- else
- end;
+ // Validate number of elements
+ if (LineSpacingObj.NWires - istart + 1) <> ValueCount then
+ begin
+ DoSimpleMsg('%s: Unexpected number (%d) of wires; expected %d objects.',
+ [FullName, ValueCount, (LineSpacingObj.NWires - istart + 1)], 18102);
+ Exit;
+ end;
- //YPrim invalidation on anything that changes impedance values
- case ParamPointer of
- 3..14:
- YprimInvalid := TRUE;
- 18:
- if GeometrySpecified and assigned(FLineGeometryObj) then
- FlineGeometryObj.rhoearth := rho;
- else
+ for i := istart to LineSpacingObj.NWires do
+ begin
+ FLineWireData[i] := TConductorDataObj(Value^);
+ if FLineWireData[i].NumAmpRatings > NewNumRat then
+ begin
+ NewNumRat := FLineWireData[i].NumAmpRatings;
+ NewRatings := Copy(FLineWireData[i].AmpRatings, 0, NewNumRat);
+ RatingsInc := TRUE; // Yes, there are seasonal ratings
end;
+ NormAmps := FLineWireData[i].NormAmps;
+ EmergAmps := FLineWireData[i].EmergAmps;
+ Inc(Value);
+ end;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ if RatingsInc then
+ begin
+ NumAmpRatings := NewNumRat;
+ AmpRatings := NewRatings;
end;
- // If SymComponentsChanged THEN RecalcElementData;
+ SetAsNextSeq(ord(TProp.Ratings));
end;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TLine.MakeLike(const LineName: String): Integer;
+// A Line Defaults to 3-phases and some typical symmetrical component data
+//
+// Line impedances are specified in per unit length and are multiplied by the length
+// when the primitive Y matrix is computed.
+//
+// You may specify the impedances of the line either by symmetrical components or
+// by R, X, and nodal C matrices (also per unit length).
+//
+// All C's is entered in nano farads.
+//
+// The ultimate values are in the matrices. If you specify matrices, then the symmetrical
+// component values are ignored. However, if you change any of the symmetrical component values
+// the matrices will be recomputed. It is assumed you want to use symmetrical component values.
+// Don't mix data entry by matrix and by symmetrical components.
+//
+// Note that if you change the number of phases, the matrices are reallocated and reinitialized
+// with whatever is currently in the symmetrical component data.
+
+procedure TLineObj.MakeLike(OtherPtr: Pointer);
var
- OtherLine: TLineObj;
- i: Integer;
-
+ Other: TObj;
begin
- Result := 0;
- {See if we can find this line name in the present collection}
- OtherLine := Find(LineName);
- if OtherLine <> NIL then
- with DSS.ActiveLineObj do
- begin
+ inherited MakeLike(OtherPtr); // Take care of inherited class properties
- if Fnphases <> OtherLine.Fnphases then
- begin
- Nphases := OtherLine.Fnphases;
- NConds := Fnphases; // force reallocation of terminals and conductors
-
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
-
- if Z <> NIL then
- Z.Free;
- if Zinv <> NIL then
- Zinv.Free;
- if Yc <> NIL then
- Yc.Free;
-
- // For a line, nphases = ncond, for now
- Z := TCmatrix.CreateMatrix(Fnphases);
- Zinv := TCMatrix.CreateMatrix(Fnphases);
- Yc := TCMatrix.CreateMatrix(Fnphases);
- end;
+ Other := TObj(OtherPtr);
+ if Fnphases <> Other.Fnphases then
+ begin
+ FNphases := Other.Fnphases;
+ NConds := Fnphases; // force reallocation of terminals and conductors
- Z.CopyFrom(OtherLine.Z);
- // Zinv.CopyFrom(OtherLine.Zinv);
- Yc.CopyFrom(OtherLine.Yc);
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
- R1 := OtherLine.R1;
- X1 := OtherLine.X1;
- R0 := OtherLine.R0;
- X0 := OtherLine.X0;
- C1 := OtherLine.C1;
- C0 := OtherLine.C0;
- Len := OtherLine.Len;
+ if Z <> NIL then
+ Z.Free;
+ if Zinv <> NIL then
+ Zinv.Free;
+ if Yc <> NIL then
+ Yc.Free;
- SymComponentsModel := OtherLine.SymComponentsModel;
- FCapSpecified := OtherLine.FCapSpecified;
+ // For a line, nphases = ncond, for now
+ Z := TCmatrix.CreateMatrix(Fnphases);
+ Zinv := TCMatrix.CreateMatrix(Fnphases);
+ Yc := TCMatrix.CreateMatrix(Fnphases);
+ end;
- ClassMakeLike(OtherLine); // Take care of inherited class properties
+ Z.CopyFrom(Other.Z);
+ // Zinv.CopyFrom(Other.Zinv);
+ Yc.CopyFrom(Other.Yc);
- for i := 1 to ParentClass.NumProperties do
- FPropertyValue^[i] := OtherLine.FPropertyValue^[i];
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Line MakeLike: "' + LineName + '" Not Found.', 182);
+ R1 := Other.R1;
+ X1 := Other.X1;
+ R0 := Other.R0;
+ X0 := Other.X0;
+ C1 := Other.C1;
+ C0 := Other.C0;
+ Len := Other.Len;
+ SymComponentsModel := Other.SymComponentsModel;
+ FCapSpecified := Other.FCapSpecified;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TLine Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
constructor TLineObj.Create(ParClass: TDSSClass; const LineName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(LineName);
+ Name := AnsiLowerCase(LineName);
DSSObjType := ParClass.DSSClassType; // DSSObjType + LINESECTION; // in both PDElement list and Linesection lists
- Nphases := 3; // Directly set conds and phases
+ FNphases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 2; // Force allocation of terminals and conductors
IsSwitch := FALSE;
@@ -895,7 +762,7 @@ constructor TLineObj.Create(ParClass: TDSSClass; const LineName: String);
Z := NIL;
Zinv := NIL;
Yc := NIL;
- CondCode := '';
+ LineCodeObj := NIL;
Rg := 0.01805; //ohms per 1000 ft
Xg := 0.155081;
@@ -914,28 +781,20 @@ constructor TLineObj.Create(ParClass: TDSSClass; const LineName: String);
SymComponentsChanged := FALSE;
SymComponentsModel := TRUE;
- GeometrySpecified := FALSE;
- GeometryCode := '';
+ LineGeometryObj := NIL;
LengthUnits := UNITS_NONE; // Assume everything matches
UserLengthUnits := UNITS_NONE;
FUnitsConvert := 1.0;
FLineCodeUnits := UNITS_NONE;
- FLineCodeSpecified := FALSE;
FEarthModel := DSS.DefaultEarthModel;
FLineType := 1; // Default to OH Line
- SpacingSpecified := FALSE;
- FLineSpacingObj := NIL;
+ LineSpacingObj := NIL;
FLineWireData := NIL;
FWireDataSize := 0;
FPhaseChoice := Unknown;
- SpacingCode := '';
FZFrequency := -1.0; // indicate Z not computed.
- FLineGeometryObj := NIL;
-
- InitPropertyValues(0);
-
Yorder := Fnterms * Fnconds;
RecalcElementData;
@@ -943,13 +802,9 @@ constructor TLineObj.Create(ParClass: TDSSClass; const LineName: String);
NumAmpRatings := 1;
setlength(AmpRatings, NumAmpRatings);
AmpRatings[0] := NormAmps;
-
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TLineObj.Destroy;
-
begin
if Assigned(Z) then
Z.Free;
@@ -961,9 +816,11 @@ destructor TLineObj.Destroy;
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TLineObj.ReallocZandYcMatrices;
begin
+ if (Z <> NIL) and (Z.Order = Fnphases) then
+ Exit;
+
if Z <> NIL then
Z.Free;
if Zinv <> NIL then
@@ -979,78 +836,70 @@ procedure TLineObj.ReallocZandYcMatrices;
procedure TLineObj.DoLongLine(Frequency: Double);
// do long line correction for len and frequwnen
-
var
Zs, Zm, Ys, Ym: Complex;
GammaL, ExpP, ExpM, Exp2P, Exp2M, SinhGL, Tanh2GL: Complex;
-
begin
-
- // nominal PI parameters per unit length but Len variable is used here
+ // nominal PI parameters per unit length but Len variable is used here
Zs := cmplx(R1, X1);
Ys := cmplx(0.0, TwoPi * Frequency * C1);
- // apply the long-line correction to obtain Zm and Ym
- GammaL := Csqrt(Cmul(Zs, Ys));
- GammaL := CmulReal(GammaL, Len);
- ExpP := CmulReal(cmplx(cos(GammaL.im), sin(GammaL.im)), exp(GammaL.re));
- Exp2P := CmulReal(cmplx(cos(0.5 * GammaL.im), sin(0.5 * GammaL.im)), exp(0.5 * GammaL.re));
+ // apply the long-line correction to obtain Zm and Ym
+ GammaL := Csqrt(Zs * Ys);
+ GammaL := GammaL * Len;
+ ExpP := cmplx(cos(GammaL.im), sin(GammaL.im)) * exp(GammaL.re);
+ Exp2P := cmplx(cos(0.5 * GammaL.im), sin(0.5 * GammaL.im)) * exp(0.5 * GammaL.re);
ExpM := Cinv(ExpP);
Exp2M := Cinv(Exp2P);
- SinhGL := CmulReal(Csub(ExpP, ExpM), 0.5);
- Tanh2GL := Cdiv(Csub(Exp2P, Exp2M), Cadd(Exp2P, Exp2M));
- Zm := Cdiv(Cmul(CMulReal(Zs, Len), SinhGL), GammaL);
- Ym := Cdiv(Cmul(CMulReal(Ys, Len), Tanh2GL), CmulReal(GammaL, 0.5));
- // rely on this function being called only once, unless R1, X1, or C1 changes
+ SinhGL := (ExpP - ExpM) * 0.5;
+ Tanh2GL := (Exp2P - Exp2M) / (Exp2P + Exp2M);
+ Zm := (Zs * Len * SinhGL) / GammaL;
+ Ym := (Ys * Len * Tanh2GL) / (GammaL * 0.5);
+ // rely on this function being called only once, unless R1, X1, or C1 changes
R1 := Zm.re / Len;
X1 := Zm.im / Len;
C1 := Ym.im / Len / TwoPi / Frequency;
-
end;
procedure TLineObj.RecalcElementData;
-
-{
- This routine is only called when the symmetrical component data have changed
- It computes the values for Z and Yc in ohms per unit length
-
- Can also compute long line correction for 1-phase pos sequence line models
-}
+// This routine is only called when the symmetrical component data have changed
+// It computes the values for Z and Yc in ohms per unit length
+//
+// Can also compute long line correction for 1-phase pos sequence line models
var
Zs, Zm, Ys, Ym, Ztemp: Complex;
i, j: Integer;
Yc1, Yc0, OneThird: Double;
begin
-
ReallocZandYcMatrices;
OneThird := 1.0 / 3.0; // Do this to get more precision in next few statements
- {Only time this is called is if symmetrical components are specified}
+ // Only time this is called is if symmetrical components are specified
- Ztemp := CmulReal(cmplx(R1, X1), 2.0);
- {Handle special case for 1-phase line and/or pos seq model }
+ Ztemp := cmplx(R1, X1) * 2.0;
+ // Handle special case for 1-phase line and/or pos seq model
if (FnPhases = 1) or ActiveCircuit.PositiveSequence then
begin
- // long-line equivalent PI, but only for CktModel=Positive
+ // long-line equivalent PI, but only for CktModel=Positive
if ActiveCircuit.PositiveSequence and (C1 > 0) then
begin
DoLongLine(BaseFrequency); // computes R1, X1, C1 per unit length
end;
- // zero sequence the same as positive sequence
+ // zero sequence the same as positive sequence
R0 := R1;
X0 := X1;
C0 := C1;
end;
- Zs := CmulReal(CAdd(Ztemp, Cmplx(R0, X0)), OneThird);
- Zm := CmulReal(Csub(cmplx(R0, X0), Cmplx(R1, X1)), OneThird);
+ Zs := (Ztemp + Cmplx(R0, X0)) * OneThird;
+ Zm := (cmplx(R0, X0) - Cmplx(R1, X1)) * OneThird;
Yc1 := TwoPi * BaseFrequency * C1;
Yc0 := TwoPi * BaseFrequency * C0;
- Ys := CMulReal(Cadd(CMulReal(Cmplx(0.0, Yc1), 2.0), Cmplx(0.0, Yc0)), OneThird);
- Ym := CmulReal(Csub(cmplx(0.0, Yc0), Cmplx(0.0, Yc1)), OneThird);
+ Ys := (Cmplx(0.0, Yc1) * 2 + Cmplx(0.0, Yc0)) * OneThird;
+ Ym := (cmplx(0.0, Yc0) - Cmplx(0.0, Yc1)) * OneThird;
for i := 1 to Fnphases do
begin
@@ -1066,20 +915,16 @@ procedure TLineObj.RecalcElementData;
SymComponentsChanged := FALSE;
// values in ohms per unit length
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TLineObj.CalcFltRate;
begin
- // inherited;
-
- // Assume Faultrate specified in same units as length
+ // inherited;
+ // Assume Faultrate specified in same units as length
BranchFltRate := Faultrate * pctperm * 0.01 * Len;
end;
procedure TLineObj.CalcYPrim;
-
var
Value: Complex;
ZinvValues: pComplexArray;
@@ -1091,15 +936,14 @@ procedure TLineObj.CalcYPrim;
LengthMultiplier: Double;
i, j, k, Norder: Integer;
-
begin
FreqMultiplier := 1.0;
LengthMultiplier := 1.0;
if SymComponentsChanged then
begin
- {Try to catch inadvertent user error when they forget to specify C1 and C0 }
- {Check to see if user has spec'd C1 and C0. If not, adjust default values for new length units}
+ //Try to catch inadvertent user error when they forget to specify C1 and C0
+ //Check to see if user has spec'd C1 and C0. If not, adjust default values for new length units
if not FCapSpecified then
begin
C1 := C1 / ConvertLineUnits(UNITS_KFT, LengthUnits); // were defined in kft
@@ -1112,15 +956,12 @@ procedure TLineObj.CalcYPrim;
ClearYPrim;
-
// Build Series YPrim
with YPrim_Series do
begin
-
- {Build Zmatrix}
+ // Build Zmatrix
if GeometrySpecified then
begin
-
FMakeZFromGeometry(ActiveCircuit.Solution.Frequency); // Includes length in proper units
if DSS.SolutionAbort then
Exit;
@@ -1129,11 +970,9 @@ procedure TLineObj.CalcYPrim;
else
if SpacingSpecified then
begin
-
FMakeZFromSpacing(ActiveCircuit.Solution.Frequency); // Includes length in proper units
if DSS.SolutionAbort then
Exit;
-
end
else
begin // Z is from line code or specified in line data
@@ -1142,13 +981,13 @@ procedure TLineObj.CalcYPrim;
FYprimFreq := ActiveCircuit.Solution.Frequency;
FreqMultiplier := FYprimFreq / BaseFrequency;
- { Put in Series RL }
+ // Put in Series RL
ZValues := Z.GetValuesArrayPtr(Norder);
ZinvValues := Zinv.GetValuesArrayPtr(Norder);
- // Correct the impedances for length and frequency
- // Rg increases with frequency
- // Xg modified by ln of sqrt(1/f)
+ // Correct the impedances for length and frequency
+ // Rg increases with frequency
+ // Xg modified by ln of sqrt(1/f)
if Xg <> 0.0 then
Xgmod := 0.5 * KXg * ln(FreqMultiplier)
else
@@ -1156,30 +995,28 @@ procedure TLineObj.CalcYPrim;
for i := 1 to Norder * Norder do
ZinvValues^[i] := Cmplx((ZValues^[i].re + Rg * (FreqMultiplier - 1.0)) * LengthMultiplier, (ZValues^[i].im - Xgmod) * LengthMultiplier * FreqMultiplier);
- Zinv.Invert; {Invert Z in place to get values to put in Yprim}
-
-
+ Zinv.Invert; // Invert Z in-place to get values to put in Yprim
end;
- {At this point have Z and Zinv in proper values including length}
- {If GIC simulation, convert Zinv back to sym components, R Only }
+ // At this point have Z and Zinv in proper values including length
+ // If GIC simulation, convert Zinv back to sym components, R Only
if ActiveCircuit.Solution.Frequency < 0.51 then // 0.5 Hz is cutoff
ConvertZinvToPosSeqR;
-
if Zinv.Inverterror > 0 then
begin
- {If error, put in tiny series conductance}
+ // If error, put in tiny series conductance
// TEMc - shut this up for the CDPSM connectivity profile test, or whenever else it gets annoying
- DoErrorMsg('TLineObj.CalcYPrim', 'Matrix Inversion Error for Line "' + Name + '"',
- 'Invalid impedance specified. Replaced with tiny conductance.', 183);
+ DoErrorMsg('TLineObj.CalcYPrim',
+ Format(_('Matrix Inversion Error for Line "%s"'), [Name]),
+ _('Invalid impedance specified. Replaced with tiny conductance.'), 183);
Zinv.Clear;
for i := 1 to Fnphases do
Zinv.SetElement(i, i, Cmplx(epsilon, 0.0));
end
else
- { Now, Put in Yprim_Series matrix }
+ // Now, Put in Yprim_Series matrix
for i := 1 to Fnphases do
begin
for j := 1 to Fnphases do
@@ -1187,20 +1024,18 @@ procedure TLineObj.CalcYPrim;
Value := Zinv.GetElement(i, j);
SetElement(i, j, Value);
SetElement(i + Fnphases, j + Fnphases, Value);
- Value := cnegate(Value);
+ Value := -Value;
SetElemSym(i, j + Fnphases, Value);
end;
end;
- end; {With Yprim_series}
+ end;
YPrim.Copyfrom(Yprim_Series); // Initialize YPrim for series impedances
- // 10/3/2006 moved this to after the copy to Yprim so it doesn't affect normal line model capacitance
- // 3-30-04 ----- Rev 2-4-09 to include both sides of line
- // Increase diagonal elements of both sides of line so that we will avoid isolated bus problem
- // add equivalent of 10 kvar capacitive at 345 kV
+ // Increase diagonal elements of both sides of line so that we will avoid isolated bus problem
+ // add equivalent of 10 kvar capacitive at 345 kV
with Yprim_Series do
for i := 1 to Yorder do
AddElement(i, i, CAP_EPSILON);
@@ -1209,20 +1044,18 @@ procedure TLineObj.CalcYPrim;
if ActiveCircuit.Solution.Frequency > 0.51 then // Skip Capacitance for GIC
with YPrim_Shunt do
begin
-
- {Put half the Shunt Capacitive Admittance at each end}
+ // Put half the Shunt Capacitive Admittance at each end
YValues := Yc.GetValuesArrayPtr(Norder);
if GeometrySpecified or SpacingSpecified then
begin
-
- {Values are already compensated for length and frequency}
+ // Values are already compensated for length and frequency
k := 0;
for j := 1 to Fnphases do
for i := 1 to Fnphases do
begin
Inc(k); // Assume matrix in col order (1,1 2,1 3,1 ...)
- Value := CDivReal(YValues^[k], 2.0); // half at each end ...
+ Value := YValues^[k] / 2.0; // half at each end ...
AddElement(i, j, Value);
AddElement(i + Fnphases, j + Fnphases, Value);
end;
@@ -1230,7 +1063,7 @@ procedure TLineObj.CalcYPrim;
end
else
begin
- {Regular line model - values computed per unit length at base frequency}
+ // Regular line model - values computed per unit length at base frequency
k := 0;
for j := 1 to Fnphases do
for i := 1 to Fnphases do
@@ -1243,34 +1076,34 @@ procedure TLineObj.CalcYPrim;
end;
- {Now Account for Open Conductors}
- {For any conductor that is open, zero out row and column}
+ // Now Account for Open Conductors
+ // For any conductor that is open, zero out row and column
- end; {With YPRIM}
+ end;
YPrim.AddFrom(Yprim_Shunt);
inherited CalcYPrim;
YprimInvalid := FALSE;
-
end;
-procedure TLineObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
+procedure TLineObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i, j: Integer;
rslt: String;
LengthMult: Double;
-
begin
inherited DumpProperties(F, Complete);
-
with ParentClass do
begin
FSWriteln(F, '~ ' + PropertyName^[1] + '=' + firstbus);
FSWriteln(F, '~ ' + PropertyName^[2] + '=' + nextbus);
- FSWriteln(F, '~ ' + PropertyName^[3] + '=' + CondCode);
+ if LineCodeObj <> NIL then
+ FSWriteln(F, '~ ' + PropertyName^[3] + '=' + LineCodeObj.Name)
+ else
+ FSWriteln(F, '~ ' + PropertyName^[3] + '=' + '');
+
FSWriteln(F, Format('~ %s=%d', [PropertyName^[4], len]));
FSWriteln(F, Format('~ %s=%d', [PropertyName^[5], Fnphases]));
if SymComponentsModel then
@@ -1304,7 +1137,7 @@ procedure TLineObj.DumpProperties(F: TFileStream; Complete: Boolean);
rslt := '----';
FSWriteln(F, '~ ' + PropertyName^[11] + '=' + Rslt);
- // If GeometrySpecified Or SpacingSpecified then length is embedded in Z and Yc 4-9-2020
+ // If GeometrySpecified Or SpacingSpecified then length is embedded in Z and Yc 4-9-2020
if GeometrySpecified or SpacingSpecified then
LengthMult := Len
else
@@ -1344,195 +1177,24 @@ procedure TLineObj.DumpProperties(F: TFileStream; Complete: Boolean);
FSWrite(F, '~ ' + PropertyName^[15] + '=');
FSWriteln(F, StrTOrF(IsSwitch));
- {Dump the rest by default}
+ // Dump the rest by default
for i := 16 to NumProperties do
begin
FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
end;
end;
-
end;
-
-{*********** Placeholder for Line module No Load Loss procedure *********}
-procedure TLineObj.GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Complex);
-begin
-
- {For Now, we'll just do the default behavior until we implement shunt losses}
-
- inherited;
-
-end;
-
-function TLineObj.GetPropertyValue(Index: Integer): String;
-var
- k,
- i, j: Integer;
- Factor: Double;
- TempStr: String;
-begin
-
-
- case Index of
- 12..14:
- Result := '[';
-
- else
- Result := '';
- end;
- {Report Impedance values in ohms per unit length of present length units}
- case Index of
- 1:
- Result := GetBus(1);
- 2:
- Result := GetBus(2);
- 4:
- Result := Format('%-.7g', [Len]);
- 5:
- Result := Format('%d', [FNphases]);
- 6:
- if SymComponentsModel then
- Result := Format('%-.7g', [R1 / FUnitsConvert])
- else
- Result := '----';
- 7:
- if SymComponentsModel then
- Result := Format('%-.7g', [X1 / FUnitsConvert])
- else
- Result := '----';
- 8:
- if SymComponentsModel then
- Result := Format('%-.7g', [R0 / FUnitsConvert])
- else
- Result := '----';
- 9:
- if SymComponentsModel then
- Result := Format('%-.7g', [X0 / FUnitsConvert])
- else
- Result := '----';
- 10:
- if SymComponentsModel then
- Result := Format('%-.7g', [C1 * 1.0e9 / FUnitsConvert])
- else
- Result := '----';
- 11:
- if SymComponentsModel then
- Result := Format('%-.7g', [C0 * 1.0e9 / FUnitsConvert])
- else
- Result := '----';
-
- 12:
- for i := 1 to FNconds do // R matrix
- begin
- for j := 1 to i do
- begin // report in per unit Length in length units
- if GeometrySpecified or SpacingSpecified then
- Result := Result + Format('%-.7g', [Z.GetElement(i, j).re / len]) + ' '
- else
- Result := Result + Format('%-.7g', [Z.GetElement(i, j).re / FUnitsConvert]) + ' ';
- end;
- if i < FNconds then
- Result := Result + '|';
- end;
-
- 13:
- for i := 1 to FNconds do // X matrix
- begin
- for j := 1 to i do
- begin
- if GeometrySpecified or SpacingSpecified then
- Result := Result + Format('%-.7g', [Z.GetElement(i, j).im / Len]) + ' '
- else
- Result := Result + Format('%-.7g', [Z.GetElement(i, j).im / FUnitsConvert]) + ' ';
- end;
- if i < FNconds then
- Result := Result + '|';
- end;
-
- 14:
- begin // CMatrix nf
- Factor := TwoPi * BaseFrequency * 1.0e-9;
- for i := 1 to FNconds do
- begin
- for j := 1 to i do
- begin
- if GeometrySpecified or SpacingSpecified then
- Result := Result + Format('%-.7g', [YC.GetElement(i, j).im / Factor / Len]) + ' '
- else
- Result := Result + Format('%-.7g', [YC.GetElement(i, j).im / Factor / FUnitsConvert]) + ' ';
- end;
- if i < FNconds then
- Result := Result + '|';
- end;
- end;
- 15:
- Result := StrTOrF(IsSwitch);
- 16:
- Result := Format('%-g', [Rg]);
- 17:
- Result := Format('%-g', [Xg]);
- 18:
- Result := Format('%-g', [Rho]);
- 20:
- Result := LineUnitsStr(LengthUnits);
- 23:
- Result := GetEarthModel(FEarthModel);
- 26:
- if SymComponentsModel then
- Result := Format('%.7g', [twopi * Basefrequency * C1 * 1.0e6])
- else
- Result := '----';
- 27:
- if SymComponentsModel then
- Result := Format('%.7g', [twopi * Basefrequency * C0 * 1.0e6])
- else
- Result := '----';
- 28:
- Result := inttostr(NumAmpRatings);
- 29:
- begin
- TempStr := '[';
- for k := 1 to NumAmpRatings do
- TempStr := TempStr + floattoStrf(AmpRatings[k - 1], ffGeneral, 8, 4) + ',';
- TempStr := TempStr + ']';
- Result := TempStr;
- end;
- 30:
- if (FLineType >= 1) and (FLineType <= (High(LINE_TYPES) + 1)) then
- Result := LINE_TYPES[FLineType - 1]
- else
- Result := '';
-
- // Intercept FaultRate, PctPerm, and HourstoRepair
- (NumPropsThisClass + 3):
- Result := Format('%-g', [FaultRate]);
-
- (NumPropsThisClass + 4):
- Result := Format('%-g', [PctPerm]);
-
- (NumPropsThisClass + 5):
- Result := Format('%-g', [HrsToRepair]);
-
- else
- Result := inherited GetPropertyValue(index);
- end;
-
- case Index of
- 12..14:
- Result := Result + ']';
-
- else
- end;
-
-
-end;
+// *********** Placeholder for Line module No Load Loss procedure *********
+//procedure TLineObj.GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Complex);
+//begin
+// // For Now, we'll just do the default behavior until we implement shunt losses
+// inherited;
+//end;
procedure TLineObj.GetSeqLosses(var PosSeqLosses, NegSeqLosses, ZeroSeqLosses: complex);
-
-{ Only consider 3-phase branches with Pos seq >> Neg seq
- Otherwise, we don't know whether it is a 3-phase line or just a line with 3 phases
-}
-
+// Only consider 3-phase branches with Pos seq >> Neg seq
+// Otherwise, we don't know whether it is a 3-phase line or just a line with 3 phases
var
i, j, k: Integer;
Vph,
@@ -1540,16 +1202,14 @@ procedure TLineObj.GetSeqLosses(var PosSeqLosses, NegSeqLosses, ZeroSeqLosses: c
I012: array[0..2] of Complex;
begin
-
PosSeqLosses := CZERO;
NegSeqLosses := CZERO;
ZeroSeqLosses := CZERO;
- {Method: sum seq powers going into each terminal
- }
-
+ // Method: sum seq powers going into each terminal
+
if Fnphases = 3 then
- begin {3-phase lines only}
+ begin // 3-phase lines only
ComputeIterminal;
for i := 1 to 2 do
begin
@@ -1558,81 +1218,41 @@ procedure TLineObj.GetSeqLosses(var PosSeqLosses, NegSeqLosses, ZeroSeqLosses: c
Vph[j] := ActiveCircuit.Solution.NodeV^[NodeRef^[k + j]];
Phase2SymComp(pComplexArray(@Vph), pComplexArray(@V012));
Phase2SymComp(pComplexArray(@Iterminal^[k]), pComplexArray(@I012));
- Caccum(PosSeqLosses, Cmul(V012[1], Conjg(I012[1])));
- Caccum(NegSeqLosses, Cmul(V012[2], Conjg(I012[2]))); // accumulate both line modes
- Caccum(ZeroSeqLosses, Cmul(V012[0], Conjg(I012[0])));
+ PosSeqLosses += V012[1] * cong(I012[1]);
+ NegSeqLosses += V012[2] * cong(I012[2]); // accumulate both line modes
+ ZeroSeqLosses += V012[0] * cong(I012[0]);
end;
- cmulrealaccum(PosSeqLosses, 3.0);
- cmulrealaccum(NegSeqLosses, 3.0);
- cmulrealaccum(ZeroSeqLosses, 3.0);
+ PosSeqLosses *= 3.0;
+ NegSeqLosses *= 3.0;
+ ZeroSeqLosses *= 3.0;
end;
-
end;
-procedure TLineObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := Getbus(1);
- PropertyValue[2] := Getbus(2);
- PropertyValue[3] := '';
- PropertyValue[4] := '1.0'; // '5.28'; Changed 2/17/00
- PropertyValue[5] := '3';
- PropertyValue[6] := '.058';
- PropertyValue[7] := '.1206';
- PropertyValue[8] := '.1784';
- PropertyValue[9] := '.4047';
- PropertyValue[10] := '3.4';
- PropertyValue[11] := '1.6';
- PropertyValue[12] := '';
- PropertyValue[13] := '';
- PropertyValue[14] := '';
- PropertyValue[15] := 'false';
- PropertyValue[16] := '0.01805';
- PropertyValue[17] := '0.155081';
- PropertyValue[18] := '100';
- PropertyValue[19] := '';
- PropertyValue[20] := 'NONE';
- PropertyValue[21] := '';
- PropertyValue[22] := '';
- PropertyValue[23] := GetEarthModel(SIMPLECARSON);
- PropertyValue[24] := '';
- PropertyValue[25] := '';
- PropertyValue[26] := '1.2818'; // B1 microS
- PropertyValue[27] := '0.60319'; // B0 microS
- PropertyValue[28] := '1'; // 1 Season
- PropertyValue[29] := '[400]'; // 1 rating
- PropertyValue[30] := 'OH'; // Overhead line default
-
- inherited InitPropertyValues(NumPropsThisClass);
-
- // Override Inherited properties just in case
- PropertyValue[NumPropsThisClass + 1] := '400'; //Normamps
- PropertyValue[NumPropsThisClass + 2] := '600'; //emergamps
- PropertyValue[NumPropsThisClass + 3] := '0.1'; //Fault rate
- PropertyValue[NumPropsThisClass + 4] := '20'; // Pct Perm
- PropertyValue[NumPropsThisClass + 5] := '3'; // Hrs to repair
-
- ClearPropSeqArray;
-end;
-
-procedure TLineObj.MakePosSequence;
+procedure TLineObj.MakePosSequence();
var
- S: String;
C1_new, Cs, Cm: Double;
LengthMult: Double;
Z1, ZS, Zm: Complex;
- i, j: Integer;
+ changes, i, j: Integer;
+ NormAmps0: Double;
+ EmergAmps0: Double;
+ LengthUnits0: Integer;
begin
-// set to single phase and make sure R1, X1, C1 set.
-// If already single phase, let alone
+ NormAmps0 := NormAmps;
+ EmergAmps0 := EmergAmps;
+ LengthUnits0 := LengthUnits;
+
+ // set to single phase and make sure R1, X1, C1 set.
+ // If already single phase, let alone
if FnPhases > 1 then
begin
- // Kill certain propertyvalue elements to get a cleaner looking save
- PrpSequence^[3] := 0;
+ BeginEdit(True);
+ // Kill certain propertyvalue elements to get a cleaner looking save
+ PrpSequence[3] := 0;
for i := 6 to 14 do
- PrpSequence^[i] := 0;
+ PrpSequence[i] := 0;
- // If GeometrySpecified Or SpacingSpecified then length is embedded in Z and Yc 4-9-2020
+ // If GeometrySpecified Or SpacingSpecified then length is embedded in Z and Yc 4-9-2020
if GeometrySpecified or SpacingSpecified then
LengthMult := Len
else
@@ -1640,7 +1260,12 @@ procedure TLineObj.MakePosSequence;
if IsSwitch then
begin
- S := ' R1=1 X1=1 C1=1.1 Phases=1 Len=0.001'
+ SetDouble(ord(TProp.R1), 1);
+ SetDouble(ord(TProp.X1), 1);
+ SetDouble(ord(TProp.C1), 1.1);
+ SetInteger(ord(TProp.Phases), 1);
+ SetDouble(ord(TProp.length), 0.001);
+ changes := 5;
end
else
begin
@@ -1652,21 +1277,21 @@ procedure TLineObj.MakePosSequence;
end
else
begin // matrix was input directly, or built from physical data
- // average the diagonal and off-dialgonal elements
+ // average the diagonal and off-dialgonal elements
Zs := CZERO;
for i := 1 to FnPhases do
- Caccum(Zs, Z.GetElement(i, i));
- Zs := CdivReal(Zs, (Fnphases * LengthMult));
+ Zs += Z.GetElement(i, i);
+ Zs := Zs / (Fnphases * LengthMult);
Zm := CZERO;
for i := 1 to FnPhases - 1 do // Corrected 6-21-04
begin
for j := i + 1 to FnPhases do
- Caccum(Zm, Z.GetElement(i, j));
+ Zm += Z.GetElement(i, j);
end;
- Zm := CdivReal(Zm, (LengthMult * Fnphases * (FnPhases - 1.0) / 2.0));
- Z1 := CSub(Zs, Zm);
+ Zm := Zm / (LengthMult * Fnphases * (FnPhases - 1.0) / 2.0);
+ Z1 := Zs - Zm;
- // Do same for Capacitances
+ // Do same for Capacitances
Cs := 0.0;
for i := 1 to FnPhases do
Cs := Cs + Yc.GetElement(i, i).im;
@@ -1676,63 +1301,63 @@ procedure TLineObj.MakePosSequence;
Cm := Cm + Yc.GetElement(i, j).im;
C1_new := (Cs - Cm) / TwoPi / BaseFrequency / (LengthMult * Fnphases * (FnPhases - 1.0) / 2.0) * 1.0e9; // nanofarads
- // compensate for length units
- Z1 := CDivReal(Z1, FunitsConvert);
+ // compensate for length units
+ Z1 := Z1 / FunitsConvert;
C1_New := C1_New / FunitsConvert;
end;
- S := Format(' R1=%-.5g %-.5g C1=%-.5g Phases=1', [Z1.re, Z1.im, C1_new]);
+ SetDouble(ord(TProp.R1), Z1.re);
+ SetDouble(ord(TProp.X1), Z1.im);
+ SetDouble(ord(TProp.C1), C1_new);
+ SetInteger(ord(TProp.Phases), 1);
+ changes := 4;
end;
- // Conductor Current Ratings
- S := S + Format(' Normamps=%-.5g %-.5g', [NormAmps, EmergAmps]);
- // Repeat the Length Units to compensate for unexpected reset
- S := S + ' Units=' + LineUnitsStr(LengthUnits);
- Parser.CmdString := S;
- Edit;
+ // Conductor Current Ratings
+ SetDouble(NumPropsThisClass + ord(TPDElementProp.NormAmps), NormAmps0);
+ SetDouble(NumPropsThisClass + ord(TPDElementProp.EmergAmps), EmergAmps0);
+ // Repeat the Length Units to compensate for unexpected reset
+ SetInteger(ord(TProp.Units), LengthUnits0);
+ EndEdit(changes + 3);
end;
-
- inherited MakePosSequence;
+ inherited MakePosSequence();
end;
-function TLineObj.MergeWith(var OtherLine: TLineObj; Series: Boolean): Boolean;
-
-// Cleaned up and corrected 3-17-15
-
+function TLineObj.MergeWith(var Other: TLineObj; Series: Boolean): Boolean; //TODO: remember to test this
// Merge this line with another line and disble the other line.
var
Values1, Values2: pComplexArray;
Order1, Order2,
i, j,
Common1, Common2: Integer;
- TotalLen, wnano: Double;
- S, NewName: String;
+ TotalLen: Double;
+ NewName: String;
TestBusNum: Integer;
LenUnitsSaved: Integer;
NewZ: Complex;
LenSelf, LenOther: Double;
-
+ RXC: Array[1..6] of Double;
+ UseRXC: Boolean;
begin
Result := FALSE; // initialize the result
- if OtherLine <> NIL then
+ if Other <> NIL then
begin
- if Fnphases <> OtherLine.Fnphases then
+ if Fnphases <> Other.Fnphases then
Exit; // Can't merge
LenUnitsSaved := LengthUnits;
YPrimInvalid := TRUE;
- // Redefine property values to make it appear that line was defined this way originally using matrices
-
+ // Redefine property values to make it appear that line was defined this way originally using matrices
if Series then
- TotalLen := Len + Otherline.Len * ConvertLineUnits(OtherLine.LengthUnits, LengthUnits)
+ TotalLen := Len + Other.Len * ConvertLineUnits(Other.LengthUnits, LengthUnits)
else
TotalLen := 1.0;
if Series then
begin
- { redefine the bus connections}
+ // redefine the bus connections
- // Find the bus in common between the two lines
+ // Find the bus in common between the two lines
Common1 := 0;
Common2 := 0;
i := 1;
@@ -1741,7 +1366,7 @@ function TLineObj.MergeWith(var OtherLine: TLineObj; Series: Boolean): Boolean;
TestBusNum := ActiveCircuit.MapNodeToBus^[NodeRef^[1 + (i - 1) * Fnconds]].BusRef;
for j := 1 to 2 do
begin
- if ActiveCircuit.MapNodeToBus^[OtherLine.NodeRef^[1 + (j - 1) * OtherLine.Nconds]].BusRef = TestBusNum then
+ if ActiveCircuit.MapNodeToBus^[Other.NodeRef^[1 + (j - 1) * Other.Nconds]].BusRef = TestBusNum then
begin
Common1 := i;
Common2 := j;
@@ -1754,356 +1379,222 @@ function TLineObj.MergeWith(var OtherLine: TLineObj; Series: Boolean): Boolean;
if Common1 = 0 then
Exit; // There's been an error; didn't find anything in common
- {Redefine the bus connections, eliminating the common bus}
+ // Redefine the bus connections, eliminating the common bus
case Common1 of
1:
case Common2 of
1:
- S := 'Bus1="' + OtherLine.GetBus(2) + '"';
+ ParsePropertyValue(ord(TProp.Bus1), Other.GetBus(2));
2:
- S := 'Bus1="' + OtherLine.GetBus(1) + '"';
+ ParsePropertyValue(ord(TProp.Bus1), Other.GetBus(1));
end;
2:
case Common2 of
1:
- S := 'Bus2="' + OtherLine.GetBus(2) + '"';
+ ParsePropertyValue(ord(TProp.Bus2), Other.GetBus(2));
2:
- S := 'Bus2="' + OtherLine.GetBus(1) + '"';
+ ParsePropertyValue(ord(TProp.Bus2), Other.GetBus(1));
end;
end;
+ end; // If Series
- Parser.cmdstring := S;
- Edit;
-
- end; {If Series}
-
- {Rename the line}
+ // Rename the line
if Series then
- NewName := OtherLine.Name + '~' + Name //(GetBus(1)) + '~' + StripExtension(GetBus(2))
+ NewName := Other.Name + '~' + Name //(GetBus(1)) + '~' + StripExtension(GetBus(2))
else
NewName := StripExtension(GetBus(1)) + '||' + StripExtension(GetBus(2));
- {Update ControlElement Connections to This Line}
- UpdateControlElements('line.' + NewName, 'line.' + Name);
- UpdateControlElements('line.' + NewName, 'line.' + OtherLine.Name);
+ // Update ControlElement Connections to This Line
+ UpdateControlElements(self, Other);
Name := NewName;
if Series then
IsSwitch := FALSE; // not allowed on series merge.
- {Now Do the impedances}
+ // Now Do the impedances
LenSelf := Len / FunitsConvert; // in units of the R X Data
- LenOther := OtherLine.Len / OtherLine.FunitsConvert;
-
- { If both lines are Symmmetrical Components, just merge the R1..C0 values}
- { This will catch many 3-phase lines since this is a common way to define lines}
- if SymComponentsModel and OtherLine.SymComponentsModel and (nphases = 3) then
- begin {------------------------- Sym Component Model ----------------------------------}
+ LenOther := Other.Len / Other.FunitsConvert;
+
+ // If both lines are Symmmetrical Components, just merge the R1..C0 values
+ // This will catch many 3-phase lines since this is a common way to define lines
+ if SymComponentsModel and Other.SymComponentsModel and (nphases = 3) then
+ begin // Sym Component Model
+ UseRXC := False;
+ // This reset the length units
+ BeginEdit(True);
if Series then
- begin
- S := ' R1=' + Format('%-g', [(R1 * LenSelf + OtherLine.R1 * LenOther) / TotalLen]); // Ohms per unit length of this line length units
- S := S + Format(' %-g', [(X1 * LenSelf + OtherLine.X1 * LenOther) / TotalLen]);
- S := S + Format(' %-g', [(R0 * LenSelf + OtherLine.R0 * LenOther) / TotalLen]);
- S := S + Format(' %-g', [(X0 * LenSelf + OtherLine.X0 * LenOther) / TotalLen]);
- S := S + Format(' %-g', [(C1 * LenSelf + OtherLine.C1 * LenOther) / TotalLen * 1.0e9]);
- S := S + Format(' %-g', [(C0 * LenSelf + OtherLine.C0 * LenOther) / TotalLen * 1.0e9]);
+ begin // Ohms per unit length of this line length units
+ RXC[1] := (R1 * LenSelf + Other.R1 * LenOther) / TotalLen; // R1
+ RXC[2] := (X1 * LenSelf + Other.X1 * LenOther) / TotalLen; // X1
+ RXC[3] := (R0 * LenSelf + Other.R0 * LenOther) / TotalLen; // R0
+ RXC[4] := (X0 * LenSelf + Other.X0 * LenOther) / TotalLen; // X0
+ RXC[5] := (C1 * LenSelf + Other.C1 * LenOther) / TotalLen * 1.0e9; // C1
+ RXC[6] := (C0 * LenSelf + Other.C0 * LenOther) / TotalLen * 1.0e9; // C0
+ UseRXC := True;
end
- else {parallel}
+ else // parallel
begin
if IsSwitch then
- S := '' {Leave as is if switch; just dummy z anyway}
+ begin
+ // Leave as is if switch; just dummy z anyway
+ end
else
- if OtherLine.IsSwitch then
- S := ' switch=yes' {This will take care of setting Z's}
+ if Other.IsSwitch then
+ SetInteger(ord(TProp.Switch), 1)
else
begin
-{********* Will This work with Length multiplier? did it ever work? *************************}
- NewZ := ParallelZ(Cmplx(R1 * Len, X1 * Len), Cmplx(OtherLine.R1 * OtherLine.Len, OtherLine.X1 * OtherLine.Len));
- S := ' R1=' + Format('%-g %-g ', [NewZ.Re, NewZ.im]);
- NewZ := ParallelZ(Cmplx(R0 * Len, X0 * Len), Cmplx(OtherLine.R0 * OtherLine.Len, OtherLine.X0 * OtherLine.Len));
- S := ' R0=' + Format('%-g %-g ', [NewZ.Re, NewZ.im]);
- S := S + Format(' %-g', [(C1 * Len + OtherLine.C1 * OtherLine.Len) / TotalLen * 1.0e9]);
- S := S + Format(' %-g', [(C0 * Len + OtherLine.C0 * OtherLine.Len) / TotalLen * 1.0e9]);
+ // ********* Will This work with Length multiplier? did it ever work? *************************
+ NewZ := ParallelZ(Cmplx(R1 * Len, X1 * Len), Cmplx(Other.R1 * Other.Len, Other.X1 * Other.Len));
+ RXC[1] := NewZ.Re;
+ RXC[2] := NewZ.Im;
+ NewZ := ParallelZ(Cmplx(R0 * Len, X0 * Len), Cmplx(Other.R0 * Other.Len, Other.X0 * Other.Len));
+ RXC[3] := NewZ.Re;
+ RXC[4] := NewZ.Im;
+ RXC[3] := NewZ.Re;
+ RXC[4] := NewZ.Im;
+ RXC[5] := (C1 * Len + Other.C1 * Other.Len) / TotalLen * 1.0e9; // C1
+ RXC[6] := (C0 * Len + Other.C0 * Other.Len) / TotalLen * 1.0e9; // C0
+ UseRXC := True;
end;
end;
-
- Parser.cmdstring := S; // This reset the length units
- Edit;
-
- // update length units
- Parser.cmdstring := Format(' Length=%-g Units=%s', [TotalLen, LineUnitsStr(LenUnitsSaved)]);
- Edit;
-
- // Update symmetrical Components computation
- // (Only time this function is called is for sym comp update -- computes Z and Yc)
+ if UseRXC then
+ begin
+ SetDouble(ord(TProp.R1), RXC[1]);
+ SetDouble(ord(TProp.X1), RXC[2]);
+ SetDouble(ord(TProp.R0), RXC[3]);
+ SetDouble(ord(TProp.X0), RXC[4]);
+ SetDouble(ord(TProp.C1), RXC[5]);
+ SetDouble(ord(TProp.C0), RXC[6]);
+ end;
+ EndEdit(6);
+
+ // update length units
+ BeginEdit(False);
+ SetDouble(ord(TProp.Length), TotalLen);
+ SetInteger(ord(TProp.Units), LenUnitsSaved);
+ EndEdit(2);
+ // Update symmetrical Components computation
+ // (Only time this function is called is for sym comp update -- computes Z and Yc)
RecalcelementData;
-
end
- else {------------- Matrix Model for anything other than Symmetrical Components -------------------------}
+ else // Matrix Model for anything other than Symmetrical Components
if not Series then
- TotalLen := Len / 2.0 {We'll assume lines are equal for now}
+ // TODO: "fix" this?
+ TotalLen := Len / 2.0 // We'll assume lines are equal for now
else
- begin {Matrices were defined}
-
- // Merge Z matrices
+ begin // Matrices were defined
+ // Merge Z matrices
Values1 := Z.GetValuesArrayPtr(Order1);
- Values2 := OtherLine.Z.GetValuesArrayPtr(Order2);
+ Values2 := Other.Z.GetValuesArrayPtr(Order2);
if Order1 <> Order2 then
Exit; // OOps. Lines not same size for some reason
- // If Geometry specified, length is already included; so reset to 1.0
+ // If Geometry specified, length is already included; so reset to 1.0
if GeometrySpecified or SpacingSpecified then
LenSelf := 1.0;
- if OtherLine.GeometrySpecified or OtherLine.SpacingSpecified then
+ if Other.GeometrySpecified or Other.SpacingSpecified then
LenOther := 1.0;
- // Z <= (Z1 + Z2 )/TotalLen to get equiv ohms per unit length
+ // Z <= (Z1 + Z2 )/TotalLen to get equiv ohms per unit length
for i := 1 to Order1 * Order1 do
- Values1^[i] := CDivReal(Cadd(CmulReal(Values1^[i], LenSelf), CmulReal(Values2^[i], LenOther)), TotalLen);
+ Values1[i] := (Values1[i] * LenSelf + Values2^[i] * LenOther) / TotalLen;
- // Merge Yc matrices
+ // Merge Yc matrices
Values1 := Yc.GetValuesArrayPtr(Order1);
- Values2 := OtherLine.Yc.GetValuesArrayPtr(Order2);
+ Values2 := Other.Yc.GetValuesArrayPtr(Order2);
if Order1 <> Order2 then
Exit; // OOps. Lines not same size for some reason
for i := 1 to Order1 * Order1 do
- Values1^[i] := CDivReal(Cadd(CmulReal(Values1^[i], LenSelf), CmulReal(Values2^[i], LenOther)), TotalLen);
-
- {R Matrix}
- S := 'Rmatrix=[';
- for i := 1 to Order1 do
- begin
- for j := 1 to i do
- S := S + Format(' %-g', [Z.GetElement(i, j).Re]);
- S := S + ' | ';
- end;
- S := S + '] Xmatrix=[';
- {X Matrix}
- for i := 1 to Order1 do
- begin
- for j := 1 to i do
- S := S + Format(' %-g', [Z.GetElement(i, j).im]);
- S := S + ' | ';
- end;
- S := S + ']';
- Parser.cmdstring := S;
- Edit;
-
- {C Matrix}
- wnano := TwoPi * BaseFrequency / 1.0e9;
- S := 'Cmatrix=[';
- for i := 1 to Order1 do
- begin
- for j := 1 to i do
- S := S + Format(' %-g', [(Yc.GetElement(i, j).im / wnano)]); // convert from mhos to nanofs
- S := S + ' | ';
- end;
- S := S + '] ';
- Parser.cmdstring := S;
- Edit;
-
- // update length units
- Parser.cmdstring := Format(' Length=%-g Units=%s', [TotalLen, LineUnitsStr(LenUnitsSaved)]);
- Edit;
- end; {Matrix definition}
-
-
- OtherLine.Enabled := FALSE; // Disable the Other Line
+ Values1[i] := (Values1[i] * LenSelf + Values2[i] * LenOther) / TotalLen;
+
+ Len := TotalLen;
+ LengthUnits := LenUnitsSaved;
+
+ // Just mark the properties as updated
+ SetAsNextSeq(ord(TProp.rmatrix));
+ SetAsNextSeq(ord(TProp.xmatrix));
+ SetAsNextSeq(ord(TProp.cmatrix));
+ SetAsNextSeq(ord(TProp.length));
+ SetAsNextSeq(ord(TProp.units));
+ PropertySideEffects(ord(TProp.rmatrix));
+ PropertySideEffects(ord(TProp.xmatrix));
+ PropertySideEffects(ord(TProp.cmatrix));
+ end; // Matrix definition
+
+ Other.Enabled := FALSE; // Disable the Other Line
Result := TRUE;
end
else
- DoSimpleMsg('Error in Line Merge: Attempt to merge with invalid (nil) line object found.', 184);
-
-
+ DoSimpleMsg(_('Error in Line Merge: Attempt to merge with invalid (nil) line object found.'), 184);
end;
-procedure TLineObj.UpdateControlElements(const NewName, OldName: String);
-
+procedure TLineObj.UpdateControlElements(NewLine, OldLine: TLineObj);
var
pControlElem: TControlElem;
begin
-
pControlElem := ActiveCircuit.DSSControls.First;
while pControlElem <> NIL do
begin
- if CompareText(OldName, pControlElem.ElementName) = 0 then
- begin
- Parser.cmdstring := ' Element=' + NewName; // Change name of the property
- pControlElem.Edit;
- end;
+ if OldLine = pControlElem.MonitoredElement then // TODO: check if this works (and needs to work) with Fuse
+ pControlElem.ParsePropertyValue(pControlElem.ParentClass.CommandList.GetCommand('element'), NewLine.FullName);
pControlElem := ActiveCircuit.DSSControls.Next;
end;
end;
-procedure TLineObj.FetchLineSpacing(const Code: String);
+procedure TLineObj.FetchLineSpacing();
begin
- if DSS.LineSpacingClass.SetActive(Code) then
- begin
+ if LineSpacingObj = NIL then
+ Exit;
- FLineSpacingObj := DSS.LineSpacingClass.GetActiveObj;
- FLineCodeSpecified := FALSE;
- KillGeometrySpecified;
- SpacingCode := LowerCase(Code);
-
- // need to establish Yorder before FMakeZFromSpacing
- NPhases := FLineSpacingObj.NPhases;
- Nconds := FNPhases; // Force Reallocation of terminal info
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE; // Force Rebuild of Y matrix
-
- end
- else
- DoSimpleMsg('Line Spacing object ' + Code + ' not found.(LINE.' + Name + ')', 181011);
-end;
-
-procedure TLineObj.FetchWireList(const Code: String);
-var
- RatingsInc: Boolean;
- NewNumRat,
- j,
- i, istart: Integer;
- NewRatings: array of Double;
-begin
- if not assigned(FLineSpacingObj) then
- DoSimpleMsg('You must assign the LineSpacing before the Wires Property (LINE.' + name + ').', 18102);
-
- if FPhaseChoice = Unknown then
- begin // it's an overhead line
- FLineCodeSpecified := FALSE;
- KillGeometrySpecified;
- FWireDataSize := FLineSpacingObj.NWires;
- FLineWireData := Allocmem(Sizeof(FLineWireData^[1]) * FWireDataSize);
- istart := 1;
- FPhaseChoice := Overhead;
- end
- else
- begin // adding bare neutrals to an underground line - TODO what about repeat invocation?
- istart := FLineSpacingObj.NPhases + 1;
- end;
-
- AuxParser.CmdString := Code;
-
- NewNumRat := 1;
- RatingsInc := FALSE; // So far we don't know if there are seasonal ratings
- for i := istart to FLineSpacingObj.NWires do
- begin
- AuxParser.NextParam; // ignore any parameter name not expecting any
- DSS.WireDataClass.code := AuxParser.StrValue;
- if Assigned(DSS.ActiveConductorDataObj) then
- begin
- FLineWireData^[i] := DSS.ActiveConductorDataObj;
- if FLineWireData^[i].NumAmpRatings > NewNumRat then
- begin
- NewNumRat := FLineWireData^[i].NumAmpRatings;
- setlength(NewRatings, NewNumRat);
- for j := 0 to High(NewRatings) do
- NewRatings[j] := FLineWireData^[i].AmpRatings[j];
- RatingsInc := TRUE; // Yes, there are seasonal ratings
- end;
- NormAmps := FLineWireData^[i].NormAmps;
- EmergAmps := FLineWireData^[i].EmergAmps;
- end
- else
- DoSimpleMsg('Wire "' + AuxParser.StrValue + '" was not defined first (LINE.' + name + ').', 18103);
- end;
-
- if RatingsInc then
- begin
- NumAmpRatings := NewNumRat;
- setlength(AmpRatings, NumAmpRatings);
- for j := 0 to High(AmpRatings) do
- AmpRatings[j] := NewRatings[j];
- end;
-
- UpdatePDProperties;
-end;
-
-procedure TLineObj.FetchCNCableList(const Code: String);
-var
- i: Integer;
-begin
- FLineCodeSpecified := FALSE;
+ LineCodeObj := NIL;
KillGeometrySpecified;
- if not assigned(FLineSpacingObj) then
- DoSimpleMsg('Must assign the LineSpacing before CN cables.(LINE.' + Name + ')', 18104);
-
- FPhaseChoice := ConcentricNeutral;
- FLineWireData := Allocmem(Sizeof(FLineWireData^[1]) * FLineSpacingObj.NWires);
- AuxParser.CmdString := Code;
- for i := 1 to FLineSpacingObj.NPhases do
- begin // fill extra neutrals later
- AuxParser.NextParam; // ignore any parameter name not expecting any
- DSS.CNDataClass.code := AuxParser.StrValue;
- if Assigned(DSS.ActiveConductorDataObj) then
- FLineWireData^[i] := DSS.ActiveConductorDataObj
- else
- DoSimpleMsg('CN cable ' + AuxParser.StrValue + ' was not defined first.(LINE.' + Name + ')', 18105);
- end;
+ // need to establish Yorder before FMakeZFromSpacing
+ FNPhases := LineSpacingObj.NPhases;
+ Nconds := FNPhases; // Force Reallocation of terminal info
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE; // Force Rebuild of Y matrix
+
+ FLineWireData := Allocmem(Sizeof(FLineWireData^[1]) * LineSpacingObj.NWires);
+ FWireDataSize := LineSpacingObj.NWires;
end;
-procedure TLineObj.FetchTSCableList(const Code: String);
+procedure TLineObj.FetchGeometryCode();
var
i: Integer;
begin
- FLineCodeSpecified := FALSE;
- KillGeometrySpecified;
- if not assigned(FLineSpacingObj) then
- DoSimpleMsg('Must assign the LineSpacing before TS cables.(LINE.' + Name + ')', 18106);
-
- FPhaseChoice := TapeShield;
- FLineWireData := Allocmem(Sizeof(FLineWireData^[1]) * FLineSpacingObj.NWires);
- AuxParser.CmdString := Code;
- for i := 1 to FLineSpacingObj.NPhases do
- begin // fill extra neutrals later
- AuxParser.NextParam; // ignore any parameter name not expecting any
- DSS.TSDataClass.code := AuxParser.StrValue;
- if Assigned(DSS.ActiveConductorDataObj) then
- FLineWireData^[i] := DSS.ActiveConductorDataObj
- else
- DoSimpleMsg('TS cable ' + AuxParser.StrValue + ' was not defined first. (LINE.' + Name + ')', 18107);
- end;
-end;
+ if LineGeometryObj = NIL then
+ Exit;
-procedure TLineObj.FetchGeometryCode(const Code: String);
-var
- i: Integer;
-begin
- if DSS.LineGeometryClass.SetActive(Code) then
- begin
- FLineCodeSpecified := FALSE; // Cancel this flag
- SpacingSpecified := FALSE;
+ LineCodeObj := NIL;
+ KillSpacingSpecified();
- FLineGeometryObj := DSS.LineGeometryClass.GetActiveObj;
- FZFrequency := -1.0; // Init to signify not computed
+ FZFrequency := -1.0; // Init to signify not computed
- GeometryCode := LowerCase(Code);
+ if FrhoSpecified then
+ LineGeometryObj.rhoearth := rho;
- if FrhoSpecified then
- FlineGeometryObj.rhoearth := rho;
+ NormAmps := LineGeometryObj.NormAmps;
+ EmergAmps := LineGeometryObj.EmergAmps;
+ SetAsNextSeq(ord(TProp.Ratings));
- NormAmps := FLineGeometryObj.NormAmps;
- EmergAmps := FLineGeometryObj.EmergAmps;
- UpdatePDProperties;
+ FNPhases := LineGeometryObj.Nconds;
+ Nconds := FNPhases; // Force Reallocation of terminal info
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE; // Force Rebuild of Y matrix
- NPhases := FLineGeometryObj.Nconds;
- Nconds := FNPhases; // Force Reallocation of terminal info
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE; // Force Rebuild of Y matrix
-
- NumAmpRatings := FLineGeometryObj.NumAmpRatings;
- setlength(AmpRatings, NumAmpRatings);
- for i := 0 to High(AmpRatings) do
- AmpRatings[i] := FLineGeometryObj.AmpRatings[i];
+ NumAmpRatings := LineGeometryObj.NumAmpRatings;
+ setlength(AmpRatings, NumAmpRatings);
+ for i := 0 to High(AmpRatings) do
+ AmpRatings[i] := LineGeometryObj.AmpRatings[i];
- FLineType := FLineGeometryObj.FLineType;
- end
- else
- DoSimpleMsg('Line Geometry Object:' + Code + ' not found. (LINE.' + Name + ')', 18108);
+ FLineType := LineGeometryObj.FLineType;
+ SymComponentsModel := FALSE;
+ SymComponentsChanged := FALSE;
end;
procedure TLineObj.FMakeZFromGeometry(f: Double); // make new Z, Zinv, Yc, etc
@@ -2111,42 +1602,34 @@ procedure TLineObj.FMakeZFromGeometry(f: Double); // make new Z, Zinv, Yc, etc
if f = FZFrequency then
exit; // Already Done for this frequency, no need to do anything
- if Assigned(FLineGeometryObj) then
- begin
- {This will make a New Z; Throw away present allocations}
+ if LineGeometryObj = NIL then
+ Exit;
- if assigned(Z) then
- begin
- Z.Free;
- Z := NIL;
- end;
- if assigned(Zinv) then
- begin
- Zinv.Free;
- Zinv := NIL;
- end;
- if assigned(Yc) then
- begin
- Yc.Free;
- Yc := NIL;
- end;
+ // This will make a New Z; Throw away present allocations
- DSS.ActiveEarthModel := FEarthModel;
+ if assigned(Z) then
+ FreeAndNil(Z);
+ if assigned(Zinv) then
+ FreeAndNil(Zinv);
- Z := FLineGeometryObj.Zmatrix[f, len, LengthUnits];
- Yc := FLineGeometryObj.YCmatrix[f, len, LengthUnits];
- {Init Zinv}
- if Assigned(Z) then
- begin
- Zinv := TCMatrix.CreateMatrix(Z.order); // Either no. phases or no. conductors
- Zinv.CopyFrom(Z);
- Zinv.Invert; {Invert Z in place to get values to put in Yprim}
- end;
+ if assigned(Yc) then
+ FreeAndNil(Yc);
- // Z and YC are actual total impedance for the line;
+ DSS.ActiveEarthModel := FEarthModel;
- FZFrequency := f;
+ Z := LineGeometryObj.Zmatrix[f, len, LengthUnits];
+ Yc := LineGeometryObj.YCmatrix[f, len, LengthUnits];
+ // Init Zinv
+ if Assigned(Z) then
+ begin
+ Zinv := TCMatrix.CreateMatrix(Z.order); // Either no. phases or no. conductors
+ Zinv.CopyFrom(Z);
+ Zinv.Invert; // Invert Z in place to get values to put in Yprim
end;
+
+ // Z and YC are actual total impedance for the line;
+
+ FZFrequency := f;
end;
procedure TLineObj.FMakeZFromSpacing(f: Double); // make new Z, Zinv, Yc, etc
@@ -2172,15 +1655,15 @@ procedure TLineObj.FMakeZFromSpacing(f: Double); // make new Z, Zinv, Yc, etc
Yc := NIL;
end;
- // make a temporary LineGeometry to calculate line constants
+ // make a temporary LineGeometry to calculate line constants
pGeo := TLineGeometryObj.Create(DSS.LineGeometryClass, Name);
- pGeo.LoadSpacingAndWires(FLineSpacingObj, FLineWireData); // this sets OH, CN, or TS
+ pGeo.LoadSpacingAndWires(LineSpacingObj, FLineWireData); // this sets OH, CN, or TS
if FrhoSpecified then
pGeo.rhoearth := rho;
NormAmps := pGeo.NormAmps;
EmergAmps := pGeo.EmergAmps;
- UpdatePDProperties;
+ SetAsNextSeq(ord(TProp.Ratings));
DSS.ActiveEarthModel := FEarthModel;
@@ -2190,7 +1673,7 @@ procedure TLineObj.FMakeZFromSpacing(f: Double); // make new Z, Zinv, Yc, etc
begin
Zinv := TCMatrix.CreateMatrix(Z.order); // Either no. phases or no. conductors
Zinv.CopyFrom(Z);
- Zinv.Invert; {Invert Z in place to get values to put in Yprim}
+ Zinv.Invert; // Invert Z in place to get values to put in Yprim
end;
pGeo.Free;
@@ -2199,12 +1682,11 @@ procedure TLineObj.FMakeZFromSpacing(f: Double); // make new Z, Zinv, Yc, etc
procedure TLineObj.KillGeometrySpecified;
begin
-{Indicate No Line Geometry specification if this is called}
+ // Indicate No Line Geometry specification if this is called
if GeometrySpecified then
begin
- FLineGeometryObj := NIL;
+ LineGeometryObj := NIL;
FZFrequency := -1.0;
- GeometrySpecified := FALSE;
end;
end;
@@ -2212,17 +1694,17 @@ procedure TLineObj.KillSpacingSpecified;
begin
if SpacingSpecified then
begin
- FLineSpacingObj := NIL;
+ LineSpacingObj := NIL;
+ FWireDataSize := 0;
Reallocmem(FLineWireData, 0);
FPhaseChoice := Unknown;
FZFrequency := -1.0;
- SpacingSpecified := FALSE;
end;
end;
procedure TLineObj.ClearYPrim;
begin
- // Line Object needs both Series and Shunt YPrims built
+ // Line Object needs both Series and Shunt YPrims built
if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Shunt = NIL) OR (Yprim_Series = NIL) {YPrimInvalid} then
begin // Reallocate YPrim if something has invalidated old allocation
if YPrim_Series <> NIL then
@@ -2245,23 +1727,19 @@ procedure TLineObj.ClearYPrim;
end;
procedure TLineObj.ConvertZinvToPosSeqR;
-
// For GIC Analysis, use only real part of Z
-
var
Z1, ZS, Zm: Complex;
i: Integer;
-
begin
-
-// re-invert Zinv
+ // re-invert Zinv
Zinv.Invert;
-// Now Zinv is back to Z with length included
+ // Now Zinv is back to Z with length included
// average the diagonal and off-dialgonal elements
Zs := Zinv.AvgDiagonal;
Zm := Zinv.AvgOffDiagonal;
- Z1 := CSub(Zs, Zm);
+ Z1 := Zs - Zm;
Z1.im := 0.0; // ignore X part
Zinv.Clear;
@@ -2273,7 +1751,7 @@ procedure TLineObj.ConvertZinvToPosSeqR;
end;
procedure TLineObj.ResetLengthUnits;
-{If specify the impedances always assume the length units match}
+// If specify the impedances always assume the length units match
begin
FUnitsConvert := 1.0;
LengthUnits := UNITS_NONE; // but do not erase FUserLengthUnits, in case of CIM export
@@ -2283,9 +1761,9 @@ function TLineObj.NumConductorData: Integer;
begin
Result := 0;
if Assigned(FLineWireData) then
- Result := FLineSpacingObj.NWires;
- if Assigned(FLineGeometryObj) then
- Result := FLineGeometryObj.NWires;
+ Result := LineSpacingObj.NWires;
+ if Assigned(LineGeometryObj) then
+ Result := LineGeometryObj.NWires;
end;
function TLineObj.FetchConductorData(i: Integer): TConductorDataObj;
@@ -2293,19 +1771,30 @@ function TLineObj.FetchConductorData(i: Integer): TConductorDataObj;
Result := NIL;
if Assigned(FLineWireData) then
begin
- if i <= FLineSpacingObj.Nwires then
+ if i <= LineSpacingObj.Nwires then
Result := FLineWireData[i];
end
else
- if Assigned(FLineGeometryObj) then
+ if Assigned(LineGeometryObj) then
begin
- if i <= FLineGeometryObj.Nwires then
- Result := FLineGeometryObj.ConductorData[i];
+ if i <= LineGeometryObj.Nwires then
+ Result := LineGeometryObj.ConductorData[i];
end;
end;
-initialization
+function TLineObj.LineCodeSpecified: Boolean;
+begin
+ Result := LineCodeObj <> NIL;
+end;
- CAP_EPSILON := cmplx(0.0, 4.2e-8); // 5 kvar of capacitive reactance at 345 kV to avoid open line problem
+function TLineObj.GeometrySpecified: Boolean;
+begin
+ Result := LineGeometryObj <> NIL;
+end;
+
+function TLineObj.SpacingSpecified: Boolean;
+begin
+ Result := Assigned(LineSpacingObj) and Assigned(FLineWireData);
+end;
-end.
+end.
\ No newline at end of file
diff --git a/src/PDElements/PDClass.pas b/src/PDElements/PDClass.pas
index c555cfb49..2a9255527 100644
--- a/src/PDElements/PDClass.pas
+++ b/src/PDElements/PDClass.pas
@@ -7,8 +7,6 @@
----------------------------------------------------------
}
-{$M+}
-
interface
uses
@@ -16,123 +14,82 @@ interface
CktElementClass;
type
- TPDClass = class(TCktElementClass)
- PRIVATE
+{$SCOPEDENUMS ON}
+ TPDElementProp = (
+ INVALID = 0,
+ normamps=1,
+ emergamps=2,
+ faultrate=3,
+ pctperm=4,
+ repair=5
+ );
+{$SCOPEDENUMS OFF}
+ TPDClass = class(TCktElementClass)
PROTECTED
- procedure ClassEdit(const ActivePDObj: Pointer; const ParamPointer: Integer);
- procedure ClassMakeLike(const OtherObj: Pointer);
- procedure CountProperties; // Add no. of intrinsic properties
- procedure DefineProperties; // Add Properties of this class to propName
+ procedure CountPropertiesAndAllocate; override;
+ procedure DefineProperties; override;
PUBLIC
- NumPDClassProps: Integer;
- constructor Create(dssContext: TDSSContext);
+ constructor Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
destructor Destroy; OVERRIDE;
- PUBLISHED
-
end;
-
implementation
uses
DSSClassDefs,
PDElement,
- ParserDel,
DSSGlobals,
Utilities,
DSSHelper,
DSSObjectHelper,
TypInfo;
-constructor TPDClass.Create(dssContext: TDSSContext);
+type
+ TObj = TPDElement;
+ TProp = TPDElementProp;
+const
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
+
+constructor TPDClass.Create(dssContext: TDSSContext; DSSClsType: Integer; DSSClsName: String);
begin
- inherited Create(dssContext);
- NumPDClassProps := 5;
- DSSClassType := PD_ELEMENT;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
+
+ inherited Create(dssContext, DSSClsType, DSSClsName);
+ if (DSSClassType and NON_PCPD_ELEM) <> NON_PCPD_ELEM then
+ DSSClassType := DSSClassType or PD_ELEMENT;
+
+ ClassParents.Add('PDClass');
end;
destructor TPDClass.Destroy;
-
begin
inherited Destroy;
end;
-procedure TPDClass.CountProperties;
+procedure TPDClass.CountPropertiesAndAllocate;
begin
- NumProperties := NumProperties + NumPDClassProps;
- inherited CountProperties;
+ NumProperties := NumProperties + NumPropsThisClass;
+ inherited CountPropertiesAndAllocate;
end;
procedure TPDClass.DefineProperties;
-
-// Define the properties for the base power delivery element class
-
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
- PropertyName^[ActiveProperty + 1] := 'normamps';
- PropertyName^[ActiveProperty + 2] := 'emergamps';
- PropertyName^[ActiveProperty + 3] := 'faultrate';
- PropertyName^[ActiveProperty + 4] := 'pctperm';
- PropertyName^[ActiveProperty + 5] := 'repair';
-
- PropertyHelp^[ActiveProperty + 1] := 'Normal rated current.';
- PropertyHelp^[ActiveProperty + 2] := 'Maximum or emerg current.';
- PropertyHelp^[ActiveProperty + 3] := 'Failure rate per year.';
- PropertyHelp^[ActiveProperty + 4] := 'Percent of failures that become permanent.';
- PropertyHelp^[ActiveProperty + 5] := 'Hours to repair.';
+ PopulatePropertyNames(ActiveProperty, NumPropsThisClass, PropInfo, False, 'PDClass');
- ActiveProperty := ActiveProperty + NumPDClassProps;
+ PropertyOffset[ActiveProperty + ord(TProp.normamps)] := ptruint(@obj.NormAmps);
+ PropertyOffset[ActiveProperty + ord(TProp.emergamps)] := ptruint(@obj.EmergAmps);
+ PropertyOffset[ActiveProperty + ord(TProp.faultrate)] := ptruint(@obj.FaultRate);
+ PropertyOffset[ActiveProperty + ord(TProp.pctperm)] := ptruint(@obj.PctPerm);
+ PropertyOffset[ActiveProperty + ord(TProp.repair)] := ptruint(@obj.HrsToRepair);
+ ActiveProperty := ActiveProperty + NumPropsThisClass;
inherited DefineProperties;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TPDClass.ClassEdit(const ActivePDObj: Pointer; const ParamPointer: Integer);
-begin
- // continue parsing with contents of Parser
-
- if ParamPointer > 0 then
- with TPDElement(ActivePDObj) do
- begin
-
- case ParamPointer of
- 1:
- NormAmps := Parser.Dblvalue;
- 2:
- EmergAmps := Parser.Dblvalue;
- 3:
- FaultRate := Parser.Dblvalue;
- 4:
- PctPerm := Parser.Dblvalue;
- 5:
- HrsToRepair := Parser.DblValue;
- else
- inherited ClassEdit(ActivePDObj, ParamPointer - NumPDClassProps)
- end;
- end;
-
-end;
-
-procedure TPDClass.ClassMakeLike(const OtherObj: Pointer);
-
-var
- OtherPDObj: TPDElement;
-begin
-
- OtherPDObj := TPDElement(OtherObj);
-
- with TPDElement(ActiveDSSObject) do
- begin
- NormAmps := OtherPDObj.NormAmps;
- EmergAmps := OtherPDObj.EmergAmps;
- FaultRate := OtherPDObj.FaultRate;
- PctPerm := OtherPDObj.PctPerm;
- HrsToRepair := OtherPDObj.HrsToRepair;
- end;
-
- inherited ClassMakeLike(OtherObj);
-
-end;
-
-end.
+end.
\ No newline at end of file
diff --git a/src/PDElements/PDElement.pas b/src/PDElements/PDElement.pas
index 5c32dfef5..361950054 100644
--- a/src/PDElements/PDElement.pas
+++ b/src/PDElements/PDElement.pas
@@ -7,18 +7,11 @@
----------------------------------------------------------
}
-{
- Change Log
- 1/10/00 Fixed bug where OverLoad_EEN, _UE was not being set for elements
- where the rating was not specified.
- 4/11/01 Fixed error in computin excess kVAs (factor readjustment)
-}
-
interface
uses
CktElement,
- ucomplex,
+ UComplex, DSSUcomplex,
ucmatrix,
DSSClass,
MeterElement,
@@ -40,7 +33,7 @@ TPDElement = class(TDSSCktElement)
PctPerm, // percent of faults that are permanent in this element
BranchFltRate, // net failure rate for this branch
AccumulatedBrFltRate, // accumulated failure rate for this branch
- MilesThisLine, // length in miles if line
+ MilesThisLine, // length in miles if line -- TODO: remove, trivial to recalc
AccumulatedMilesDownStream, // total miles downstream
HrsToRepair: Double;
@@ -67,8 +60,8 @@ TPDElement = class(TDSSCktElement)
constructor Create(ParClass: TDSSClass);
destructor Destroy; OVERRIDE;
-
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
+ procedure MakeLike(OtherObj: Pointer); override;
+
procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present values of terminal
procedure CalcFltRate; VIRTUAL; // Calc failure rates for section and buses
@@ -79,7 +72,6 @@ TPDElement = class(TDSSCktElement)
property ExcesskVANorm[idxTerm: Integer]: Complex READ Get_ExcesskVANorm;
property ExcesskVAEmerg[idxTerm: Integer]: Complex READ Get_ExcesskVAEmerg;
-
end;
@@ -95,23 +87,19 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TPDElement;
-{---------Summing Utility proc-------}
procedure accumsum(var a: Double; b: Double); inline;
begin
a := a + b;
end;
-{------------------------------------}
-
procedure TPDElement.AccumFltRate;
-
var
FromBus: TDSSBus;
ToBus: TDSSBus;
-
begin
-
with ActiveCircuit do
begin
if FromTerminal = 2 then
@@ -119,7 +107,7 @@ procedure TPDElement.AccumFltRate;
else
ToTerminal := 2;
- {Get fault Rate for TO bus and add it to this section failure rate}
+ // Get fault Rate for TO bus and add it to this section failure rate
ToBus := Buses^[Terminals[ToTerminal - 1].BusRef];
AccumulatedBrFltRate := ToBus.BusFltRate + BranchFltRate;
FromBus := Buses^[Terminals[FromTerminal - 1].BusRef];
@@ -128,14 +116,13 @@ procedure TPDElement.AccumFltRate;
AccumulatedMilesDownStream := ToBus.BusTotalMiles + MilesThisLine;
accumsum(FromBus.BusTotalMiles, AccumulatedMilesDownStream);
- {Compute accumulated to FROM Bus; if a fault interrupter, assume it isolates all downline faults}
- if not HasOcpDevice then
+ // Compute accumulated to FROM Bus; if a fault interrupter, assume it isolates all downline faults
+ if not (Flg.HasOcpDevice in Flags) then
begin
// accumlate it to FROM bus
accumsum(FromBus.BusFltRate, AccumulatedBrFltRate);
end;
end;
-
end;
procedure TPDElement.CalcFltRate; {Virtual function -- LINE is different, for one}
@@ -145,7 +132,6 @@ procedure TPDElement.CalcFltRate; {Virtual function -- LINE is different, for
{May be overridden by specific device class behavior}
BranchFltRate := Faultrate * pctperm * 0.01;
-
end;
procedure TPDElement.CalcCustInterrupts;
@@ -160,13 +146,12 @@ procedure TPDElement.CalcCustInterrupts;
end;
procedure TPDElement.CalcNum_Int(var SectionCount: Integer; AssumeRestoration: Boolean);
-{This is called on the forward sweep to set the number of interruptions at the To bus.}
+// This is called on the forward sweep to set the number of interruptions at the To bus.
var
FromBus: TDSSBus;
ToBus: TDSSBus;
begin
-
with ActiveCircuit do
begin
if FromTerminal = 2 then
@@ -179,23 +164,22 @@ procedure TPDElement.CalcNum_Int(var SectionCount: Integer; AssumeRestoration: B
// If no interrupting device then the downline bus will have the same num of interruptions
ToBus.Bus_Num_Interrupt := FromBus.Bus_Num_Interrupt;
- { If Interrupting device (on FROM side)then downline bus will have
- additional interruptions ---- including for fused lateral
- If assuming restoration and the device is an automatic device, the To bus will be
- interrupted only for faults on the main section, not including fused sections.
- }
- if HasOCPDevice then
+ // If Interrupting device (on FROM side)then downline bus will have
+ // additional interruptions ---- including for fused lateral
+ // If assuming restoration and the device is an automatic device, the To bus will be
+ // interrupted only for faults on the main section, not including fused sections.
+ if Flg.HasOCPDevice in Flags then
begin
- if AssumeRestoration and HasAutoOCPDevice then
- {To Bus will be interrupted only for faults on this section.
- AccumulatedBrFltRate does not include Branches down from
- Branches with OCP devics}
+ if AssumeRestoration and (Flg.HasAutoOCPDevice in Flags) then
+ // To Bus will be interrupted only for faults on this section
+ // AccumulatedBrFltRate does not include Branches down fro
+ // Branches with OCP devics
ToBus.Bus_Num_Interrupt := AccumulatedBrFltRate
else
accumsum(ToBus.Bus_Num_Interrupt, AccumulatedBrFltRate);
- {If there is an OCP device on this PDElement, this is the
- beginning of a new section.}
+ // If there is an OCP device on this PDElement, this is the
+ // beginning of a new section.
inc(SectionCount);
ToBus.BusSectionID := SectionCount; // Assign it to the new section
end
@@ -204,7 +188,6 @@ procedure TPDElement.CalcNum_Int(var SectionCount: Integer; AssumeRestoration: B
BranchSectionID := ToBus.BusSectionID;
end;
-
end;
constructor TPDElement.Create(ParClass: TDSSClass);
@@ -225,13 +208,10 @@ constructor TPDElement.Create(ParClass: TDSSClass);
NumAmpRatings := 1;
setlength(AmpRatings, 1); // Initialized here
AmpRatings[0] := 1000;
-
-
end;
destructor TPDElement.Destroy;
begin
-
inherited Destroy;
end;
@@ -243,7 +223,6 @@ procedure TPDElement.GetCurrents(Curr: pComplexArray);
if Enabled then
begin
-
with ActiveCircuit.Solution do
for i := 1 to Yorder do
Vterminal^[i] := NodeV^[NodeRef^[i]];
@@ -256,20 +235,17 @@ procedure TPDElement.GetCurrents(Curr: pComplexArray);
except
On E: Exception do
- DoErrorMsg(('Trying to Get Currents for Element: ' + Name + '.'), E.Message,
- 'Has the circuit been solved?', 660);
+ DoErrorMsg(Format(_('Trying to Get Currents for Element: "%s".'), [Name]),
+ E.Message,
+ _('Has the circuit been solved?'), 660);
end;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - -
function TPDElement.Get_ExcessKVANorm(idxTerm: Integer): Complex;
-
var
Factor: Double;
kVA: Complex;
begin
-
if (NormAmps = 0.0) or not Enabled then
begin
OverLoad_EEN := 0.0; // bug fixed 1/10/00
@@ -277,23 +253,21 @@ function TPDElement.Get_ExcessKVANorm(idxTerm: Integer): Complex;
Exit;
end;
- kVA := CmulReal(Power[idxTerm], 0.001); // Also forces computation of Current into Itemp
+ kVA := Power[idxTerm] * 0.001; // Also forces computation of Current into Itemp
Factor := (MaxTerminalOneIMag / NormAmps - 1.0);
if (Factor > 0.0) then
begin
OverLoad_EEN := Factor;
Factor := 1.0 - 1.0 / (Factor + 1.0); // To get factor
- Result := CmulReal(kVA, Factor);
+ Result := kVA * Factor;
end
else
begin
OverLoad_EEN := 0.0;
Result := cZero;
end;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - -
function TPDElement.Get_ExcessKVAEmerg(idxTerm: Integer): Complex;
var
Factor: Double;
@@ -306,38 +280,22 @@ function TPDElement.Get_ExcessKVAEmerg(idxTerm: Integer): Complex;
Exit;
end;
- kVA := CmulReal(Power[idxTerm], 0.001); // Also forces computation of Current into Itemp
+ kVA := Power[idxTerm] * 0.001; // Also forces computation of Current into Itemp
Factor := (MaxTerminalOneIMag / EmergAmps - 1.0);
if Factor > 0.0 then
begin
Overload_UE := Factor;
Factor := 1.0 - 1.0 / (Factor + 1.0); // To get Excess
- Result := CmulReal(kVA, Factor);
+ Result := kVA * Factor;
end
else
begin
Overload_UE := 0.0;
Result := cZero;
end;
-
-end;
-
-procedure TPDElement.InitPropertyValues(ArrayOffset: Integer);
-begin
-
-
- PropertyValue[ArrayOffset + 1] := '400'; //Normamps
- PropertyValue[ArrayOffset + 2] := '600'; //emergamps
- PropertyValue[ArrayOffset + 3] := '0.1'; //Fault rate
- PropertyValue[ArrayOffset + 4] := '20'; // Pct Perm
- PropertyValue[ArrayOffset + 5] := '3'; // Hrs to repair
-
- inherited initPropertyValues(ArrayOffset + 5);
-
end;
-
procedure TPDElement.ZeroReliabilityAccums;
var
FromBus: TDSSBus;
@@ -354,7 +312,21 @@ procedure TPDElement.ZeroReliabilityAccums;
Bus_Num_Interrupt := 0.0;
BusSectionID := -1; // signify not set
end;
+end;
+
+procedure TPDElement.MakeLike(OtherObj: Pointer);
+var
+ Other: TObj;
+begin
+ inherited MakeLike(OtherObj);
+
+ Other := TPDElement(OtherObj);
+ NormAmps := Other.NormAmps;
+ EmergAmps := Other.EmergAmps;
+ FaultRate := Other.FaultRate;
+ PctPerm := Other.PctPerm;
+ HrsToRepair := Other.HrsToRepair;
end;
end.
diff --git a/src/PDElements/Reactor.pas b/src/PDElements/Reactor.pas
index 61a83e78c..c98188f62 100644
--- a/src/PDElements/Reactor.pas
+++ b/src/PDElements/Reactor.pas
@@ -7,54 +7,46 @@
----------------------------------------------------------
}
-{ 10-26-00 Created from Capacitor object
- 3-2-06 Added Parallel Option and corrected frequency adjustments
- RMATRIX, Xmatrix untested
- 2013 Added Symmetrical component specification and frequency-dependence for simplr
- R+jX model
+//Basic Reactor
+//
+// Uses same rules as Capacitor and Fault for connections
+//
+// Implemented as a two-terminal constant impedance (Power Delivery Element)
+// Defaults to a Shunt Reactor but can be connected as a two-terminal series reactor
+//
+// If Parallel=Yes, then the R and X components are treated as being in parallel
+//
+// Bus2 connection defaults to 0 node of Bus1 (if Bus2 has the default bus connection
+// at the time Bus1 is defined. Therefore, if only Bus1 is specified, a shunt Reactor results.
+// If delta connected, Bus2 is set to node zero of Bus1 and nothing is returned in the lower
+// half of YPrim - all zeroes.
+//
+// If an ungrounded wye is desired, explicitly set Bus2= and set all nodes the same,
+// e.g. Bus1.4.4.4 (uses 4th node of Bus1 as neutral point)
+// or BusNew.1.1.1 (makes a new bus for the neutral point)
+// You must specify the nodes or you will get a series Reactor!
+//
+// A series Reactor is specified simply by setting bus2 and declaring the connection
+// to be Wye. If the connection is specified as delta, nothing will be connected to Bus2.
+// In fact the number of terminals is set to 1.
+//
+// Reactance may be specified as:
+//
+// 1. kvar and kv ratings at base frequency. impedance. Specify kvar as total for
+// all phases. For 1-phase, kV = Reactor coil kV rating.
+// For 2 or 3-phase, kV is line-line three phase. For more than 3 phases, specify
+// kV as actual coil voltage.
+// 2. Series Resistance, R, and Reactance, X, in ohns at base frequency to be used in each phase. If specified in this manner,
+// the given value is always used whether wye or delta. X may be specified as Inductance, LmH, in mH.
+// The Z property may also be used to specify R and X in an array.
+// 3. A R and X matrices .
+// If conn=wye then 2-terminal through device
+// If conn=delta then 1-terminal.
+// Ohms at base frequency
+// Note that Rmatix may be in parallel with Xmatric (set parallel = Yes)
+// 4. As symmetrical component values using Z1, Z2, and Z0 complex array properties.
+// Z2 defaults to Z1, but can be set to a different value.
-
-Basic Reactor
-
- Uses same rules as Capacitor and Fault for connections
-
- Implemented as a two-terminal constant impedance (Power Delivery Element)
- Defaults to a Shunt Reactor but can be connected as a two-terminal series reactor
-
- If Parallel=Yes, then the R and X components are treated as being in parallel
-
- Bus2 connection defaults to 0 node of Bus1 (if Bus2 has the default bus connection
- at the time Bus1 is defined. Therefore, if only Bus1 is specified, a shunt Reactor results.
- If delta connected, Bus2 is set to node zero of Bus1 and nothing is returned in the lower
- half of YPrim - all zeroes.
-
- If an ungrounded wye is desired, explicitly set Bus2= and set all nodes the same,
- e.g. Bus1.4.4.4 (uses 4th node of Bus1 as neutral point)
- or BusNew.1.1.1 (makes a new bus for the neutral point)
- You must specify the nodes or you will get a series Reactor!
-
- A series Reactor is specified simply by setting bus2 and declaring the connection
- to be Wye. If the connection is specified as delta, nothing will be connected to Bus2.
- In fact the number of terminals is set to 1.
-
- Reactance may be specified as:
-
- 1. kvar and kv ratings at base frequency. impedance. Specify kvar as total for
- all phases. For 1-phase, kV = Reactor coil kV rating.
- For 2 or 3-phase, kV is line-line three phase. For more than 3 phases, specify
- kV as actual coil voltage.
- 2. Series Resistance, R, and Reactance, X, in ohns at base frequency to be used in each phase. If specified in this manner,
- the given value is always used whether wye or delta. X may be specified as Inductance, LmH, in mH.
- The Z property may also be used to specify R and X in an array.
- 3. A R and X matrices .
- If conn=wye then 2-terminal through device
- If conn=delta then 1-terminal.
- Ohms at base frequency
- Note that Rmatix may be in parallel with Xmatric (set parallel = Yes)
- 4. As symmetrical component values using Z1, Z2, and Z0 complex array properties.
- Z2 defaults to Z1, but can be set to a different value.
-
-}
interface
uses
@@ -63,43 +55,52 @@ interface
DSSClass,
PDClass,
PDElement,
- uComplex,
+ UComplex, DSSUcomplex,
UcMatrix,
ArrayDef,
XYCurve;
type
{$SCOPEDENUMS ON}
- TReactorConnection = (
- Wye = 0, // wye, star, line-neutral connection
- Delta = 1 // delta, line-line connection
+ TReactorProp = (
+ INVALID = 0,
+ bus1 = 1,
+ bus2 = 2,
+ phases = 3,
+ kvar = 4,
+ kv = 5,
+ conn = 6,
+ Rmatrix = 7,
+ Xmatrix = 8,
+ Parallel = 9,
+ R = 10,
+ X = 11,
+ Rp = 12,
+ Z1 = 13,
+ Z2 = 14,
+ Z0 = 15,
+ Z = 16,
+ RCurve = 17,
+ LCurve = 18,
+ LmH = 19
);
+
+ TReactorConnection = TGeneralConnection;
{$SCOPEDENUMS OFF}
TReactor = class(TPDClass)
- PRIVATE
- procedure Domatrix(var Matrix: pDoubleArray);
-
- procedure InterpretConnection(const S: String);
- procedure ReactorSetbus1(const s: String);
PROTECTED
- function MakeLike(const ReactorName: String): Integer; OVERRIDE;
- procedure DefineProperties; // Add Properties of this class to propName
+ procedure DefineProperties; override; // Add Properties of this class to propName
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
-
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
TReactorObj = class(TPDElement)
-{$IFDEF DSS_CAPI}
PUBLIC
-{$ELSE}
- PRIVATE
-{$ENDIF}
+ //TODO: remove R and X, use Z instead
R, Rp, Gp,
X, L,
kvarrating,
@@ -111,7 +112,7 @@ TReactorObj = class(TPDElement)
Connection: TReactorConnection; // 0 or 1 for wye (default) or delta, respectively
SpecType: Integer; // 1=kvar, 2=R+jX, 3=R and X matrices, 4=sym components
- IsParallel: Boolean;
+ IsParallel: LongBool;
RpSpecified: Boolean;
Bus2Defined: Boolean;
Z2Specified: Boolean;
@@ -120,24 +121,22 @@ TReactorObj = class(TPDElement)
PUBLIC
- RCurve: String;
RCurveObj: TXYCurveObj;
- LCurve: String;
LCurveObj: TXYCurveObj;
constructor Create(ParClass: TDSSClass; const ReactorName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Complex); OVERRIDE;
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
// CIM XML access - this is only tested for the IEEE 8500-node feeder
property SimpleR: Double READ R;
@@ -147,7 +146,6 @@ TReactorObj = class(TPDElement)
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Sysutils,
@@ -157,470 +155,270 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TReactorObj;
+ TProp = TReactorProp;
const
- NumPropsThisClass = 19;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TReactor.Create(dssContext: TDSSContext); // Creates superstructure for all Reactor objects
+constructor TReactor.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'Reactor';
- DSSClassType := DSSClassType + REACTOR_ELEMENT;
-
- ActiveElement := 0;
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
+ inherited Create(dssContext, REACTOR_ELEMENT, 'Reactor');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TReactor.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TReactor.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
- PropertyName^[1] := 'bus1';
- PropertyName^[2] := 'bus2';
- PropertyName^[3] := 'phases';
- PropertyName^[4] := 'kvar';
- PropertyName^[5] := 'kv';
- PropertyName^[6] := 'conn';
- PropertyName^[7] := 'Rmatrix';
- PropertyName^[8] := 'Xmatrix';
- PropertyName^[9] := 'Parallel';
- PropertyName^[10] := 'R';
- PropertyName^[11] := 'X';
- PropertyName^[12] := 'Rp';
- PropertyName^[13] := 'Z1';
- PropertyName^[14] := 'Z2';
- PropertyName^[15] := 'Z0';
- PropertyName^[16] := 'Z';
- PropertyName^[17] := 'RCurve';
- PropertyName^[18] := 'LCurve';
- PropertyName^[19] := 'LmH';
-
- // define Property help values
-
- PropertyHelp^[1] := 'Name of first bus. Examples:' + CRLF +
- 'bus1=busname' + CRLF +
- 'bus1=busname.1.2.3' + CRLF + CRLF +
- 'Bus2 property will default to this bus, node 0, unless previously specified. ' +
- 'Only Bus1 need be specified for a Yg shunt reactor.';
- PropertyHelp^[2] := 'Name of 2nd bus. Defaults to all phases connected ' +
- 'to first bus, node 0, (Shunt Wye Connection) ' +
- 'except when Bus2 is specifically defined.' + CRLF + CRLF +
- 'Not necessary to specify for delta (LL) connection';
- PropertyHelp^[3] := 'Number of phases.';
- PropertyHelp^[4] := 'Total kvar, all phases. Evenly divided among phases. Only determines X. Specify R separately';
- PropertyHelp^[5] := 'For 2, 3-phase, kV phase-phase. Otherwise specify actual coil rating.';
- PropertyHelp^[6] := '={wye | delta |LN |LL} Default is wye, which is equivalent to LN. If Delta, then only one terminal.';
- PropertyHelp^[7] := 'Resistance matrix, lower triangle, ohms at base frequency. Order of the matrix is the number of phases. ' +
- 'Mutually exclusive to specifying parameters by kvar or X.';
- PropertyHelp^[8] := 'Reactance matrix, lower triangle, ohms at base frequency. Order of the matrix is the number of phases. ' +
- 'Mutually exclusive to specifying parameters by kvar or X.';
- PropertyHelp^[9] := '{Yes | No} Default=No. Indicates whether Rmatrix and Xmatrix are to be considered in parallel. ' +
- 'Default is series. For other models, specify R and Rp.';
- PropertyHelp^[10] := 'Resistance (in series with reactance), each phase, ohms. ' +
- 'This property applies to REACTOR specified by either kvar or X. See also help on Z.';
- PropertyHelp^[11] := 'Reactance, each phase, ohms at base frequency. See also help on Z and LmH properties.';
- PropertyHelp^[12] := 'Resistance in parallel with R and X (the entire branch). Assumed infinite if not specified.';
- PropertyHelp^[13] := 'Positive-sequence impedance, ohms, as a 2-element array representing a complex number. Example: ' + CRLF + CRLF +
- 'Z1=[1, 2] ! represents 1 + j2 ' + CRLF + CRLF +
- 'If defined, Z1, Z2, and Z0 are used to define the impedance matrix of the REACTOR. ' +
- 'Z1 MUST BE DEFINED TO USE THIS OPTION FOR DEFINING THE MATRIX.' + CRLF + CRLF +
- 'Side Effect: Sets Z2 and Z0 to same values unless they were previously defined.';
- PropertyHelp^[14] := 'Negative-sequence impedance, ohms, as a 2-element array representing a complex number. Example: ' + CRLF + CRLF +
- 'Z2=[1, 2] ! represents 1 + j2 ' + CRLF + CRLF +
- 'Used to define the impedance matrix of the REACTOR if Z1 is also specified. ' + CRLF + CRLF +
- 'Note: Z2 defaults to Z1 if it is not specifically defined. If Z2 is not equal to Z1, the impedance matrix is asymmetrical.';
- PropertyHelp^[15] := 'Zer0-sequence impedance, ohms, as a 2-element array representing a complex number. Example: ' + CRLF + CRLF +
- 'Z0=[3, 4] ! represents 3 + j4 ' + CRLF + CRLF +
- 'Used to define the impedance matrix of the REACTOR if Z1 is also specified. ' + CRLF + CRLF +
- 'Note: Z0 defaults to Z1 if it is not specifically defined. ';
- PropertyHelp^[16] := 'Alternative way of defining R and X properties. Enter a 2-element array representing R +jX in ohms. Example:' + CRLF + CRLF +
- 'Z=[5 10] ! equivalent to R=5 X=10 ';
- PropertyHelp^[17] := 'Name of XYCurve object, previously defined, describing per-unit variation of phase resistance, R, vs. frequency. Applies to resistance specified by R or Z property. ' +
- 'If actual values are not known, R often increases by approximately the square root of frequency.';
- PropertyHelp^[18] := 'Name of XYCurve object, previously defined, describing per-unit variation of phase inductance, L=X/w, vs. frequency. Applies to reactance specified by X, LmH, Z, or kvar property.' +
- 'L generally decreases somewhat with frequency above the base frequency, approaching a limit at a few kHz.';
- PropertyHelp^[19] := 'Inductance, mH. Alternate way to define the reactance, X, property.';
-
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // real matrix
+ PropertyType[ord(TProp.Rmatrix)] := TPropertyType.DoubleSymMatrixProperty;
+ PropertyOffset[ord(TProp.Rmatrix)] := ptruint(@obj.Rmatrix);
+ PropertyOffset2[ord(TProp.Rmatrix)] := ptruint(@obj.Fnphases);
+
+ PropertyType[ord(TProp.Xmatrix)] := TPropertyType.DoubleSymMatrixProperty;
+ PropertyOffset[ord(TProp.Xmatrix)] := ptruint(@obj.Xmatrix);
+ PropertyOffset2[ord(TProp.Xmatrix)] := ptruint(@obj.Fnphases);
+
+ // integer properties
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ // enums
+ PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.conn)] := ptruint(@obj.Connection);
+ PropertyOffset2[ord(TProp.conn)] := PtrInt(DSS.ConnectionEnum);
+
+ // bus properties
+ PropertyType[ord(TProp.bus1)] := TPropertyType.BusProperty;
+ PropertyType[ord(TProp.bus2)] := TPropertyType.BusProperty;
+ PropertyOffset[ord(TProp.bus1)] := 1;
+ PropertyOffset[ord(TProp.bus2)] := 2;
+
+ // boolean properties
+ PropertyType[ord(TProp.Parallel)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.Parallel)] := ptruint(@obj.IsParallel);
+
+ // object properties
+ PropertyType[ord(TProp.RCurve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyType[ord(TProp.LCurve)] := TPropertyType.DSSObjectReferenceProperty;
+
+ PropertyOffset[ord(TProp.RCurve)] := ptruint(@obj.RCurveObj);
+ PropertyOffset[ord(TProp.LCurve)] := ptruint(@obj.LCurveObj);
+
+ PropertyOffset2[ord(TProp.RCurve)] := ptruint(DSS.XYCurveClass);
+ PropertyOffset2[ord(TProp.LCurve)] := ptruint(DSS.XYCurveClass);
+
+ // complex properties
+ PropertyType[ord(TProp.Z)] := TPropertyType.ComplexProperty;
+ PropertyType[ord(TProp.Z0)] := TPropertyType.ComplexProperty;
+ PropertyType[ord(TProp.Z1)] := TPropertyType.ComplexProperty;
+ PropertyType[ord(TProp.Z2)] := TPropertyType.ComplexProperty;
+ PropertyOffset[ord(TProp.Z)] := ptruint(@obj.Z);
+ PropertyOffset[ord(TProp.Z0)] := ptruint(@obj.Z0);
+ PropertyOffset[ord(TProp.Z1)] := ptruint(@obj.Z1);
+ PropertyOffset[ord(TProp.Z2)] := ptruint(@obj.Z2);
+
+ // double properties (default type)
+ PropertyOffset[ord(TProp.kvar)] := ptruint(@obj.kvarRating);
+ PropertyOffset[ord(TProp.kv)] := ptruint(@obj.kvRating);
+ PropertyOffset[ord(TProp.R)] := ptruint(@obj.R);
+ PropertyOffset[ord(TProp.X)] := ptruint(@obj.X);
+ PropertyOffset[ord(TProp.Rp)] := ptruint(@obj.Rp);
+ PropertyFlags[ord(TProp.R)] := [TPropertyFlag.Redundant];
+ PropertyFlags[ord(TProp.X)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.R)] := ord(TProp.Z);
+ PropertyRedundantWith[ord(TProp.X)] := ord(TProp.Z);
+
+ // scaled double
+ PropertyOffset[ord(TProp.LmH)] := ptruint(@obj.L);
+ PropertyScale[ord(TProp.LmH)] := 1.0e-3;
+ PropertyFlags[ord(TProp.LmH)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.LmH)] := ord(TProp.X);
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
-end;
-
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TReactor.NewObject(const ObjName: String): Integer;
-begin
- // create a new object of this class and add to list
- with ActiveCircuit do
- begin
- ActiveCktElement := TReactorObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
-end;
-
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TReactor.Domatrix(var Matrix: pDoubleArray);
-var
- OrderFound, j: Integer;
- MatBuffer: pDoubleArray;
-
-begin
- with DSS.ActiveReactorObj do
- begin
- MatBuffer := Allocmem(Sizeof(Double) * Fnphases * Fnphases);
- OrderFound := Parser.ParseAsSymMatrix(Fnphases, MatBuffer);
-
- if OrderFound > 0 then // Parse was successful Else don't change Matrix
- begin {X}
- Reallocmem(Matrix, Sizeof(Matrix^[1]) * Fnphases * Fnphases);
- for j := 1 to Fnphases * Fnphases do
- Matrix^[j] := MatBuffer^[j];
- end;
-
- ReallocMem(MatBuffer, 0);
- end;
+ inherited DefineProperties;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TReactor.InterpretConnection(const S: String);
-
-// Accepts
-// delta or LL (Case insensitive)
-// Y, wye, or LN
+function TReactor.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- TestS: String;
-
+ Obj: TObj;
begin
- with DSS.ActiveReactorObj do
- begin
- TestS := lowercase(S);
- case TestS[1] of
- 'y', 'w':
- Connection := TReactorConnection.Wye; {Wye}
- 'd':
- Connection := TReactorConnection.Delta; {Delta or line-Line}
- 'l':
- case Tests[2] of
- 'n':
- Connection := TReactorConnection.Wye;
- 'l':
- Connection := TReactorConnection.Delta;
- end;
-
- end;
- case Connection of
- TReactorConnection.Delta:
- Nterms := 1; // Force reallocation of terminals
- TReactorConnection.Wye:
- if Fnterms <> 2 then
- Nterms := 2;
- end;
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TReactor.ReactorSetbus1(const s: String);
-
+procedure TReactorObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- s2: String;
+ S, S2: String;
i, dotpos: Integer;
-
- // Special handling for Bus 1
- // Set Bus2 = Bus1.0.0.0
-
begin
- with DSS.ActiveReactorObj do
- begin
- SetBus(1, S);
-
- // Default Bus2 to zero node of Bus1 if not already defined. (Wye Grounded connection)
-
- if not Bus2Defined then
+ case Idx of
+ 1:
begin
- // Strip node designations from S
- dotpos := Pos('.', S);
- if dotpos > 0 then
- S2 := Copy(S, 1, dotpos - 1)
- else
- S2 := Copy(S, 1, Length(S)); // copy up to Dot
- for i := 1 to Fnphases do
- S2 := S2 + '.0';
+ // Default Bus2 to zero node of Bus1 if not already defined. (Wye Grounded connection)
+ if (not Bus2Defined) and (Nterms > 1) then
+ begin
+ // Strip node designations from S
+ S := GetBus(1);
+ dotpos := Pos('.', S);
+ if dotpos > 0 then
+ S2 := Copy(S, 1, dotpos - 1)
+ else
+ S2 := Copy(S, 1, Length(S)); // copy up to Dot
+ for i := 1 to Fnphases do
+ S2 := S2 + '.0';
- SetBus(2, S2);
- IsShunt := TRUE;
+ SetBus(2, S2);
+ IsShunt := TRUE;
+ end;
+ PrpSequence^[2] := 0; // Reset this for save function
end;
- end;
-end;
-
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TReactor.Edit: Integer;
-
-var
- ParamPointer: Integer;
- ParamName: String;
- Param: String;
-
-begin
- Result := 0;
- // continue parsing with contents of Parser
- DSS.ActiveReactorObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveReactorObj; // use property to set this value
-
-
- with DSS.ActiveReactorObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 230);
- 1:
- ReactorSetbus1(param);
- 2:
- Setbus(2, param);
- 3:
-{ Numphases := Parser.IntValue}; // see below
- 4:
- kvarRating := Parser.Dblvalue;
- 5:
- kvRating := Parser.Dblvalue;
- 6:
- InterpretConnection(Param);
- 7:
- DoMatrix(RMatrix);
- 8:
- DoMatrix(XMatrix);
- 9:
- IsParallel := InterpretYesNo(Param);
- 10:
- R := Parser.Dblvalue;
- 11:
- X := Parser.Dblvalue;
- 12:
- Rp := Parser.Dblvalue;
- 13:
- Z1 := InterpretComplex(Param);
- 14:
- Z2 := InterpretComplex(Param);
- 15:
- Z0 := InterpretComplex(Param);
- 16:
- Z := InterpretComplex(Param);
- 17:
- RCurve := Param;
- 18:
- LCurve := Param;
- 19:
- L := Parser.DblValue / 1000.0; // convert from mH to H
- else
- // Inherited Property Edits
- ClassEdit(DSS.ActiveReactorObj, ParamPointer - NumPropsThisClass)
+ 2:
+ if AnsiCompareText(StripExtension(GetBus(1)), StripExtension(GetBus(2))) <> 0 then
+ begin
+ IsShunt := FALSE;
+ Bus2Defined := TRUE;
end;
-
- // Some specials ...
- case ParamPointer of
- 1:
- begin
- PropertyValue[2] := GetBus(2); // this gets modified
- PrpSequence^[2] := 0; // Reset this for save function
- end;
- 2:
- if CompareText(StripExtension(GetBus(1)), StripExtension(GetBus(2))) <> 0 then
- begin
- IsShunt := FALSE;
- Bus2Defined := TRUE;
- end;
- 3:
- if Fnphases <> Parser.IntValue then
- begin
- Nphases := Parser.IntValue;
- NConds := Fnphases; // Force Reallocation of terminal info
- Yorder := Fnterms * Fnconds;
- end;
- 4:
- SpecType := 1; // X specified by kvar, kV
- 7, 8:
- SpecType := 3;
- 11:
- SpecType := 2; // X specified directly rather than computed from kvar
- 12:
- RpSpecified := TRUE;
- 13:
- begin
- SpecType := 4; // have to set Z1 to get this mode
- if not Z2Specified then
- Z2 := Z1;
- if not Z0Specified then
- Z0 := Z1;
- end;
- 14:
- Z2Specified := TRUE;
- 15:
- Z0Specified := TRUE;
- 16:
- begin
- R := Z.re;
- X := Z.im;
- SpecType := 2;
- end;
- 17:
- RCurveObj := DSS.XYCurveClass.Find(RCurve);
- 18:
- LCurveObj := DSS.XYCurveClass.Find(LCurve);
- 19:
- begin
- SpecType := 2;
- X := L * TwoPi * BaseFrequency;
- end
- else
+ 3:
+ if Fnphases <> previousIntVal then
+ begin
+ NConds := Fnphases; // Force Reallocation of terminal info
+ Yorder := Fnterms * Fnconds;
end;
-
- //YPrim invalidation on anything that changes impedance values
- case ParamPointer of
- 3..16:
- YprimInvalid := TRUE;
- 17:
- if RCurveObj = NIL then
- DoSimpleMsg('Resistance-frequency curve XYCurve.' + RCurve + ' not Found.', 2301);
- 18:
- if LCurveObj = NIL then
- DoSimpleMsg('Inductance-frequency curve XYCurve.' + LCurve + ' not Found.', 2301);
- 19:
- YprimInvalid := TRUE;
- else
+ 4:
+ SpecType := 1; // X specified by kvar, kV
+ ord(TProp.conn):
+ case Connection of
+ TReactorConnection.Delta:
+ Nterms := 1; // Force reallocation of terminals
+ TReactorConnection.Wye:
+ if Fnterms <> 2 then
+ Nterms := 2;
end;
-
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ 7, 8:
+ SpecType := 3;
+ 11:
+ SpecType := 2; // X specified directly rather than computed from kvar
+ 12:
+ RpSpecified := TRUE;
+ 13:
+ begin
+ SpecType := 4; // have to set Z1 to get this mode
+ if not Z2Specified then
+ Z2 := Z1;
+ if not Z0Specified then
+ Z0 := Z1;
end;
-
- RecalcElementData;
+ 14:
+ Z2Specified := TRUE;
+ 15:
+ Z0Specified := TRUE;
+ 16:
+ begin
+ R := Z.re;
+ X := Z.im;
+ SpecType := 2;
+ end;
+ 19:
+ begin
+ SpecType := 2;
+ X := L * TwoPi * BaseFrequency;
+ end
end;
+ //YPrim invalidation on anything that changes impedance values
+ case Idx of
+ 3..16:
+ YprimInvalid := TRUE;
+ 19:
+ YprimInvalid := TRUE;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TReactor.MakeLike(const ReactorName: String): Integer;
+procedure TReactorObj.MakeLike(OtherPtr: Pointer);
var
- OtherReactor: TReactorObj;
+ Other: TObj;
i: Integer;
begin
- Result := 0;
- {See if we can find this Reactor name in the present collection}
- OtherReactor := Find(ReactorName);
- if OtherReactor <> NIL then
- with DSS.ActiveReactorObj do
- begin
-
- if Fnphases <> OtherReactor.Fnphases then
- begin
- NPhases := OtherReactor.Fnphases;
- NConds := Fnphases; // force reallocation of terminals and conductors
-
- Yorder := Fnconds * Fnterms;
- YPrimInvalid := TRUE;
-
- end;
+ inherited MakeLike(OtherPtr); // Take care of inherited class properties
- R := OtherReactor.R;
- X := OtherReactor.X;
- Rp := OtherReactor.Rp;
+ Other := TObj(OtherPtr);
+ if Fnphases <> Other.Fnphases then
+ begin
+ FNPhases := Other.Fnphases;
+ NConds := Fnphases; // force reallocation of terminals and conductors
- RpSpecified := OtherReactor.RpSpecified;
- IsParallel := OtherReactor.IsParallel;
+ Yorder := Fnconds * Fnterms;
+ YPrimInvalid := TRUE;
+ end;
- kvarrating := OtherReactor.kvarrating;
- kvrating := OtherReactor.kvrating;
- Connection := OtherReactor.Connection;
- SpecType := OtherReactor.SpecType;
+ R := Other.R;
+ X := Other.X;
+ Rp := Other.Rp;
- Z := OtherReactor.Z;
- Z1 := OtherReactor.Z1;
- Z2 := OtherReactor.Z2;
- Z0 := OtherReactor.Z0;
- Z2Specified := OtherReactor.Z2Specified;
- Z0Specified := OtherReactor.Z0Specified;
+ RpSpecified := Other.RpSpecified;
+ IsParallel := Other.IsParallel;
+ kvarrating := Other.kvarrating;
+ kvrating := Other.kvrating;
+ Connection := Other.Connection;
+ SpecType := Other.SpecType;
- RCurve := OtherReactor.RCurve;
- RCurveobj := OtherReactor.RCurveobj;
- LCurve := OtherReactor.LCurve;
- LCurveobj := OtherReactor.LCurveobj;
+ Z := Other.Z;
+ Z1 := Other.Z1;
+ Z2 := Other.Z2;
+ Z0 := Other.Z0;
+ Z2Specified := Other.Z2Specified;
+ Z0Specified := Other.Z0Specified;
- if OtherReactor.Rmatrix = NIL then
- Reallocmem(Rmatrix, 0)
- else
- begin
- Reallocmem(Rmatrix, SizeOf(Rmatrix^[1]) * Fnphases * Fnphases);
- for i := 1 to Fnphases * Fnphases do
- Rmatrix^[i] := OtherReactor.Rmatrix^[i];
- end;
+ RCurveobj := Other.RCurveobj;
+ LCurveobj := Other.LCurveobj;
- if OtherReactor.Xmatrix = NIL then
- Reallocmem(Xmatrix, 0)
- else
- begin
- Reallocmem(Xmatrix, SizeOf(Xmatrix^[1]) * Fnphases * Fnphases);
- for i := 1 to Fnphases * Fnphases do
- Xmatrix^[i] := OtherReactor.Xmatrix^[i];
- end;
-
- ClassMakeLike(OtherReactor); // Take care of inherited class properties
-
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherReactor.PropertyValue[i];
- Result := 1;
- end
+ if Other.Rmatrix = NIL then
+ Reallocmem(Rmatrix, 0)
else
- DoSimpleMsg('Error in Reactor MakeLike: "' + ReactorName + '" Not Found.', 231);
-
+ begin
+ Reallocmem(Rmatrix, SizeOf(Rmatrix^[1]) * Fnphases * Fnphases);
+ for i := 1 to Fnphases * Fnphases do
+ Rmatrix^[i] := Other.Rmatrix^[i];
+ end;
+ if Other.Xmatrix = NIL then
+ Reallocmem(Xmatrix, 0)
+ else
+ begin
+ Reallocmem(Xmatrix, SizeOf(Xmatrix^[1]) * Fnphases * Fnphases);
+ for i := 1 to Fnphases * Fnphases do
+ Xmatrix^[i] := Other.Xmatrix^[i];
+ end;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TReactor Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
constructor TReactorObj.Create(ParClass: TDSSClass; const ReactorName: String);
-
begin
inherited Create(ParClass);
- Name := LowerCase(ReactorName);
+ Name := AnsiLowerCase(ReactorName);
DSSObjType := ParClass.DSSClassType;
- NPhases := 3; // Directly set conds and phases
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
Nterms := 2; // Force allocation of terminals and conductors
@@ -652,17 +450,12 @@ constructor TReactorObj.Create(ParClass: TDSSClass; const ReactorName: String);
HrsToRepair := 3.0;
Yorder := Fnterms * Fnconds;
- RCurve := '';
RCurveObj := NIL;
- LCurve := '';
LCurveObj := NIL;
RecalcElementData;
-
- InitPropertyValues(0);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TReactorObj.Destroy;
begin
ReallocMem(Rmatrix, 0);
@@ -672,16 +465,12 @@ destructor TReactorObj.Destroy;
inherited destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TReactorObj.RecalcElementData;
var
KvarPerPhase, PhasekV: Double;
i, CheckError: Integer;
-
begin
-
case SpecType of
-
1:
begin // kvar
kvarPerPhase := kvarRating / Fnphases;
@@ -702,7 +491,7 @@ procedure TReactorObj.RecalcElementData;
end;
X := SQR(PhasekV) * 1000.0 / kvarPerPhase;
L := X / twopi / BaseFrequency;
- {Leave R as specified}
+ // Leave R as specified
NormAmps := kvarPerPhase / PhasekV;
EmergAmps := NormAmps * 1.35;
end;
@@ -724,40 +513,35 @@ procedure TReactorObj.RecalcElementData;
if IsParallel and (SpecType = 3) then
begin
-
ReAllocmem(Gmatrix, SizeOf(Gmatrix^[1]) * Fnphases * Fnphases);
ReAllocmem(Bmatrix, SizeOf(Bmatrix^[1]) * Fnphases * Fnphases);
- {Copy Rmatrix to Gmatrix and Invert}
+ // Copy Rmatrix to Gmatrix and Invert
for i := 1 to Fnphases * Fnphases do
Gmatrix^[i] := RMatrix^[i];
// should be Gmatrix ETKInvert(Rmatrix, Fnphases, CheckError);
ETKInvert(Gmatrix, Fnphases, CheckError);
if CheckError > 0 then
begin
- DoSimpleMsg('Error inverting R Matrix for Reactor.' + name + ' - G is zeroed.', 232);
+ DoSimpleMsg('Error inverting R Matrix for "%s" - G is zeroed.', [FullName], 232);
for i := 1 to Fnphases * Fnphases do
Gmatrix^[i] := 0.0;
end;
- {Copy Xmatrix to Bmatrix and Invert}
+ // Copy Xmatrix to Bmatrix and Invert
for i := 1 to Fnphases * Fnphases do
Bmatrix^[i] := -XMatrix^[i];
ETKInvert(Bmatrix, Fnphases, CheckError);
if CheckError > 0 then
begin
- DoSimpleMsg('Error inverting X Matrix for Reactor.' + name + ' - B is zeroed.', 233);
+ DoSimpleMsg('Error inverting X Matrix for "%s" - B is zeroed.', [FullName], 233);
for i := 1 to Fnphases * Fnphases do
Bmatrix^[i] := 0.0;
end;
end;
-
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TReactorObj.CalcYPrim;
-
var
Value, Value1, Value2: Complex;
Calpha1, CAlpha2: Complex;
@@ -766,15 +550,11 @@ procedure TReactorObj.CalcYPrim;
FreqMultiplier: Double;
ZValues: pComplexArray;
YPrimTemp,
- ZMatrix{, Ymatrix }: TCMatrix;
+ ZMatrix: TCMatrix;
RValue, LValue: Double;
-
begin
-
-// Normally build only Yprim Shunt, but if there are 2 terminals and
-// Bus1 <> Bus 2
-
-
+ // Normally build only Yprim Shunt, but if there are 2 terminals and
+ // Bus1 <> Bus 2
if (Yprim = NIL) OR (Yprim.order <> Yorder) {YPrimInvalid} then
begin // Reallocate YPrim if something has invalidated old allocation
if YPrim_Shunt <> NIL then
@@ -802,11 +582,10 @@ procedure TReactorObj.CalcYPrim;
with YPrimTemp do
begin
-
FYprimFreq := ActiveCircuit.Solution.Frequency;
FreqMultiplier := FYprimFreq / BaseFrequency;
- {If GIC simulation, Resistance Only }
+ // If GIC simulation, Resistance Only
if ActiveCircuit.Solution.Frequency < 0.51 then
begin // 0.5 Hz is cutoff
if X > 0.0 then
@@ -816,7 +595,7 @@ procedure TReactorObj.CalcYPrim;
FreqMultiplier := 0.0; // sets reactance part to zero
end;
- { Now, Put in Yprim matrix }
+ // Now, Put in Yprim matrix
case SpecType of
@@ -835,13 +614,13 @@ procedure TReactorObj.CalcYPrim;
Value := Cinv(Cmplx(RValue, LValue * Twopi * FYprimFreq));
// Add in Rp Value if specified
if RpSpecified then
- Caccum(Value, Cmplx(Gp, 0.0));
+ Value += Cmplx(Gp, 0.0);
case Connection of
TReactorConnection.Delta:
begin // Line-Line
- Value2 := CmulReal(Value, 2.0);
- Value := cnegate(Value);
+ Value2 := Value * 2.0;
+ Value := -Value;
for i := 1 to Fnphases do
begin
SetElement(i, i, Value2);
@@ -856,7 +635,7 @@ procedure TReactorObj.CalcYPrim;
begin
SetElement(i, i, Value); // Elements are only on the diagonals
SetElement(i + Fnphases, i + Fnphases, Value);
- SetElemSym(i, i + Fnphases, cnegate(Value));
+ SetElemSym(i, i + Fnphases, -Value);
end;
end;
end;
@@ -865,45 +644,45 @@ procedure TReactorObj.CalcYPrim;
3:
begin // Z matrix specified
- {Compute Z matrix}
+ //Compute Z matrix
- { Put in Parallel R & L }
+ // Put in Parallel R & L
if IsParallel then
- begin {Build Z as a Y Matrix}
-
+ begin // Build Z as a Y Matrix
for i := 1 to Fnphases do
begin
for j := 1 to Fnphases do
begin
idx := (j - 1) * Fnphases + i;
- {FreqMultiplier = 0 signifies GIC model where we only need R part}
+ // FreqMultiplier = 0 signifies GIC model where we only need R part
if FreqMultiplier > 0.0 then
Value := Cmplx(Gmatrix^[idx], Bmatrix^[idx] / FreqMultiplier)
else
Value := Cmplx(Gmatrix^[idx], 0.0);
SetElement(i, j, Value);
SetElement(i + Fnphases, j + Fnphases, Value);
- SetElemSym(i, j + Fnphases, Cnegate(Value));
+ SetElemSym(i, j + Fnphases, -Value);
end;
end;
end
else
- begin {For Series R and X}
+ begin // For Series R and X
Zmatrix := TcMatrix.CreateMatrix(Fnphases);
ZValues := Zmatrix.GetValuesArrayPtr(Fnphases); // So we can stuff array fast
- { Put in Series R & L }
+ // Put in Series R & L
for i := 1 to Fnphases * Fnphases do
begin
- // Correct the impedances for frequency
+ // Correct the impedances for frequency
ZValues^[i] := Cmplx(RMatrix^[i], Xmatrix^[i] * FreqMultiplier);
end;
- ZMatrix.Invert; {Invert in place - is now Ymatrix}
+ ZMatrix.Invert; // Invert in place - is now Ymatrix
if ZMatrix.InvertError > 0 then
- begin {If error, put in tiny series conductance}
- DoErrorMsg('TReactorObj.CalcYPrim', 'Matrix Inversion Error for Reactor "' + Name + '"',
- 'Invalid impedance specified. Replaced with tiny conductance.', 234);
+ begin // If error, put in tiny series conductance
+ DoErrorMsg('TReactorObj.CalcYPrim',
+ Format(_('Matrix Inversion Error for Reactor "%s"'), [Name]),
+ _('Invalid impedance specified. Replaced with tiny conductance.'), 234);
ZMatrix.Clear;
for i := 1 to Fnphases do
ZMatrix.SetElement(i, i, Cmplx(epsilon, 0.0));
@@ -916,7 +695,7 @@ procedure TReactorObj.CalcYPrim;
Value := Zmatrix.GetElement(i, j);
SetElement(i, j, Value);
SetElement(i + Fnphases, j + Fnphases, Value);
- SetElemSym(i, j + Fnphases, Cnegate(Value));
+ SetElemSym(i, j + Fnphases, -Value);
end;
end;
@@ -926,72 +705,12 @@ procedure TReactorObj.CalcYPrim;
4:
begin // Symmetrical component Z's specified
-
-(***
-
- parallel doesn't make sense
- If IsParallel Then
- Begin
-
- If Cabs(Z0) > 0.0 Then Y0 := Cinv(Z0) Else Y0 := Cmplx(1.0e12, 0.0);
- If Cabs(Z1) > 0.0 Then Y1 := Cinv(Z1) Else Y1 := Cmplx(1.0e12, 0.0);
- If Cabs(Z2) > 0.0 Then Y2 := Cinv(Z2) Else Y2 := Cmplx(1.0e12, 0.0);
-
- {Assumes the sequence networks are in parallel}
- Ymatrix := TcMatrix.CreateMatrix(Fnphases);
-
- // diagonal elements -- all the same
- If Fnphases=1 Then // assume positive sequence only model
- Value := Y1
- Else
- Value := Cadd(Y2, Cadd(Y1, Y0));
-
- Value.im := Value.im / FreqMultiplier; // Correct the impedances for frequency
- Value := CdivReal(Value, 3.0);
- With Ymatrix Do FOR i := 1 to Fnphases Do SetElement(i, i, Value);
-
-
-
- If FnPhases = 3 Then // otherwise undefined
- Begin
- Calpha1 := Conjg(Calpha); // Change it to agree with textbooks
- Calpha2 := Cmul(Calpha1, Calpha1); // Alpha squared = 1 /_ 240 = 1/_-120
- Value2 := Cadd(Cmul(Calpha2,Y2),Cadd(Cmul(Calpha1, Y1), Y0));
- Value1 := Cadd(Cmul(Calpha2,Y1),Cadd(Cmul(Calpha1, Y2), Y0));
-
- Value1.im := Value1.im / FreqMultiplier; // Correct the impedances for frequency
- Value2.im := Value2.im / FreqMultiplier; // Correct the impedances for frequency
-
- Value1 := CdivReal(Value1, 3.0);
- Value2 := CdivReal(Value2, 3.0);
- With Ymatrix Do Begin
- //Lower Triangle
- SetElement(2, 1, Value1);
- SetElement(3, 1, Value2);
- SetElement(3, 2, Value1);
- //Upper Triangle
- SetElement(1, 2, Value2);
- SetElement(1, 3, Value1);
- SetElement(2, 3, Value2);
- End;
- End;
-
- FOR i := 1 to Fnphases Do BEGIN // could be asymmetric
- FOR j := 1 to Fnphases Do BEGIN
- Value := Ymatrix.GetElement(i,j);
- SetElement(i, j, Value);
- SetElement(i+Fnphases, j+Fnphases, Value);
- SetElement(i, j+Fnphases, Cnegate(Value));
- SetElement(i+Fnphases, j, Cnegate(Value));
- END;
- END;
-
- Ymatrix.Free;
-
- End
- Else Begin
-***)
- {Series R+jX }
+//
+// parallel doesn't make sense
+// If IsParallel Then
+// ...
+
+ // Series R+jX
Zmatrix := TcMatrix.CreateMatrix(Fnphases);
@@ -999,10 +718,10 @@ procedure TReactorObj.CalcYPrim;
if Fnphases = 1 then // assume positive sequence only model
Value := Z1
else
- Value := Cadd(Z2, Cadd(Z1, Z0));
+ Value := Z2 + Z1 + Z0;
Value.im := Value.im * FreqMultiplier; // Correct the impedances for frequency
- Value := CdivReal(Value, 3.0);
+ Value := Value / 3.0;
for i := 1 to Fnphases do
begin
Zmatrix.SetElement(i, i, Value)
@@ -1010,27 +729,26 @@ procedure TReactorObj.CalcYPrim;
if FnPhases = 3 then // otherwise undefined
begin
+ // There are two possible off-diagonal elements if Z1 <> Z2
+ // Calpha is defined as 1 /_ -120 instead of 1 /_ 120
- // There are two possible off-diagonal elements if Z1 <> Z2
- // Calpha is defined as 1 /_ -120 instead of 1 /_ 120
-
- Calpha1 := Conjg(Calpha); // Change it to agree with textbooks
- Calpha2 := Cmul(Calpha1, Calpha1); // Alpha squared = 1 /_ 240 = 1/_-120
- Value2 := Cadd(Cmul(Calpha2, Z2), Cadd(Cmul(Calpha1, Z1), Z0));
- Value1 := Cadd(Cmul(Calpha2, Z1), Cadd(Cmul(Calpha1, Z2), Z0));
+ Calpha1 := cong(Calpha); // Change it to agree with textbooks
+ Calpha2 := Calpha1 * Calpha1; // Alpha squared = 1 /_ 240 = 1/_-120
+ Value2 := Calpha2 * Z2 + Calpha1 * Z1 + Z0;
+ Value1 := Calpha2 * Z1 + Calpha1 * Z2 + Z0;
Value1.im := Value1.im * FreqMultiplier; // Correct the impedances for frequency
Value2.im := Value2.im * FreqMultiplier; // Correct the impedances for frequency
- Value1 := CdivReal(Value1, 3.0);
- Value2 := CdivReal(Value2, 3.0);
+ Value1 := Value1/ 3.0;
+ Value2 := Value2/ 3.0;
with Zmatrix do
begin
- //Lower Triangle
+ //Lower Triangle
SetElement(2, 1, Value1);
SetElement(3, 1, Value2);
SetElement(3, 2, Value1);
- //Upper Triangle
+ //Upper Triangle
SetElement(1, 2, Value2);
SetElement(1, 3, Value1);
SetElement(2, 3, Value2);
@@ -1038,11 +756,12 @@ procedure TReactorObj.CalcYPrim;
end;
- ZMatrix.Invert; {Invert in place - is now Ymatrix}
+ ZMatrix.Invert; // Invert in place - is now Ymatrix
if ZMatrix.InvertError > 0 then
- begin {If error, put in tiny series conductance}
- DoErrorMsg('TReactorObj.CalcYPrim', 'Matrix Inversion Error for Reactor "' + Name + '"',
- 'Invalid impedance specified. Replaced with tiny conductance.', 234);
+ begin // If error, put in tiny series conductance
+ DoErrorMsg('TReactorObj.CalcYPrim',
+ Format(_('Matrix Inversion Error for Reactor "%s"'), [Name]),
+ _('Invalid impedance specified. Replaced with tiny conductance.'), 234);
ZMatrix.Clear;
for i := 1 to Fnphases do
ZMatrix.SetElement(i, i, Cmplx(epsilon, 0.0));
@@ -1055,8 +774,8 @@ procedure TReactorObj.CalcYPrim;
Value := Zmatrix.GetElement(i, j);
SetElement(i, j, Value);
SetElement(i + Fnphases, j + Fnphases, Value);
- SetElement(i, j + Fnphases, Cnegate(Value));
- SetElement(i + Fnphases, j, Cnegate(Value));
+ SetElement(i, j + Fnphases, -Value);
+ SetElement(i + Fnphases, j, -Value);
end;
end;
@@ -1065,8 +784,7 @@ procedure TReactorObj.CalcYPrim;
end;
// END;
end;
-
- end; {With YPRIM}
+ end;
// Set YPrim_Series based on diagonals of YPrim_shunt so that CalcVoltages doesn't fail
if IsShunt then
@@ -1076,22 +794,20 @@ procedure TReactorObj.CalcYPrim;
Yprim_Series.SetElement(i, i, Yprim_Shunt.Getelement(i, i))
else
for i := 1 to Yorder do
- Yprim_Series.SetElement(i, i, CmulReal(Yprim_Shunt.Getelement(i, i), 1.0e-10));
+ Yprim_Series.SetElement(i, i, Yprim_Shunt.Getelement(i, i) * 1.0e-10);
end;
Yprim.Copyfrom(YPrimTemp);
- {Don't Free YPrimTemp - It's just a pointer to an existing complex matrix}
+ // Don't Free YPrimTemp - It's just a pointer to an existing complex matrix
inherited CalcYPrim;
YprimInvalid := FALSE;
end;
-procedure TReactorObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
+procedure TReactorObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i, j, k: Integer;
-
begin
inherited DumpProperties(F, Complete);
@@ -1139,31 +855,28 @@ procedure TReactorObj.DumpProperties(F: TFileStream; Complete: Boolean);
FSWriteln(F, '~ ' + PropertyName^[k] + '=' + PropertyValue[k]);
end;
end;
-
end;
procedure TReactorObj.GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Complex);
var
i: Integer;
begin
-
{Only report No Load Losses if Rp defined and Reactor is a shunt device;
Else do default behavior.}
if (RpSpecified and IsShunt and (Rp <> 0.0)) then
begin
-
TotalLosses := Losses; // Side effect: computes Iterminal and Vterminal
{Compute losses in Rp Branch from voltages across shunt element -- node to ground}
NoLoadLosses := CZERO;
with ActiveCircuit.Solution do
for i := 1 to FNphases do
with NodeV^[NodeRef^[i]] do
- Caccum(NoLoadLosses, cmplx((SQR(re) + SQR(im)) / Rp, 0.0)); // V^2/Rp
+ NoLoadLosses += cmplx((SQR(re) + SQR(im)) / Rp, 0.0); // V^2/Rp
if ActiveCircuit.PositiveSequence then
- CmulReal(NoLoadLosses, 3.0);
- LoadLosses := Csub(TotalLosses, NoLoadLosses); // Subtract no load losses from total losses
+ NoLoadLosses := NoLoadLosses * 3.0;
+ LoadLosses := TotalLosses - NoLoadLosses; // Subtract no load losses from total losses
end
@@ -1172,80 +885,19 @@ procedure TReactorObj.GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Compl
end;
-function TReactorObj.GetPropertyValue(Index: Integer): String;
-begin
-
- case Index of
- 10:
- Result := Format('%-.8g', [R]);
- 11:
- Result := Format('%-.8g', [X]);
- {Special cases for array properties}
- 13:
- Result := Format('[%-.8g, %-.8g]', [Z1.re, Z1.im]);
- 14:
- Result := Format('[%-.8g, %-.8g]', [Z2.re, Z2.im]);
- 15:
- Result := Format('[%-.8g, %-.8g]', [Z0.re, Z0.im]);
- 16:
- Result := Format('[%-.8g, %-.8g]', [R, X]);
- 19:
- Result := Format('%-.8g', [L * 1000.0]);
- else
- Result := inherited GetPropertyValue(index);
- end;
-
-end;
-
-procedure TReactorObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := GetBus(1);
- PropertyValue[2] := GetBus(2);
- PropertyValue[3] := '3';
- PropertyValue[4] := '1200';
- PropertyValue[5] := '12.47';
- PropertyValue[6] := 'wye';
- PropertyValue[7] := '';
- PropertyValue[8] := '';
- PropertyValue[9] := 'NO'; // Parallel
- PropertyValue[10] := '0'; // R series
- PropertyValue[11] := Format('%-.6g', [X]); //X
- PropertyValue[12] := '0'; //Rp
- PropertyValue[13] := '[0 0]'; //Z1
- PropertyValue[14] := '[0 0]'; //Z2
- PropertyValue[15] := '[0 0]'; //Z0
- PropertyValue[16] := '[0 0]'; //Z
- PropertyValue[17] := '';
- PropertyValue[18] := '';
- PropertyValue[19] := Format('%-.8g', [X / TwoPi / BaseFrequency * 1000.0]); //X
-
- inherited InitPropertyValues(NumPropsThisClass);
-
- // Override Inherited properties
- PropertyValue[NumPropsThisClass + 1] := Str_Real(Normamps, 0);
- PropertyValue[NumPropsThisClass + 2] := Str_Real(Emergamps, 0);
- PropertyValue[NumPropsThisClass + 3] := Str_Real(FaultRate, 0);
- PropertyValue[NumPropsThisClass + 4] := Str_Real(PctPerm, 0);
- PropertyValue[NumPropsThisClass + 5] := Str_Real(HrsToRepair, 0);
-
- ClearPropSeqArray;
-
-end;
-
-
-procedure TReactorObj.MakePosSequence;
+procedure TReactorObj.MakePosSequence();
var
- S: String;
- kvarperphase, phasekV, Rs, Rm: Double;
- i, j: Integer;
-
+ kvarperphase, phasekV, Rs, Rm, R, X: Double;
+ changes, i, j: Integer;
begin
- {If FnPhases>1 Then }
+ changes := 1;
+ BeginEdit(True);
+ // If FnPhases>1 Then
begin
- S := ' ';
case SpecType of
-
+ 2, // R + j X
+ 4: // symmetrical components Z1 specified
+ SetInteger(ord(TProp.Phases), 1);
1:
begin // kvar
kvarPerPhase := kvarRating / 3.0; // divide among 3 phases Fnphases;
@@ -1254,19 +906,16 @@ procedure TReactorObj.MakePosSequence;
else
PhasekV := kVRating;
- S := 'Phases=1 ' + Format(' kV=%-.5g kvar=%-.5g', [PhasekV, kvarPerPhase]);
- {Leave R as specified}
-
- end;
- 2:
- begin // R + j X
- S := 'Phases=1 ';
+ SetInteger(ord(TProp.Phases), 1);
+ SetDouble(ord(TProp.kV), PhasekV);
+ SetDouble(ord(TProp.kvar), kvarPerPhase);
+ changes := 3;
+ // Leave R as specified
end;
3:
if FnPhases > 1 then
begin // Matrices
- S := 'Phases=1 ';
- // R1
+ // R1
Rs := 0.0; // Avg Self
for i := 1 to FnPhases do
Rs := Rs + Rmatrix^[(i - 1) * Fnphases + i];
@@ -1276,10 +925,9 @@ procedure TReactorObj.MakePosSequence;
for j := i to FnPhases do
Rm := Rm + Rmatrix^[(i - 1) * Fnphases + j];
Rm := Rm / (FnPhases * (Fnphases - 1.0) / 2.0);
+ R := (Rs - Rm);
- S := S + Format(' R=%-.5g', [(Rs - Rm)]);
-
- // X1
+ // X1
Rs := 0.0; // Avg Self
for i := 1 to FnPhases do
Rs := Rs + Xmatrix^[(i - 1) * Fnphases + i];
@@ -1289,25 +937,17 @@ procedure TReactorObj.MakePosSequence;
for j := i to FnPhases do
Rm := Rm + Xmatrix^[(i - 1) * Fnphases + j];
Rm := Rm / (FnPhases * (Fnphases - 1.0) / 2.0);
+ X := (Rs - Rm);
- S := S + Format(' X=%-.5g', [(Rs - Rm)]);
-
+ SetInteger(ord(TProp.Phases), 1);
+ SetDouble(ord(TProp.R), R);
+ SetDouble(ord(TProp.X), X);
+ changes := 3;
end;
- 4:
- begin // symmetrical components Z1 specified
- S := 'Phases=1 ';
- end;
-
end;
-
- Parser.CmdString := S;
- Edit;
-
end;
-
-
+ EndEdit(changes);
inherited;
-
end;
-end.
+end.
\ No newline at end of file
diff --git a/src/PDElements/Transformer.pas b/src/PDElements/Transformer.pas
index 94a0865aa..e2d054c7b 100644
--- a/src/PDElements/Transformer.pas
+++ b/src/PDElements/Transformer.pas
@@ -7,21 +7,6 @@
----------------------------------------------------------
}
-{
- Change log
- 1-28-00 Added tap properties so that regulator can control it.
- 1-29-00 Added GetWindingVoltages
- 2-1-00 Replaced TranParser with global AuxParser
- 2-9-00 Fixed Set_PresentTap bug
- 1-23-03 Added code to get 30 deg lag correct of y-delta transformers
- 2-18-03 changed Rneut default to open (-1)
- 2-21-03 changed automatic resetting of connection designator upon changing Rneut
- 9-12-11 Fixed pctLoadLoss problem with sequence of definition with kVA property
-}
-
-{ You can designate a transformer to be a substation by setting the sub=yes parameter}
-
-
interface
uses
@@ -31,44 +16,89 @@ interface
PDClass,
Circuit,
PDElement,
- uComplex,
+ UComplex, DSSUcomplex,
UcMatrix,
- ParserDel,
Arraydef,
+ DSSObject,
math;
type
+{$SCOPEDENUMS ON}
+ TTransfProp = (
+ INVALID = 0,
+ phases=1,
+ windings=2,
+
+ // Winding Definition
+ wdg=3,
+ bus=4,
+ conn=5,
+ kV=6,
+ kVA=7,
+ tap=8,
+ pctR=9,
+ Rneut=10,
+ Xneut=11,
+
+ // General Data
+ buses=12,
+ conns=13,
+ kVs=14,
+ kVAs=15,
+ taps=16,
+ XHL=17,
+ XHT=18,
+ XLT=19,
+ Xscarray=20,
+
+ thermal=21,
+ n=22,
+ m=23,
+ flrise=24,
+ hsrise=25,
+ pctloadloss=26,
+ pctnoloadloss=27,
+ normhkVA=28,
+ emerghkVA=29,
+ sub=30,
+ MaxTap=31,
+ MinTap=32,
+ NumTaps=33,
+ subname=34,
+ pctimag=35,
+ ppm_antifloat=36,
+
+ pctRs=37,
+
+ bank=38,
+
+ XfmrCode=39,
+
+ XRConst=40,
+ X12=41,
+ X13=42,
+ X23=43,
+ LeadLag=44,
+ WdgCurrents=45,
+ Core=46,
+ RdcOhms=47,
+
+ Seasons=48,
+ Ratings=49
+ );
+{$SCOPEDENUMS OFF}
TTransf = class(TPDClass)
-
- PRIVATE
-
- procedure SetActiveWinding(w: Integer);
- procedure InterpretConnection(const S: String);
- procedure InterpretAllConns(const S: String);
- procedure InterpretAllBuses(const S: String);
- procedure InterpretAllTaps(const S: String);
- procedure InterpretAllkVRatings(const S: String);
- procedure InterpretAllkVARatings(const S: String);
- procedure InterpretAllRs(const S: String);
- function TrapZero(const Value: Double; DefaultValue: Double): Double;
- function InterpretLeadLag(const S: String): Boolean;
-
- {PROCEDURE MakeNewBusNameForNeutral(Var NewBusName:String; Nphases:Integer);}
PROTECTED
- procedure DefineProperties;
- function MakeLike(const TransfName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
-
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ function BeginEdit(ptr: Pointer; SetActive: Boolean=True): Pointer; override;
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
- TWinding = class(Tobject)
- PUBLIC
+ TWinding = record
Connection: Integer;
kVLL,
VBase,
@@ -82,32 +112,23 @@ TWinding = class(Tobject)
Y_PPM: Double; // Anti Float reactance adder
RdcSpecified: Boolean;
- {Tap Changer Data}
+ // Tap Changer Data
TapIncrement,
MinTap,
MaxTap: Double;
NumTaps: Integer;
procedure ComputeAntiFloatAdder(PPM_Factor, VABase1ph: Double);
-
- constructor Create;
- destructor Destroy; OVERRIDE;
+ procedure Init();
end;
WindingArray = array[1..3] of TWinding;
pWindingArray = ^WindingArray;
TTransfObj = class(TPDElement)
-{$IFDEF DSS_CAPI}
PUBLIC
-{$ELSE}
- PRIVATE
-{$ENDIF}
-
DeltaDirection: Integer;
- ppm_FloatFactor: Double; // parts per million winding float factor
- pctImag: Double;
- XRConst: Boolean;
+ XRConst: LongBool;
function Get_PresentTap(i: Integer): Double;
procedure Set_PresentTap(i: Integer; const Value: Double);
@@ -119,13 +140,11 @@ TTransfObj = class(TPDElement)
// CIM accessors
function Get_NumTaps(i: Integer): Integer;
function Get_WdgResistance(i: Integer): Double;
- function Get_WdgRdc(i: Integer): Double;
function Get_WdgConnection(i: Integer): Integer;
function Get_WdgkVA(i: Integer): Double;
function Get_Xsc(i: Integer): Double;
function Get_WdgRneutral(i: Integer): Double;
function Get_WdgXneutral(i: Integer): Double;
- function Get_WdgYPPM(i: Integer): Double;
procedure CalcY_Terminal(FreqMult: Double);
procedure GICBuildYTerminal;
@@ -133,18 +152,15 @@ TTransfObj = class(TPDElement)
procedure BuildYPrimComponent(YPrim_Component, Y_Terminal: TCMatrix);
procedure AddNeutralToY(FreqMultiplier: Double);
- procedure FetchXfmrCode(const Code: String);
+ procedure FetchXfmrCode();
PROTECTED
- NumWindings: Integer;
MaxWindings: Integer;
TermRef: pIntegerArray; // keeps track of terminal connections
- XHL, XHT, XLT: Double; // per unit
Zbase: Double;
XSC: pDoubleArray; // per unit SC measurements
- VABase: Double; // FOR impedances
ZB: TCMatrix;
Y_1Volt: TCMatrix;
@@ -154,92 +170,74 @@ TTransfObj = class(TPDElement)
Y_Terminal_Freqmult: Double;
- NormMaxHkVA: Double;
- EmergMaxHkVA: Double;
- ThermalTimeConst: Double; {hr}
- n_thermal: Double;
- m_thermal: Double; {Exponents}
- FLrise: Double;
- HSrise: Double;
- pctLoadLoss: Double;
- pctNoLoadLoss: Double;
-
- HVLeadsLV: Boolean;
+ HVLeadsLV: LongBool;
XHLChanged: Boolean;
kVARatings: Array Of Double;
procedure SetTermRef;
PUBLIC
+ NumWindings: Integer;
ActiveWinding: Integer; // public for COM interface
- IsSubstation: Boolean;
+ IsSubstation: LongBool;
SubstationName: String;
Winding: pWindingArray;
XfmrBank: String;
- XfmrCode: String;
- CoreType: Integer; {0=Shell; 1=1ph; 3-3leg; 5=5-leg}
- strCoreType: String;
+ XfmrCodeObj: TDSSObject;
+ CoreType: Integer; // 0=Shell; 1=1ph; 3-3leg; 5=5-leg
+
+ n_thermal: Double;
+ m_thermal: Double; // Exponents
+ XHL, XHT, XLT: Double; // per unit
+ NormMaxHkVA: Double;
+ EmergMaxHkVA: Double;
+ ThermalTimeConst: Double; // hr
+ FLrise: Double;
+ HSrise: Double;
+ pctLoadLoss: Double;
+ pctNoLoadLoss: Double;
+ ppm_FloatFactor: Double; // parts per million winding float factor
+ pctImag: Double;
+ VABase: Double; // FOR impedances
constructor Create(ParClass: TDSSClass; const TransfName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure SetNumWindings(N: Integer);
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE;
- {GetLosses override for Transformer}
+ // GetLosses override for Transformer
procedure GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Complex); OVERRIDE;
function RotatePhases(iPhs: Integer): Integer;
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
+ procedure DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean = False); OVERRIDE;
procedure SaveWrite(F: TFileStream); OVERRIDE;
procedure GetWindingVoltages(iWind: Integer; VBuffer: pComplexArray);
procedure GetAllWindingCurrents(CurrBuffer: pComplexArray); // All Winding currents in complex array
- function GetWindingCurrentsResult: String; // All winding currents in string
-
- procedure MakePosSequence; OVERRIDE; // Make a positive Sequence Model
+ procedure MakePosSequence(); OVERRIDE; // Make a positive Sequence Model
+ // TODO: remove most of these
property PresentTap[i: Integer]: Double READ Get_PresentTap WRITE Set_PresentTap;
property Mintap[i: Integer]: Double READ Get_MinTap;
property Maxtap[i: Integer]: Double READ Get_MaxTap;
property TapIncrement[i: Integer]: Double READ Get_TapIncrement;
property BaseVoltage[i: Integer]: Double READ Get_BaseVoltage; // Winding VBase
property BasekVLL[i: Integer]: Double READ Get_BasekVLL; // Winding VBase
-
- // CIM accessors -- TODO: move to Helper class? Better supported since FPC 3.2.0
property NumTaps[i: Integer]: Integer READ Get_NumTaps;
- property NumberOfWindings: Integer READ NumWindings;
property WdgResistance[i: Integer]: Double READ Get_WdgResistance;
- property WdgRdc[i: Integer]: Double READ Get_WdgRdc;
property WdgkVA[i: Integer]: Double READ Get_WdgkVA;
property WdgConnection[i: Integer]: Integer READ Get_WdgConnection;
property WdgRneutral[i: Integer]: Double READ Get_WdgRneutral;
property WdgXneutral[i: Integer]: Double READ Get_WdgXneutral;
- property WdgYPPM[i: Integer]: Double READ Get_WdgYPPM;
property XscVal[i: Integer]: Double READ Get_Xsc;
- property XhlVal: Double READ Xhl;
- property XhtVal: Double READ Xht;
- property XltVal: Double READ Xlt;
- property NormalHkVA: Double READ NormMaxHkVA;
- property EmergHkVA: Double READ EmergMaxHkVA;
- property thTau: Double READ ThermalTimeConst;
- property thN: Double READ n_thermal;
- property thM: Double READ m_thermal;
- property thFLRise: Double READ FLRise;
- property thHSRise: Double READ HSRise;
- property loadLossPct: Double READ pctLoadLoss;
- property noLoadLossPct: Double READ pctNoLoadLoss;
- property imagPct: Double READ pctImag;
- property ppmFloatFac: Double READ ppm_FloatFactor;
- property baseVA: Double READ VAbase;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
implementation
uses
@@ -253,760 +251,469 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TTransfObj;
+ TProp = TTransfProp;
const
- NumPropsThisClass = 49;
+ NumPropsThisClass = Ord(High(TProp));
+var
+ PropInfo: Pointer = NIL;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-constructor TTransf.Create(dssContext: TDSSContext); // Creates superstructure for all Transformer objects
+constructor TTransf.Create(dssContext: TDSSContext);
begin
- inherited Create(dssContext);
- Class_Name := 'Transformer';
- DSSClassType := DSSClassType + XFMR_ELEMENT; // override PDElement (kept in both actually)
-
- ActiveElement := 0;
-
- DefineProperties;
-
- {Make space for transformer property list}
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE; {Allow property list abbreviations}
+ if PropInfo = NIL then
+ PropInfo := TypeInfo(TProp);
+ inherited Create(dssContext, XFMR_ELEMENT, 'Transformer');
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TTransf.Destroy;
-
begin
- // ElementList and CommandList freed in inherited destroy
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTransf.DefineProperties;
+function XscSize(obj: TObj): Integer;
begin
-
- Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-{ Define Property names }
-
- PropertyName[1] := 'phases';
- PropertyName[2] := 'windings';
-
- // Winding Definition
- PropertyName[3] := 'wdg';
- PropertyName[4] := 'bus';
- PropertyName[5] := 'conn';
- PropertyName[6] := 'kV'; // FOR 2-and 3- always kVLL ELSE actual winding KV
- PropertyName[7] := 'kVA';
- PropertyName[8] := 'tap';
- PropertyName[9] := '%R';
- PropertyName[10] := 'Rneut';
- PropertyName[11] := 'Xneut';
-
- // General Data
- PropertyName[12] := 'buses';
- PropertyName[13] := 'conns';
- PropertyName[14] := 'kVs';
- PropertyName[15] := 'kVAs';
- PropertyName[16] := 'taps';
- PropertyName[17] := 'XHL';
- PropertyName[18] := 'XHT';
- PropertyName[19] := 'XLT';
- PropertyName[20] := 'Xscarray'; // x12 13 14... 23 24.. 34 ..
- PropertyName[21] := 'thermal';
- PropertyName[22] := 'n';
- PropertyName[23] := 'm';
- PropertyName[24] := 'flrise';
- PropertyName[25] := 'hsrise';
- PropertyName[26] := '%loadloss';
- PropertyName[27] := '%noloadloss';
- PropertyName[28] := 'normhkVA';
- PropertyName[29] := 'emerghkVA';
- PropertyName[30] := 'sub'; // =y/n
- PropertyName[31] := 'MaxTap';
- PropertyName[32] := 'MinTap';
- PropertyName[33] := 'NumTaps';
- PropertyName[34] := 'subname';
- PropertyName[35] := '%imag';
- PropertyName[36] := 'ppm_antifloat';
- PropertyName[37] := '%Rs';
-
- PropertyName[38] := 'bank';
- PropertyName[39] := 'XfmrCode';
- PropertyName[40] := 'XRConst';
- PropertyName[41] := 'X12';
- PropertyName[42] := 'X13';
- PropertyName[43] := 'X23';
- PropertyName[44] := 'LeadLag';
- PropertyName[45] := 'WdgCurrents';
- PropertyName[46] := 'Core';
- PropertyName[47] := 'RdcOhms';
- PropertyName[48] := 'Seasons';
- PropertyName[49] := 'Ratings';
-
-
- // define Property help values
- PropertyHelp[1] := 'Number of phases this transformer. Default is 3.';
- PropertyHelp[2] := 'Number of windings, this transformers. (Also is the number of terminals) ' +
- 'Default is 2. This property triggers memory allocation for the Transformer and will cause other properties to revert to default values.';
- // Winding Definition
- PropertyHelp[3] := 'Set this = to the number of the winding you wish to define. Then set ' +
- 'the values for this winding. Repeat for each winding. Alternatively, use ' +
- 'the array collections (buses, kVAs, etc.) to define the windings. Note: ' +
- 'reactances are BETWEEN pairs of windings; they are not the property of a single winding.';
- PropertyHelp[4] := 'Bus connection spec for this winding.';
- PropertyHelp[5] := 'Connection of this winding {wye*, Delta, LN, LL}. Default is "wye" with the neutral solidly grounded. ';
- PropertyHelp[6] := 'For 2-or 3-phase, enter phase-phase kV rating. Otherwise, kV rating of the actual winding';
- PropertyHelp[7] := 'Base kVA rating of the winding. Side effect: forces change of max normal and emerg kVA ratings.' +
- 'If 2-winding transformer, forces other winding to same value. ' +
- 'When winding 1 is defined, all other windings are defaulted to the same rating ' +
- 'and the first two winding resistances are defaulted to the %loadloss value.';
- PropertyHelp[8] := 'Per unit tap that this winding is on.';
- PropertyHelp[9] := 'Percent resistance this winding. (half of total for a 2-winding).';
- PropertyHelp[10] := 'Default = -1. Neutral resistance of wye (star)-connected winding in actual ohms. ' +
- 'If entered as a negative value, the neutral is assumed to be open, or floating. ' +
- 'To solidly ground the neutral, connect the neutral conductor to Node 0 in the Bus property spec for this winding. ' +
- 'For example: Bus=MyBusName.1.2.3.0, which is generally the default connection.';
- PropertyHelp[11] := 'Neutral reactance of wye(star)-connected winding in actual ohms. May be + or -.';
-
- // General Data
- PropertyHelp[12] := 'Use this to specify all the bus connections at once using an array. Example:' + CRLF + CRLF +
- 'New Transformer.T1 buses="Hibus, lowbus"';
- PropertyHelp[13] := 'Use this to specify all the Winding connections at once using an array. Example:' + CRLF + CRLF +
- 'New Transformer.T1 buses="Hibus, lowbus" ' +
- '~ conns=(delta, wye)';
- PropertyHelp[14] := 'Use this to specify the kV ratings of all windings at once using an array. Example:' + CRLF + CRLF +
- 'New Transformer.T1 buses="Hibus, lowbus" ' + CRLF +
- '~ conns=(delta, wye)' + CRLF +
- '~ kvs=(115, 12.47)' + CRLF + CRLF +
- 'See kV= property for voltage rules.';
- PropertyHelp[15] := 'Use this to specify the kVA ratings of all windings at once using an array.';
- PropertyHelp[16] := 'Use this to specify the p.u. tap of all windings at once using an array.';
- PropertyHelp[17] := 'Use this to specify the percent reactance, H-L (winding 1 to winding 2). Use ' +
- 'for 2- or 3-winding transformers. On the kVA base of winding 1. See also X12.';
- PropertyHelp[18] := 'Use this to specify the percent reactance, H-T (winding 1 to winding 3). Use ' +
- 'for 3-winding transformers only. On the kVA base of winding 1. See also X13.';
- PropertyHelp[19] := 'Use this to specify the percent reactance, L-T (winding 2 to winding 3). Use ' +
- 'for 3-winding transformers only. On the kVA base of winding 1. See also X23.';
- PropertyHelp[20] := 'Use this to specify the percent reactance between all pairs of windings as an array. ' +
- 'All values are on the kVA base of winding 1. The order of the values is as follows:' + CRLF + CRLF +
- '(x12 13 14... 23 24.. 34 ..) ' + CRLF + CRLF +
- 'There will be n(n-1)/2 values, where n=number of windings.';
- PropertyHelp[21] := 'Thermal time constant of the transformer in hours. Typically about 2.';
- PropertyHelp[22] := 'n Exponent for thermal properties in IEEE C57. Typically 0.8.';
- PropertyHelp[23] := 'm Exponent for thermal properties in IEEE C57. Typically 0.9 - 1.0';
- PropertyHelp[24] := 'Temperature rise, deg C, for full load. Default is 65.';
- PropertyHelp[25] := 'Hot spot temperature rise, deg C. Default is 15.';
- PropertyHelp[26] := 'Percent load loss at full load. The %R of the High and Low windings (1 and 2) are adjusted to agree at rated kVA loading.';
- PropertyHelp[27] := 'Percent no load losses at rated excitatation voltage. Default is 0. Converts to a resistance in parallel with the magnetizing impedance in each winding.';
- PropertyHelp[28] := 'Normal maximum kVA rating of H winding (winding 1). Usually 100% - 110% of' +
- 'maximum nameplate rating, depending on load shape. Defaults to 110% of kVA rating of Winding 1.';
- PropertyHelp[29] := 'Emergency (contingency) kVA rating of H winding (winding 1). Usually 140% - 150% of' +
- 'maximum nameplate rating, depending on load shape. Defaults to 150% of kVA rating of Winding 1.';
- PropertyHelp[30] := '={Yes|No} Designates whether this transformer is to be considered a substation.' +
- 'Default is No.'; // =y/n
-
- PropertyHelp[31] := 'Max per unit tap for the active winding. Default is 1.10';
- PropertyHelp[32] := 'Min per unit tap for the active winding. Default is 0.90';
- PropertyHelp[33] := 'Total number of taps between min and max tap. Default is 32 (16 raise and 16 lower taps about the neutral position). The neutral position is not counted.';
- PropertyHelp[34] := 'Substation Name. Optional. Default is null. If specified, printed on plots';
- PropertyHelp[35] := 'Percent magnetizing current. Default=0.0. Magnetizing branch is in parallel with windings in each phase. Also, see "ppm_antifloat".';
- PropertyHelp[36] := 'Default=1 ppm. Parts per million of transformer winding VA rating connected to ground to protect against accidentally floating a winding without a reference. ' +
- 'If positive then the effect is adding a very large reactance to ground. If negative, then a capacitor.';
- PropertyHelp[37] := 'Use this property to specify all the winding %resistances using an array. Example:' + CRLF + CRLF +
- 'New Transformer.T1 buses="Hibus, lowbus" ' +
- '~ %Rs=(0.2 0.3)';
- PropertyHelp[38] := 'Name of the bank this transformer is part of, for CIM, MultiSpeak, and other interfaces.';
- PropertyHelp[39] := 'Name of a library entry for transformer properties. The named XfmrCode must already be defined.';
- PropertyHelp[40] := '={Yes|No} Default is NO. Signifies whether or not the X/R is assumed contant for harmonic studies.';
- PropertyHelp[41] := 'Alternative to XHL for specifying the percent reactance from winding 1 to winding 2. Use ' +
- 'for 2- or 3-winding transformers. Percent on the kVA base of winding 1. ';
- PropertyHelp[42] := 'Alternative to XHT for specifying the percent reactance from winding 1 to winding 3. Use ' +
- 'for 3-winding transformers only. Percent on the kVA base of winding 1. ';
- PropertyHelp[43] := 'Alternative to XLT for specifying the percent reactance from winding 2 to winding 3.Use ' +
- 'for 3-winding transformers only. Percent on the kVA base of winding 1. ';
- PropertyHelp[44] := '{Lead | Lag (default) | ANSI (default) | Euro } Designation in mixed Delta-wye connections the ' +
- 'relationship between HV to LV winding. Default is ANSI 30 deg lag, e.g., Dy1 of Yd1 vector group. ' +
- 'To get typical European Dy11 connection, specify either "lead" or "Euro"';
- PropertyHelp[45] := '(Read only) Makes winding currents available via return on query (? Transformer.TX.WdgCurrents). ' +
- 'Order: Phase 1, Wdg 1, Wdg 2, ..., Phase 2 ...';
- PropertyHelp[46] := '{Shell*|5-leg|3-Leg|1-phase} Core Type. Used for GIC analysis';
- PropertyHelp[47] := 'Winding dc resistance in OHMS. Useful for GIC analysis. From transformer test report. ' +
- 'Defaults to 85% of %R property';
- PropertyHelp[48] := 'Defines the number of ratings to be defined for the transfomer, to be used only when defining seasonal ratings using the "Ratings" property.';
- PropertyHelp[49] := 'An array of ratings to be used when the seasonal ratings flag is True. It can be used to insert' +
- CRLF + 'multiple ratings to change during a QSTS simulation to evaluate different ratings in transformers. Is given in kVA';
-
- ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ with obj do
+ Result := (NumWindings - 1) * NumWindings div 2;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TTransf.NewObject(const ObjName: String): Integer;
-begin
- // create a new object of this class and add to list
- with ActiveCircuit do
- begin
-
- ActiveCktElement := TTransfObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject); // Return index of transformer in transformer list
-
- end;
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TTransf.Edit: Integer;
-{
- A Transf Defaults to 3-phases, 2-windings (both wye)
-}
+function GetWindingCurrentsResult(Obj: TObj): String;
+// Returns string mag, angle
var
- ParamPointer,
- i: Integer;
- ParamName: String; {For parsing property names}
- Param: String;
-
+ WindingCurrents: pComplexArray;
+ i, j, k: Integer;
begin
- // continue parsing cmdline presently in Parser
-
- {Make this object the active circuit element}
- DSS.ActiveTransfObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveTransfObj; // use property to set this value
-
- Result := 0;
-
- with DSS.ActiveTransfObj do
+ with Obj do
begin
- XHLChanged := FALSE;
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
- begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "Transformer.' + Name + '"', 110);
- 1:
- Nphases := Parser.IntValue;
- 2:
- SetNumWindings(Parser.IntValue); // Reallocate stuff if bigger
- 3:
- SetActiveWinding(Parser.IntValue);
- 4:
- Setbus(ActiveWinding, param);
- 5:
- InterpretConnection(Param);
- 6:
- Winding^[ActiveWinding].kVLL := parser.Dblvalue;
- 7:
- Winding^[ActiveWinding].kVA := parser.Dblvalue;
- 8:
- Winding^[ActiveWinding].puTap := parser.Dblvalue;
- 9:
- Winding^[ActiveWinding].Rpu := parser.Dblvalue * 0.01; // %R
- 10:
- Winding^[ActiveWinding].Rneut := parser.Dblvalue;
- 11:
- Winding^[ActiveWinding].Xneut := parser.Dblvalue;
- 12:
- InterpretAllBuses(Param);
- 13:
- InterpretAllConns(Param);
- 14:
- InterpretAllkVRatings(Param);
- 15:
- InterpretAllkVARatings(Param);
- 16:
- InterpretAllTaps(Param);
- 17:
- XHL := TrapZero(parser.Dblvalue, 7.0) * 0.01;
- 18:
- XHT := TrapZero(parser.Dblvalue, 35.0) * 0.01;
- 19:
- XLT := TrapZero(parser.Dblvalue, 30.0) * 0.01;
- 20:
- Parser.ParseAsVector(((NumWindings - 1) * NumWindings div 2), Xsc);
- 21:
- ThermalTimeConst := Parser.DblValue;
- 22:
- n_thermal := Parser.DblValue;
- 23:
- m_thermal := Parser.DblValue;
- 24:
- FLrise := Parser.DblValue;
- 25:
- HSRise := Parser.DblValue;
- 26:
- pctLoadLoss := Parser.DblValue;
- 27:
- pctNoLoadLoss := Parser.DblValue;
- 28:
- NormMaxHkVA := Parser.Dblvalue;
- 29:
- EmergMaxHkVA := Parser.Dblvalue;
- 30:
- IsSubstation := InterpretYesNo(Param);
- 31:
- Winding^[ActiveWinding].MaxTap := Parser.DblValue;
- 32:
- Winding^[ActiveWinding].MinTap := Parser.DblValue;
- 33:
- Winding^[ActiveWinding].NumTaps := Parser.IntValue;
- 34:
- SubstationName := Param;
- 35:
- pctImag := Parser.DblValue;
- 36:
- ppm_FloatFactor := Parser.DblValue * 1.0e-6;
- 37:
- InterpretAllRs(Param);
- 38:
- XfmrBank := Param;
- 39:
- FetchXfmrCode(Param);
- 40:
- XRConst := InterpretYesNo(Param);
- 41:
- XHL := TrapZero(parser.Dblvalue, 7.0) * 0.01;
- 42:
- XHT := TrapZero(parser.Dblvalue, 35.0) * 0.01;
- 43:
- XLT := TrapZero(parser.Dblvalue, 30.0) * 0.01;
- 44:
- HVLeadsLV := InterpretLeadLag(Param);
- 45:
- PropertyValue[45] := ''; // placeholder, do nothing just throw value away if someone tries to set it.
- 46:
- strCoreType := Param;
- 47:
- Winding^[ActiveWinding].RdcOhms := Parser.DblValue;
- 48:
- begin
- NumAmpRatings := Parser.IntValue;
- SetLength(kVARatings, NumAmpRatings);
- end;
- 49:
- begin
- Setlength(kVARatings, NumAmpRatings);
- Param := Parser.StrValue;
- NumAmpRatings := InterpretDblArray(Param, NumAmpRatings, Pointer(kVARatings));
- end
- else
- // Inherited properties
- ClassEdit(DSS.ActiveTransfObj, ParamPointer - NumPropsThisClass)
- end;
+ WindingCurrents := AllocMem(Sizeof(Complex) * 2 * FNPhases * NumWindings);
- {Take care of properties that require some additional work,}
- case ParamPointer of
- 1:
- NConds := Fnphases + 1; // Force redefinition of number of conductors and reallocation of matrices
- // default all winding kVAs to first winding so latter Donot have to be specified
- 7:
- if (ActiveWinding = 1) then
- begin
- for i := 2 to NumWindings do
- Winding^[i].kVA := Winding^[1].kVA;
- NormMaxHkVA := 1.1 * Winding^[1].kVA; // Defaults for new winding rating.
- EmergMaxHkVA := 1.5 * Winding^[1].kVA;
- end
- else
- if NumWindings = 2 then
- begin
- Winding^[1].kVA := Winding^[2].kVA; // For 2-winding, force both kVAs to be same
- end;
- // Update LoadLosskW if winding %r changed. Using only windings 1 and 2
- 9:
- pctLoadLoss := (Winding^[1].Rpu + Winding^[2].Rpu) * 100.0;
- 15:
- begin
- NormMaxHkVA := 1.1 * Winding^[1].kVA; // Defaults for new winding rating.
- EmergMaxHkVA := 1.5 * Winding^[1].kVA;
- end;
- 17..19:
- XHLChanged := TRUE;
- 20:
- for i := 1 to ((NumWindings - 1) * NumWindings div 2) do
- Xsc^[i] := Xsc^[i] * 0.01; // Convert to per unit
-
- 26:
- begin // Assume load loss is split evenly between windings 1 and 2
- Winding^[1].Rpu := pctLoadLoss / 2.0 / 100.0;
- Winding^[2].Rpu := Winding^[1].Rpu;
- end;
- 37:
- pctLoadLoss := (Winding^[1].Rpu + Winding^[2].Rpu) * 100.0; // Update
- 41..43:
- XHLChanged := TRUE;
- 46:
- CoreType := InterpretCoreType(Param); // Assign integer number
- 47:
- Winding^[ActiveWinding].RdcSpecified := TRUE;
- else
- end;
+ GetAllWindingCurrents(WindingCurrents);
- //YPrim invalidation on anything that changes impedance values
- case ParamPointer of
- 5..7:
- YprimInvalid := TRUE;
- 8: // tap:
-{$IFDEF DSS_CAPI_INCREMENTAL_Y}
- // Try to handle tap changes incrementally
- if ((ActiveCircuit.Solution.SolverOptions and $FFFFFFFF) <> ord(TSolverOptions.ReuseNothing)) and
- (not ActiveCircuit.Solution.SystemYChanged) and
- (YPrim <> NIL) and
- (not YPrimInvalid)
- then
- // Mark this to incrementally update the matrix.
- // If the matrix is already being rebuilt, there is
- // no point in doing this, just rebuild it as usual.
- ActiveCircuit.IncrCktElements.Add(DSS.ActiveTransfObj)
- else
-{$ENDIF}
- YprimInvalid := TRUE;
- 9..19:
- YprimInvalid := TRUE;
- 26..27:
- YprimInvalid := TRUE;
- 35..37:
- YprimInvalid := TRUE;
- 41..43:
- YPrimInvalid := TRUE;
- else
+ Result := '';
+ k := 0;
+ for i := 1 to Fnphases do
+ begin
+ for j := 1 to NumWindings do
+ begin
+ k := k + 1;
+ Result := Result + Format('%.7g, (%.5g), ', [Cabs(WindingCurrents^[k]), Cdang(WindingCurrents^[k])]);
+ k := k + 1;
+ // Skip currents from other end of the winding
end;
-
- {Advance to next property on input line}
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
end;
- RecalcElementData;
+ Reallocmem(WindingCurrents, 0); // throw away temp array
end;
-
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTransf.SetActiveWinding(w: Integer);
-
-begin
- with DSS.ActiveTransfObj do
- if (w > 0) and (w <= NumWindings) then
- ActiveWinding := w
- else
- DoSimpleMsg('Wdg parameter invalid for "' + DSS.ActiveTransfObj.Name + '"', 112);
-end;
-
-function TTransf.TrapZero(const Value: Double; DefaultValue: Double): Double;
+procedure TTransf.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
- if Value = 0.0 then
- begin
- Dosimplemsg('Zero Reactance specified for Transformer.' + DSS.ActiveTransfObj.Name, 11201);
- Result := DefaultValue;
- end
- else
- Result := Value;
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTransf.InterpretConnection(const S: String);
-
-// Accepts
-// delta or LL (Case insensitive)
-// Y, wye, or LN
+ Numproperties := NumPropsThisClass;
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ PropertyStructArrayOffset := ptruint(@obj.Winding);
+ PropertyStructArrayStep := SizeOf(TWinding);
+ PropertyStructArrayIndexOffset := ptruint(@obj.ActiveWinding);
+ PropertyStructArrayCountOffset := ptruint(@obj.NumWindings);
+
+ // RO string
+ PropertyType[ord(TProp.WdgCurrents)] := TPropertyType.StringSilentROFunctionProperty;
+ PropertyOffset[ord(TProp.WdgCurrents)] := ptruint(@GetWindingCurrentsResult);
+
+ // object properties
+ PropertyType[ord(TProp.XfmrCode)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.XfmrCode)] := ptruint(@obj.XfmrCodeObj);
+ PropertyOffset2[ord(TProp.XfmrCode)] := ptruint(DSS.XfmrCodeClass);
+
+ // double arrays
+ PropertyType[ord(TProp.Ratings)] := TPropertyType.DoubleDArrayProperty;
+ PropertyOffset[ord(TProp.Ratings)] := ptruint(@obj.kVARatings);
+ PropertyOffset2[ord(TProp.Ratings)] := ptruint(@obj.NumAmpRatings);
+
+ PropertyType[ord(TProp.Xscarray)] := TPropertyType.DoubleVArrayProperty;
+ PropertyOffset[ord(TProp.Xscarray)] := ptruint(@obj.Xsc);
+ PropertyOffset3[ord(TProp.Xscarray)] := ptruint(@XscSize); // (NumWindings - 1) * NumWindings div 2
+ PropertyFlags[ord(TProp.Xscarray)] := [TPropertyFlag.SizeIsFunction];
+ PropertyScale[ord(TProp.Xscarray)] := 0.01;
+
+ // enums
+ PropertyType[ord(TProp.Core)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.Core)] := ptruint(@obj.CoreType);
+ PropertyOffset2[ord(TProp.Core)] := PtrInt(DSS.CoreTypeEnum);
+
+ PropertyType[ord(TProp.LeadLag)] := TPropertyType.MappedStringEnumProperty;
+ PropertyOffset[ord(TProp.LeadLag)] := ptruint(@obj.HVLeadsLV); // LongBool as Integer
+ PropertyOffset2[ord(TProp.LeadLag)] := PtrInt(DSS.LeadLagEnum);
+
+ // boolean properties
+ PropertyType[ord(TProp.sub)] := TPropertyType.BooleanProperty;
+ PropertyType[ord(TProp.XRConst)] := TPropertyType.BooleanProperty;
+ PropertyOffset[ord(TProp.sub)] := ptruint(@obj.IsSubstation);
+ PropertyOffset[ord(TProp.XRConst)] := ptruint(@obj.XRConst);
+
+ // string properties
+ PropertyType[ord(TProp.bank)] := TPropertyType.StringProperty;
+ PropertyType[ord(TProp.subname)] := TPropertyType.StringProperty;
+ PropertyOffset[ord(TProp.bank)] := ptruint(@obj.XfmrBank);
+ PropertyOffset[ord(TProp.subname)] := ptruint(@obj.SubstationName);
+
+ // integer properties
+ PropertyType[ord(TProp.phases)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.phases)] := ptruint(@obj.FNPhases);
+ PropertyFlags[ord(TProp.phases)] := [TPropertyFlag.NonNegative, TPropertyFlag.NonZero];
+
+ PropertyType[ord(TProp.Seasons)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.Seasons)] := ptruint(@obj.NumAmpRatings);
+
+ PropertyType[ord(TProp.windings)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.windings)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.windings)] := [TPropertyFlag.GreaterThanOne];
+
+ PropertyType[ord(TProp.wdg)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.wdg)] := ptruint(@obj.ActiveWinding);
+ PropertyFlags[ord(TProp.wdg)] := [TPropertyFlag.IntegerStructIndex];
+
+ // double on struct array properties
+ PropertyType[ord(TProp.kV)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.kV)] := ptruint(@TWinding(nil^).kVLL);
+
+ PropertyType[ord(TProp.kVA)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.kVA)] := ptruint(@TWinding(nil^).kVA);
+
+ PropertyType[ord(TProp.tap)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.tap)] := ptruint(@TWinding(nil^).puTap);
+
+ PropertyType[ord(TProp.Rneut)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.Rneut)] := ptruint(@TWinding(nil^).Rneut);
+
+ PropertyType[ord(TProp.Xneut)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.Xneut)] := ptruint(@TWinding(nil^).Xneut);
+
+ PropertyType[ord(TProp.MaxTap)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.MaxTap)] := ptruint(@TWinding(nil^).MaxTap);
+
+ PropertyType[ord(TProp.MinTap)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.MinTap)] := ptruint(@TWinding(nil^).MinTap);
+
+ PropertyType[ord(TProp.RdcOhms)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.RdcOhms)] := ptruint(@TWinding(nil^).RdcOhms);
+
+ PropertyType[ord(TProp.pctR)] := TPropertyType.DoubleOnStructArrayProperty;
+ PropertyOffset[ord(TProp.pctR)] := ptruint(@TWinding(nil^).Rpu);
+ PropertyScale[ord(TProp.pctR)] := 0.01;
+
+ // double arrays via struct array
+ PropertyType[ord(TProp.pctRs)] := TPropertyType.DoubleArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.pctRs)] := ptruint(@TWinding(nil^).Rpu);
+ PropertyOffset2[ord(TProp.pctRs)] := ptruint(@obj.NumWindings);
+ PropertyScale[ord(TProp.pctRs)] := 0.01;
+ PropertyFlags[ord(TProp.pctRs)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.pctRs)] := ord(TProp.pctR);
+
+ PropertyType[ord(TProp.kVs)] := TPropertyType.DoubleArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.kVs)] := ptruint(@TWinding(nil^).kVLL);
+ PropertyOffset2[ord(TProp.kVs)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.kVs)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.kVs)] := ord(TProp.kV);
+
+ PropertyType[ord(TProp.kVAs)] := TPropertyType.DoubleArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.kVAs)] := ptruint(@TWinding(nil^).kVA);
+ PropertyOffset2[ord(TProp.kVAs)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.kVAs)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.kVAs)] := ord(TProp.kVA);
+
+ PropertyType[ord(TProp.taps)] := TPropertyType.DoubleArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.taps)] := ptruint(@TWinding(nil^).puTap);
+ PropertyOffset2[ord(TProp.taps)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.taps)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.taps)] := ord(TProp.tap);
+
+ // bus, indirect
+ PropertyType[ord(TProp.bus)] := TPropertyType.BusOnStructArrayProperty;
+ PropertyOffset[ord(TProp.bus)] := 1; // dummy value, just to mark the property as handled
+
+ PropertyType[ord(TProp.buses)] := TPropertyType.BusesOnStructArrayProperty;
+ PropertyOffset[ord(TProp.buses)] := ptruint(@obj.NumWindings);
+ PropertyFlags[ord(TProp.buses)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.buses)] := ord(TProp.bus);
+
+ // enum on array of structs
+ PropertyType[ord(TProp.conn)] := TPropertyType.MappedStringEnumOnStructArrayProperty;
+ PropertyOffset[ord(TProp.conn)] := ptruint(@TWinding(nil^).Connection);
+ PropertyOffset2[ord(TProp.conn)] := PtrInt(DSS.ConnectionEnum);
+
+ // array of enums on array of structs
+ PropertyType[ord(TProp.conns)] := TPropertyType.MappedStringEnumArrayOnStructArrayProperty;
+ PropertyOffset[ord(TProp.conns)] := ptruint(@TWinding(nil^).Connection);
+ PropertyOffset2[ord(TProp.conns)] := PtrInt(DSS.ConnectionEnum);
+ PropertyFlags[ord(TProp.conns)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.conns)] := ord(TProp.conn);
+
+ // integer on struct array
+ PropertyType[ord(TProp.NumTaps)] := TPropertyType.IntegerOnStructArrayProperty;
+ PropertyOffset[ord(TProp.NumTaps)] := ptruint(@TWinding(nil^).NumTaps);
+
+ // double properties
+ PropertyOffset[ord(TProp.thermal)] := ptruint(@obj.ThermalTimeConst);
+ PropertyOffset[ord(TProp.n)] := ptruint(@obj.n_thermal);
+ PropertyOffset[ord(TProp.m)] := ptruint(@obj.m_thermal);
+ PropertyOffset[ord(TProp.flrise)] := ptruint(@obj.FLrise);
+ PropertyOffset[ord(TProp.hsrise)] := ptruint(@obj.HSRise);
+ PropertyFlags[ord(TProp.thermal)] := [TPropertyFlag.Unused];
+ PropertyFlags[ord(TProp.n)] := [TPropertyFlag.Unused];
+ PropertyFlags[ord(TProp.m)] := [TPropertyFlag.Unused];
+ PropertyFlags[ord(TProp.flrise)] := [TPropertyFlag.Unused];
+ PropertyFlags[ord(TProp.hsrise)] := [TPropertyFlag.Unused];
+
+ PropertyOffset[ord(TProp.pctloadloss)] := ptruint(@obj.pctLoadLoss);
+ PropertyOffset[ord(TProp.pctnoloadloss)] := ptruint(@obj.pctNoLoadLoss);
+ PropertyOffset[ord(TProp.normhkVA)] := ptruint(@obj.NormMaxHkVA);
+ PropertyOffset[ord(TProp.emerghkVA)] := ptruint(@obj.EmergMaxHkVA);
+ PropertyOffset[ord(TProp.pctimag)] := ptruint(@obj.pctImag);
+
+ // scaled double
+ PropertyOffset[ord(TProp.ppm_antifloat)] := ptruint(@obj.ppm_FloatFactor);
+ PropertyScale[ord(TProp.ppm_antifloat)] := 1.0e-6;
+
+ // adv double percent properties
+ PropertyOffset[ord(TProp.XHL)] := ptruint(@obj.XHL);
+ PropertyOffset[ord(TProp.XHT)] := ptruint(@obj.XHT);
+ PropertyOffset[ord(TProp.XLT)] := ptruint(@obj.XLT);
+ PropertyOffset[ord(TProp.X12)] := ptruint(@obj.XHL);
+ PropertyOffset[ord(TProp.X13)] := ptruint(@obj.XHT);
+ PropertyOffset[ord(TProp.X23)] := ptruint(@obj.XLT);
+ PropertyScale[ord(TProp.XHL)] := 0.01;
+ PropertyScale[ord(TProp.XHT)] := 0.01;
+ PropertyScale[ord(TProp.XLT)] := 0.01;
+ PropertyScale[ord(TProp.X12)] := 0.01;
+ PropertyScale[ord(TProp.X13)] := 0.01;
+ PropertyScale[ord(TProp.X23)] := 0.01;
+ PropertyTrapZero[ord(TProp.XHL)] := 7.0;
+ PropertyTrapZero[ord(TProp.XHT)] := 35.0;
+ PropertyTrapZero[ord(TProp.XLT)] := 30.0;
+ PropertyTrapZero[ord(TProp.X12)] := 7.0;
+ PropertyTrapZero[ord(TProp.X13)] := 35.0;
+ PropertyTrapZero[ord(TProp.X23)] := 30.0;
+ PropertyFlags[ord(TProp.X12)] := [TPropertyFlag.Redundant];
+ PropertyFlags[ord(TProp.X13)] := [TPropertyFlag.Redundant];
+ PropertyFlags[ord(TProp.X23)] := [TPropertyFlag.Redundant];
+ PropertyRedundantWith[ord(TProp.X12)] := ord(TProp.XHL);
+ PropertyRedundantWith[ord(TProp.X13)] := ord(TProp.XHT);
+ PropertyRedundantWith[ord(TProp.X23)] := ord(TProp.XLT);
-begin
- with DSS.ActiveTransfObj do
- begin
- with Winding^[ActiveWinding] do
- begin
- case lowercase(S)[1] of
- 'y', 'w':
- Connection := 0; {Wye}
- 'd':
- Connection := 1; {Delta or line-Line}
- 'l':
- case lowercase(s)[2] of
- 'n':
- Connection := 0;
- 'l':
- Connection := 1;
- end;
- end;
- end;
- Yorder := fNConds * fNTerms;
- YPrimInvalid := TRUE;
- end;
+ ActiveProperty := NumPropsThisClass;
+ inherited DefineProperties;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTransf.InterpretAllConns(const S: String);
-// routine expecting all winding connections expressed in one array of strings
+function TTransf.NewObject(const ObjName: String; Activate: Boolean): Pointer;
var
- {S1,}
- S2: String;
- i: Integer;
+ Obj: TObj;
begin
-
- AuxParser.CmdString := S; // Load up Parser
-
- {Loop for no more than the expected number of windings}
- with DSS.ActiveTransfObj do
- for i := 1 to Numwindings do
- begin
- ActiveWinding := i;
- {S1 :=} AuxParser.NextParam; // ignore any parameter name not expecting any
- S2 := AuxParser.StrValue;
- if Length(S2) > 0 then
- InterpretConnection(S2);
- end;
-
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTransf.InterpretAllBuses(const S: String);
-// routine expecting all winding bus connections expressed in one array of strings
+procedure TTransfObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- BusNam: String;
i: Integer;
+ OldXSCSize, NewXSCSize: Integer;
begin
-
- AuxParser.CmdString := S; // Load up Parser
-
- {Loop for no more than the expected number of windings; Ignore omitted values}
- with DSS.ActiveTransfObj do
- for i := 1 to Numwindings do
+ case Idx of
+ ord(TProp.phases):
+ if FNPhases <> previousIntVal then
+ NConds := Fnphases + 1; // Force redefinition of number of conductors and reallocation of matrices
+ // default all winding kVAs to first winding so latter Donot have to be specified
+ ord(TProp.conn):
begin
- ActiveWinding := i;
- AuxParser.NextParam; // ignore any parameter name not expecting any
- BusNam := AuxParser.StrValue;
- if Length(BusNam) > 0 then
- SetBus(ActiveWinding, BusNam);
+ Yorder := fNConds * fNTerms;
+ YPrimInvalid := TRUE;
end;
-
-end;
-
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TTransf.InterpretLeadLag(const S: String): Boolean;
-// routine expecting all winding bus connections expressed in one array of strings
-begin
-
- Result := FALSE; // default to ANSI 30 Deg Lag if can't understand S
-
- if CompareTextShortest(S, 'lead') = 0 then
- Result := TRUE
- else
- if CompareTextShortest(S, 'euro') = 0 then
- Result := TRUE;
-
-end;
-
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTransf.InterpretAllkVRatings(const S: String);
-// routine expecting all winding kV ratings expressed in one array of strings
-var
- DataStr: String;
- i: Integer;
-begin
-
- AuxParser.CmdString := S; // Load up Parser
-
- {Loop for no more than the expected number of windings; Ignore omitted values}
- with DSS.ActiveTransfObj do
- for i := 1 to Numwindings do
+ ord(TProp.windings):
begin
- ActiveWinding := i;
- AuxParser.NextParam; // ignore any parameter name not expecting any
- DataStr := AuxParser.StrValue;
- if Length(DataStr) > 0 then
- Winding^[ActiveWinding].kVLL := AuxParser.Dblvalue;
- end;
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTransf.InterpretAllkVARatings(const S: String);
-// routine expecting all winding ratings expressed in one array of strings
-var
- DataStr: String;
- i: Integer;
-begin
-
- AuxParser.CmdString := S; // Load up Parser
+ OldXSCSize := (previousIntVal - 1) * previousIntVal div 2;
+ MaxWindings := NumWindings;
+ NewXSCSize := (NumWindings - 1) * NumWindings div 2;
+ FNconds := Fnphases + 1;
+ Nterms := NumWindings;
+ Reallocmem(Winding, Sizeof(TWinding) * MaxWindings); // Reallocate collector array
+ for i := 1 to MaxWindings do
+ Winding[i].Init();
+
+ // array of short circuit measurements between pairs of windings
+ ReAllocmem(XSC, SizeOF(XSC^[1]) * NewXSCSize);
+ for i := OldXSCSize + 1 to NewXSCSize do
+ XSC^[i] := 0.30;
+ Reallocmem(TermRef, SizeOf(TermRef^[1]) * 2 * NumWindings * Fnphases);
+
+ // Reallocate impedance matrices
+ if ZB <> NIL then
+ begin
+ ZB.Free;
+ Y_1Volt.Free;
+ Y_1Volt_NL.Free;
+ Y_Term.Free;
+ Y_Term_NL.Free;
+ end;
- {Loop for no more than the expected number of windings; Ignore omitted values}
- with DSS.ActiveTransfObj do
- for i := 1 to Numwindings do
- begin
- ActiveWinding := i;
- AuxParser.NextParam; // ignore any parameter name not expecting any
- DataStr := AuxParser.StrValue;
- if Length(DataStr) > 0 then
- Winding^[ActiveWinding].kVA := AuxParser.Dblvalue;
+ ZB := TCMatrix.CreateMatrix(NumWindings - 1);
+ Y_1Volt := TCMatrix.CreateMatrix(NumWindings);
+ Y_1Volt_NL := TCMatrix.CreateMatrix(NumWindings);
+ Y_Term := TCMatrix.CreateMatrix(2 * NumWindings);
+ Y_Term_NL := TCMatrix.CreateMatrix(2 * NumWindings);
end;
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTransf.InterpretAllRs(const S: String);
-// routine expecting all winding ratings expressed in one array of strings
-var
- DataStr: String;
- i: Integer;
-begin
-
- AuxParser.CmdString := S; // Load up Parser
-
- {Loop for no more than the expected number of windings; Ignore omitted values}
- with DSS.ActiveTransfObj do
- for i := 1 to Numwindings do
+ ord(TProp.kVA):
+ if (ActiveWinding = 1) then
+ begin
+ for i := 2 to NumWindings do
+ Winding^[i].kVA := Winding^[1].kVA;
+ NormMaxHkVA := 1.1 * Winding^[1].kVA; // Defaults for new winding rating.
+ EmergMaxHkVA := 1.5 * Winding^[1].kVA;
+ end
+ else
+ if NumWindings = 2 then
+ begin
+ Winding^[1].kVA := Winding^[2].kVA; // For 2-winding, force both kVAs to be same
+ end;
+ // Update LoadLosskW if winding %r changed. Using only windings 1 and 2
+ ord(TProp.pctR):
+ pctLoadLoss := (Winding^[1].Rpu + Winding^[2].Rpu) * 100.0;
+ ord(TProp.kVAs):
begin
- ActiveWinding := i;
- AuxParser.NextParam; // ignore any parameter name not expecting any
- DataStr := AuxParser.StrValue;
- if Length(DataStr) > 0 then
- Winding^[ActiveWinding].Rpu := AuxParser.Dblvalue * 0.01;
+ NormMaxHkVA := 1.1 * Winding^[1].kVA; // Defaults for new winding rating.
+ EmergMaxHkVA := 1.5 * Winding^[1].kVA;
end;
+ ord(TProp.XHL), ord(TProp.XHT), ord(TProp.XLT),
+ ord(TProp.X12), ord(TProp.X13), ord(TProp.X23):
+ XHLChanged := TRUE;
+
+ ord(TProp.pctloadloss):
+ begin // Assume load loss is split evenly between windings 1 and 2
+ Winding^[1].Rpu := pctLoadLoss / 2.0 / 100.0;
+ Winding^[2].Rpu := Winding^[1].Rpu;
+ end;
+ ord(TProp.pctRs):
+ pctLoadLoss := (Winding^[1].Rpu + Winding^[2].Rpu) * 100.0; // Update
+ ord(TProp.XfmrCode):
+ FetchXfmrCode();
+ ord(TProp.RdcOhms):
+ Winding^[ActiveWinding].RdcSpecified := TRUE;
+ ord(TProp.Seasons):
+ SetLength(kVARatings, NumAmpRatings);
+ end;
+ //YPrim invalidation on anything that changes impedance values
+ case Idx of
+ ord(TProp.tap), ord(TProp.taps):
+{$IFDEF DSS_CAPI_INCREMENTAL_Y}
+ // Try to handle tap changes incrementally
+ if ((ActiveCircuit.Solution.SolverOptions and $FFFFFFFF) <> ord(TSolverOptions.ReuseNothing)) and
+ (not ActiveCircuit.Solution.SystemYChanged) and
+ (YPrim <> NIL) and
+ (not YPrimInvalid)
+ then
+ // Mark this to incrementally update the matrix.
+ // If the matrix is already being rebuilt, there is
+ // no point in doing this, just rebuild it as usual.
+ ActiveCircuit.IncrCktElements.Add(self)
+ else
+{$ENDIF}
+ YprimInvalid := TRUE;
+ ord(TProp.kV), ord(TProp.kVA),
+ 9..15,
+ ord(TProp.pctloadloss), ord(TProp.pctnoloadloss),
+ ord(TProp.pctimag), ord(TProp.ppm_antifloat), ord(TProp.pctRs),
+ ord(TProp.XHL), ord(TProp.XHT), ord(TProp.XLT),
+ ord(TProp.X12), ord(TProp.X13), ord(TProp.X23):
+ YprimInvalid := TRUE;
+ end;
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-procedure TTransf.InterpretAllTaps(const S: String);
-// routine expecting all winding taps expressed in one array of strings
+function TTransf.BeginEdit(ptr: Pointer; SetActive: Boolean): Pointer;
var
- DataStr: String;
- i: Integer;
+ Obj: TObj;
begin
-
- AuxParser.CmdString := S; // Load up Parser
-
- {Loop for no more than the expected number of windings; Ignore omitted values}
- with DSS.ActiveTransfObj do
- for i := 1 to Numwindings do
- begin
- ActiveWinding := i;
- AuxParser.NextParam; // ignore any parameter name, not expecting any
- DataStr := AuxParser.StrValue;
- if Length(DataStr) > 0 then
- Winding^[ActiveWinding].puTap := AuxParser.Dblvalue;
- end;
-
+ Obj := TObj(inherited BeginEdit(ptr, SetActive));
+ Obj.XHLChanged := FALSE;
+ Result := Obj;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-function TTransf.MakeLike(const TransfName: String): Integer;
+procedure TTransfObj.MakeLike(OtherPtr: Pointer);
var
- OtherTransf: TTransfObj;
+ Other: TObj;
i: Integer;
-
begin
- Result := 0;
- {See if we can find this Transf name in the present collection}
- OtherTransf := Find(TransfName);
- if OtherTransf <> NIL then
- with DSS.ActiveTransfObj do
- begin
- Nphases := OtherTransf.Fnphases;
- SetNumWindings(OtherTransf.NumWindings);
- NConds := Fnphases + 1; // forces reallocation of terminals and conductors
+ inherited MakeLike(OtherPtr);
- Yorder := fNConds * fNTerms;
- YPrimInvalid := TRUE;
-
- for i := 1 to NumWindings do
- with Winding^[i] do
- begin
- Connection := OtherTransf.Winding^[i].Connection;
- kVLL := OtherTransf.Winding^[i].kVLL;
- VBase := OtherTransf.Winding^[i].VBase;
- kVA := OtherTransf.Winding^[i].kVA;
- puTAP := OtherTransf.Winding^[i].puTAP;
- Rpu := OtherTransf.Winding^[i].Rpu;
- RdcOhms := OtherTransf.Winding^[i].RdcOhms;
- RdcSpecified := OtherTransf.Winding^[i].RdcSpecified;
- RNeut := OtherTransf.Winding^[i].RNeut;
- Xneut := OtherTransf.Winding^[i].Xneut;
- // copy the taps
- TapIncrement := OtherTransf.Winding^[i].TapIncrement;
- MinTap := OtherTransf.Winding^[i].MinTap;
- MaxTap := OtherTransf.Winding^[i].MaxTap;
- NumTaps := OtherTransf.Winding^[i].NumTaps;
- end;
-
- SetTermRef;
-
- XHL := OtherTransf.XHL;
- XHT := OtherTransf.XHT;
- XLT := OtherTransf.XLT;
-
- for i := 1 to (NumWindings * (NumWindings - 1) div 2) do
- XSc^[i] := OtherTransf.XSC^[i];
-
- ZB.CopyFrom(OtherTransf.ZB);
- Y_1Volt.CopyFrom(OtherTransf.Y_1Volt);
- Y_Term.CopyFrom(OtherTransf.Y_Term);
- Y_1Volt_NL.CopyFrom(OtherTransf.Y_1Volt_NL);
- Y_Term_NL.CopyFrom(OtherTransf.Y_Term_NL);
-
- ThermalTimeConst := OtherTransf.ThermalTimeConst;
- n_thermal := OtherTransf.n_thermal;
- m_thermal := OtherTransf.m_thermal;
- FLrise := OtherTransf.FLrise;
- HSrise := OtherTransf.HSrise;
- pctLoadLoss := OtherTransf.pctLoadLoss;
- pctNoLoadLoss := OtherTransf.pctNoLoadLoss;
- NormMaxHkVA := OtherTransf.NormMaxHkVA;
- EmergMaxHkVA := OtherTransf.EmergMaxHkVA;
- XRConst := OtherTransf.XRConst;
-
- XfmrBank := OtherTransf.XfmrBank;
- XfmrCode := OtherTransf.XfmrCode;
-
- ClassMakeLike(OtherTransf);
-
- for i := 1 to ParentClass.NumProperties do
- // Skip readonly properties
- if i <> 45 then
- PropertyValue[i] := OtherTransf.PropertyValue[i];
- NumAmpratings := OtherTransf.NumAmpRatings;
- Setlength(kVARatings, NumAmpRatings);
- for i := 0 to High(kVARatings) do
- kVARatings[i] := OtherTransf.kVARatings[i];
-
- Result := 1;
- end
- else
- DoSimpleMsg('Error in Transf MakeLike: "' + TransfName + '" Not Found.', 113);
+ Other := TObj(OtherPtr);
+ FNphases := Other.Fnphases;
+ SetNumWindings(Other.NumWindings);
+ NConds := Fnphases + 1; // forces reallocation of terminals and conductors
+ Yorder := fNConds * fNTerms;
+ YPrimInvalid := TRUE;
+ for i := 1 to NumWindings do
+ Winding[i] := Other.Winding[i];
+
+ SetTermRef;
+
+ XHL := Other.XHL;
+ XHT := Other.XHT;
+ XLT := Other.XLT;
+
+ for i := 1 to (NumWindings * (NumWindings - 1) div 2) do
+ XSc^[i] := Other.XSC^[i];
+
+ ZB.CopyFrom(Other.ZB);
+ Y_1Volt.CopyFrom(Other.Y_1Volt);
+ Y_Term.CopyFrom(Other.Y_Term);
+ Y_1Volt_NL.CopyFrom(Other.Y_1Volt_NL);
+ Y_Term_NL.CopyFrom(Other.Y_Term_NL);
+
+ ThermalTimeConst := Other.ThermalTimeConst;
+ n_thermal := Other.n_thermal;
+ m_thermal := Other.m_thermal;
+ FLrise := Other.FLrise;
+ HSrise := Other.HSrise;
+ pctLoadLoss := Other.pctLoadLoss;
+ pctNoLoadLoss := Other.pctNoLoadLoss;
+ NormMaxHkVA := Other.NormMaxHkVA;
+ EmergMaxHkVA := Other.EmergMaxHkVA;
+ XRConst := Other.XRConst;
+
+ XfmrBank := Other.XfmrBank;
+ XfmrCodeObj := Other.XfmrCodeObj;
+
+ NumAmpratings := Other.NumAmpRatings;
+ Setlength(kVARatings, NumAmpRatings);
+ for i := 0 to High(kVARatings) do
+ kVARatings[i] := Other.kVARatings[i];
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TTransf Obj
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
constructor TTransfObj.Create(ParClass: TDSSClass; const TransfName: String);
var
i: Integer;
begin
inherited Create(ParClass);
- Name := LowerCase(TransfName);
+ Name := AnsiLowerCase(TransfName);
DSSObjType := ParClass.DSSClassType; //DSSObjType + XFMR; // override PDElement (kept in both actually)
- Nphases := 3; // Directly set conds and phases
+ FNphases := 3; // Directly set conds and phases
fNConds := Fnphases + 1;
+
+ ZB := NIL;
+ Y_1Volt := NIL;
+ Y_1Volt_NL := NIL;;
+ Y_Term := NIL;
+ Y_Term_NL := NIL;
SetNumWindings(2); // must do this after setting number of phases
ActiveWinding := 1;
@@ -1020,10 +727,9 @@ constructor TTransfObj.Create(ParClass: TDSSClass; const TransfName: String);
DeltaDirection := 1;
SubstationName := '';
XfmrBank := '';
- XfmrCode := '';
+ XfmrCodeObj := NIL;
CoreType := 0;
- strCoreType := 'shell';
VABase := Winding^[1].kVA * 1000.0;
ThermalTimeconst := 2.0;
@@ -1035,15 +741,14 @@ constructor TTransfObj.Create(ParClass: TDSSClass; const TransfName: String);
EmergMaxHkVA := 1.5 * Winding^[1].kVA;
pctLoadLoss := 2.0 * Winding^[1].Rpu * 100.0; // assume two windings for init'ing
ppm_FloatFactor := 0.000001;
- {Compute antifloat added for each winding }
+ // Compute antifloat added for each winding
for i := 1 to NumWindings do
Winding^[i].ComputeAntiFloatAdder(ppm_FloatFactor, VABase / FNPhases);
- {Default the no load properties to zero}
+ // Default the no load properties to zero
pctNoLoadLoss := 0.0;
pctImag := 0.0;
- {Basefrequency := 60.0; set in base class to circuit fundamental freq; Do not reset here}
FaultRate := 0.007;
IsSubstation := FALSE;
XRConst := FALSE;
@@ -1053,7 +758,6 @@ constructor TTransfObj.Create(ParClass: TDSSClass; const TransfName: String);
Y_Terminal_FreqMult := 0.0;
Yorder := fNTerms * fNconds;
- InitPropertyValues(0);
NumAmpRatings := 1;
SetLength(kVARatings, NumAmpRatings);
@@ -1065,56 +769,16 @@ constructor TTransfObj.Create(ParClass: TDSSClass; const TransfName: String);
procedure TTransfObj.SetNumWindings(N: Integer);
var
- i: Integer;
- OldWdgSize: Integer;
- NewWdgSize: Integer;
+ prev: Integer;
begin
- if N > 1 then
- begin
- for i := 1 to NumWindings do
- Winding^[i].Free; // Free old winding objects
- OldWdgSize := (NumWindings - 1) * NumWindings div 2;
- NumWindings := N;
- MaxWindings := N;
- NewWdgSize := (NumWindings - 1) * NumWindings div 2;
- FNconds := Fnphases + 1;
- Nterms := NumWindings;
- Reallocmem(Winding, Sizeof(Winding^[1]) * MaxWindings); // Reallocate collector array
- for i := 1 to MaxWindings do
- Winding^[i] := TWinding.Create;
-
- // array of short circuit measurements between pairs of windings
- ReAllocmem(XSC, SizeOF(XSC^[1]) * NewWdgSize);
- for i := OldWdgSize + 1 to NewWdgSize do
- XSC^[i] := 0.30;
- Reallocmem(TermRef, SizeOf(TermRef^[1]) * 2 * NumWindings * Fnphases);
-
- {Reallocate impedance matrices}
- ZB.Free;
- Y_1Volt.Free;
- Y_1Volt_NL.Free;
- Y_Term.Free;
- Y_Term_NL.Free;
-
- ZB := TCMatrix.CreateMatrix(NumWindings - 1);
- Y_1Volt := TCMatrix.CreateMatrix(NumWindings);
- Y_1Volt_NL := TCMatrix.CreateMatrix(NumWindings);
- Y_Term := TCMatrix.CreateMatrix(2 * NumWindings);
- Y_Term_NL := TCMatrix.CreateMatrix(2 * NumWindings);
- end
- else
- Dosimplemsg('Invalid number of windings: (' + IntToStr(N) + ') for Transformer ' + Name, 111);
+ prev := NumWindings;
+ NumWindings := N;
+ PropertySideEffects(ord(TProp.windings), prev);
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
destructor TTransfObj.Destroy;
-
-var
- i: Integer;
begin
{Throw away stuff allocated for this object}
- for i := 1 to NumWindings do
- Winding^[i].Free;
Reallocmem(Winding, 0);
Reallocmem(XSC, 0);
Reallocmem(TermRef, 0);
@@ -1126,18 +790,15 @@ destructor TTransfObj.Destroy;
inherited Destroy;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TTransfObj.RecalcElementData;
var
i,
ihvolt: Integer;
VFactor: Double;
-
begin
-
- // Determine Delta Direction
- // If high voltage is delta, delta leads y
- // If high voltage is wye, delta lags wye
+ // Determine Delta Direction
+ // If high voltage is delta, delta leads y
+ // If high voltage is wye, delta lags wye
if Winding^[1].connection = Winding^[2].connection then
DeltaDirection := 1
else
@@ -1175,7 +836,7 @@ procedure TTransfObj.RecalcElementData;
if XHLChanged then
begin
- {should only happen for 2- and 3-winding transformers}
+ // should only happen for 2- and 3-winding transformers
if NumWindings <= 3 then
for i := 1 to (NumWindings * (NumWindings - 1) div 2) do
case i of
@@ -1190,7 +851,7 @@ procedure TTransfObj.RecalcElementData;
XHLChanged := FALSE;
end;
- // Set winding voltage bases (in volts)
+ // Set winding voltage bases (in volts)
for i := 1 to NumWindings do
with Winding^[i] do // Get the actual turns voltage base for each winding
case Connection of
@@ -1206,10 +867,10 @@ procedure TTransfObj.RecalcElementData;
end;
- {Base rating of Winding 1 }
+ // Base rating of Winding 1
VABase := Winding^[1].kVA * 1000.0;
- // Set Rdc parameters for each winding.
+ // Set Rdc parameters for each winding.
for i := 1 to NumWindings do
with Winding^[i] do
begin
@@ -1226,7 +887,7 @@ procedure TTransfObj.RecalcElementData;
for i := 1 to NumWindings do
Winding^[i].ComputeAntiFloatAdder(ppm_FloatFactor, VABase / FNPhases);
- { Normal and Emergency terminal current Rating for UE check}
+ // Normal and Emergency terminal current Rating for UE check
Vfactor := 1.0; // ensure initialization
case Winding^[1].connection of
0:
@@ -1242,7 +903,7 @@ procedure TTransfObj.RecalcElementData;
end;
end;
- {Divide per phase kVA by voltage to neutral}
+ // Divide per phase kVA by voltage to neutral
NormAmps := NormMaxHkVA / Fnphases / Vfactor;
EmergAmps := EmergMaxHkVA / Fnphases / Vfactor;
@@ -1253,48 +914,41 @@ procedure TTransfObj.RecalcElementData;
CalcY_Terminal(1.0); // Calc Y_Terminal at base frequency
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TTransfObj.SaveWrite(F: TFileStream);
-{Override standard SaveWrite}
-{Transformer structure not conducive to standard means of saving}
+// Override standard SaveWrite
+// Transformer structure not conducive to standard means of saving
var
iprop: Integer;
i: Integer;
begin
- {Write only properties that were explicitly set in the
- final order they were actually set}
- iProp := GetNextPropertySet(0); // Works on ActiveDSSObject
+ // Write only properties that were explicitly set in the
+ // final order they were actually set
+ iProp := GetNextPropertySet(-9999999);
while iProp > 0 do
begin
- with ParentClass do
- {Trap wdg= and write out array properties instead}
- case RevPropertyIdxMap[iProp] of
- 3:
- begin // if WDG= was ever used write out arrays ...
- for i := 12 to 16 do
- FSWrite(F, Format(' %s=%s', [PropertyName^[i], GetPropertyValue(i)]));
- for i := 1 to Numwindings do
- with Winding^[i] do
- FSWrite(F, Format(' wdg=%d %sR=%.7g RdcOhms=%.7g', [i, '%', Rpu * 100.0, RdcOhms]));
- end;
- 4..9:
-{do Nothing}; // Ignore these properties; use arrays instead
-
- else
- if Length(PropertyValue[iProp]) > 0 then
- FSWrite(F, Format(' %s=%s', [PropertyName^[RevPropertyIdxMap[iProp]], CheckForBlanks(PropertyValue[iProp])]));
+ // Trap wdg= and write out array properties instead
+ case iProp of
+ 3: //TODO: automate this
+ begin // if WDG= was ever used write out arrays ...
+ for i := 12 to 16 do
+ FSWrite(F, Format(' %s=%s', [ParentClass.PropertyName^[i], GetPropertyValue(i)]));
+ for i := 1 to Numwindings do
+ with Winding^[i] do
+ FSWrite(F, Format(' wdg=%d %sR=%.7g RdcOhms=%.7g', [i, '%', Rpu * 100.0, RdcOhms]));
end;
+ 4..9: //TODO: mark to avoid in "savewrite"
+ ; // Ignore these properties; use arrays instead
+ else
+ if Length(PropertyValue[iProp]) > 0 then
+ FSWrite(F, Format(' %s=%s', [ParentClass.PropertyName[iProp], CheckForBlanks(PropertyValue[iProp])]));
+ end;
iProp := GetNextPropertySet(iProp);
end;
-
-
end;
procedure TTransfObj.SetTermRef;
-
// sets an array which maps the two conductors of each winding to the
// phase and neutral conductors of the transformer according to the winding connection
-
var
i, j, k: Integer;
@@ -1323,26 +977,22 @@ procedure TTransfObj.SetTermRef;
Inc(k);
TermRef^[k] := j * fNconds;
end;
-{**** WILL THIS WORK for 2-PHASE OPEN DELTA ???? Need to check this sometime}
+ // **** WILL THIS WORK for 2-PHASE OPEN DELTA ???? Need to check this sometime
1:
begin // Delta
TermRef^[k] := (j - 1) * fNconds + i;
Inc(k);
TermRef^[k] := (j - 1) * fNconds + RotatePhases(i); // connect to next phase in sequence
end;
- end; {CASE connection}
+ end;
end;
end;
- end; {CASE Fnphases}
+ end;
end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TTransfObj.CalcYPrim;
-
var
FreqMultiplier: Double;
-
begin
if (Yprim = NIL) OR (Yprim.order <> Yorder) OR (Yprim_Shunt = NIL) OR (Yprim_Series = NIL) {YPrimInvalid} then
begin
@@ -1359,7 +1009,7 @@ procedure TTransfObj.CalcYPrim;
YPrim := TcMatrix.CreateMatrix(Yorder);
end
else
- begin {Same size as last time; just zero out to start over}
+ begin // Same size as last time; just zero out to start over
YPrim_Series.Clear; // zero out YPrim
YPrim_Shunt.Clear; // zero out YPrim
Yprim.Clear;
@@ -1377,27 +1027,25 @@ procedure TTransfObj.CalcYPrim;
AddNeutralToY(FreqMultiplier);
- {Combine the two Yprim components into Yprim}
+ // Combine the two Yprim components into Yprim
YPrim.CopyFrom(YPrim_Series);
Yprim.AddFrom(Yprim_Shunt);
- {Now Account for Open Conductors}
- {For any conductor that is open, zero out row and column}
+ // Now Account for Open Conductors
+ // For any conductor that is open, zero out row and column
inherited CalcYPrim;
YprimInvalid := FALSE;
end;
-procedure TTransfObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
+procedure TTransfObj.DumpProperties(F: TFileStream; Complete: Boolean; Leaf: Boolean);
var
i, j: Integer;
ZBtemp: Tcmatrix;
-
begin
inherited DumpProperties(F, Complete);
- {Basic Property Dump}
+ // Basic Property Dump
FSWriteln(F, Format('~ NumWindings=%d', [NumWindings]));
FSWriteln(F, Format('~ phases=%d', [Fnphases]));
@@ -1477,7 +1125,7 @@ procedure TTransfObj.DumpProperties(F: TFileStream; Complete: Boolean);
FSWrite(F, format('%g ', [GetElement(i, j).im]));
FSWriteln(F);
end;
- end; {WITH}
+ end;
ZBTemp.Free;
@@ -1497,7 +1145,7 @@ procedure TTransfObj.DumpProperties(F: TFileStream; Complete: Boolean);
FSWrite(F, Format('%.4f ', [GetElement(i, j).im]));
FSWriteln(F);
end;
- end; {WITH}
+ end;
FSWriteln(F);
FSWriteln(F, 'Y_OneVolt');
@@ -1539,22 +1187,18 @@ procedure TTransfObj.DumpProperties(F: TFileStream; Complete: Boolean);
for i := 1 to 2 * NumWindings * Fnphases do
FSWrite(F, IntToStr(TermRef^[i]) + ' ');
FSWriteln(F);
-
end;
end;
procedure TWinding.ComputeAntiFloatAdder(PPM_Factor, VABase1ph: Double);
begin
Y_PPM := -PPM_Factor / (SQR(VBase) / VABase1ph) / 2.0; //12-11-12 divided by two
- // put half on each terminal of the winding.
+ // put half on each terminal of the winding.
end;
-constructor TWinding.Create;
-{
- Make a new winding
-}
+procedure TWinding.Init();
+// Make a new winding
begin
- inherited Create;
Connection := 0;
kVLL := 12.47;
VBase := kVLL / SQRT3 * 1000.0;
@@ -1572,12 +1216,6 @@ constructor TWinding.Create;
NumTaps := 32;
MaxTap := 1.10;
MinTap := 0.90;
-
-end;
-
-destructor TWinding.Destroy;
-begin
- inherited Destroy;
end;
function TTransfObj.Get_PresentTap(i: Integer): Double;
@@ -1589,15 +1227,13 @@ function TTransfObj.Get_PresentTap(i: Integer): Double;
end;
procedure TTransfObj.Set_PresentTap(i: Integer; const Value: Double);
-
var
TempVal: Double;
-
begin
if (i > 0) and (i <= NumWindings) then
with Winding^[i] do
begin
- {Range Checking}
+ // Range Checking
TempVal := Value;
if (TempVal < MinTap) then
TempVal := MinTap
@@ -1606,7 +1242,7 @@ procedure TTransfObj.Set_PresentTap(i: Integer; const Value: Double);
TempVal := MaxTap;
if TempVal <> puTap then
- begin {Only if there's been a change}
+ begin // Only if there's been a change
puTap := TempVal;
{$IFDEF DSS_CAPI_INCREMENTAL_Y}
if ((ActiveCircuit.Solution.SolverOptions and $FFFFFFFF) <> ord(TSolverOptions.ReuseNothing)) and
@@ -1635,14 +1271,6 @@ function TTransfObj.Get_WdgResistance(i: Integer): Double;
Result := 0.0;
end;
-function TTransfObj.Get_WdgRdc(i: Integer): Double;
-begin
- if (i > 0) and (i <= NumWindings) then
- Result := Winding^[i].Rdcohms
- else
- Result := 0.0;
-end;
-
function TTransfObj.Get_WdgkVA(i: Integer): Double;
begin
if (i > 0) and (i <= NumWindings) then
@@ -1667,14 +1295,6 @@ function TTransfObj.Get_WdgXneutral(i: Integer): Double;
Result := 0.0;
end;
-function TTransfObj.Get_WdgYPPM(i: Integer): Double;
-begin
- if (i > 0) and (i <= NumWindings) then
- Result := Winding^[i].Y_PPM
- else
- Result := 0.0;
-end;
-
function TTransfObj.Get_Xsc(i: Integer): Double;
var
imax: Integer;
@@ -1728,18 +1348,14 @@ function TTransfObj.Get_TapIncrement(i: Integer): Double;
end;
procedure TTransfObj.GetAllWindingCurrents(CurrBuffer: pComplexArray);
-
-{
- Return a vector of complex currents for each Winding of all phases
-
- Iterm = Yterm * Vterm
-
- Yterm order is 2*NumWindings. Each phase has same Yterm.
- Vterm order is 2*NumWindings .
-
- Calculate Iterm phase-by-phase and concatenate into CurrBuffer.
-}
-
+// Return a vector of complex currents for each Winding of all phases
+//
+// Iterm = Yterm * Vterm
+//
+// Yterm order is 2*NumWindings. Each phase has same Yterm.
+// Vterm order is 2*NumWindings .
+//
+// Calculate Iterm phase-by-phase and concatenate into CurrBuffer.
var
i, jphase, k, iPhase, iWind, NeutTerm: Integer;
VTerm: pComplexArray;
@@ -1747,13 +1363,15 @@ procedure TTransfObj.GetAllWindingCurrents(CurrBuffer: pComplexArray);
ITerm_NL: pComplexArray;
begin
+ if (not Enabled) or (NodeRef = NIL) or (ActiveCircuit.Solution.NodeV = NIL) then
+ Exit;
try
Vterm := Allocmem(SizeOf(Complex) * 2 * NumWindings);
Iterm := Allocmem(SizeOf(Complex) * 2 * NumWindings);
ITerm_NL := Allocmem(SizeOf(Complex) * 2 * NumWindings);
- {Load up Vterminal - already allocated for all cktelements}
+ // Load up Vterminal - already allocated for all cktelements
with ActiveCircuit.Solution do
if Assigned(NodeV) then
for i := 1 to Yorder do
@@ -1792,7 +1410,7 @@ procedure TTransfObj.GetAllWindingCurrents(CurrBuffer: pComplexArray);
for i := 1 to 2 * NumWindings do
begin
k := k + 1;
- CurrBuffer^[k] := Cadd(ITerm^[i], ITerm_NL^[i]);
+ CurrBuffer^[k] := ITerm^[i] + ITerm_NL^[i];
end;
end;
@@ -1803,55 +1421,19 @@ procedure TTransfObj.GetAllWindingCurrents(CurrBuffer: pComplexArray);
except
On E: Exception do
- DoSimpleMsg('Error filling voltage buffer in GetAllWindingCurrents for Circuit Element:Transformer.' + Name + CRLF +
- 'Probable Cause: Invalid definition of element.' + CRLF +
- 'System Error Message: ' + E.Message, 100114);
- end;
-
-end;
-
-
-function TTransfObj.GetWindingCurrentsResult: String;
-
-// Returns string mag, angle
-var
- WindingCurrents: pComplexArray;
- i, j, k: Integer;
-
-begin
-
- WindingCurrents := AllocMem(Sizeof(Complex) * 2 * FNPhases * NumWindings);
-
- GetAllWindingCurrents(WindingCurrents);
-
- Result := '';
- k := 0;
- for i := 1 to Fnphases do
- begin
- for j := 1 to NumWindings do
- begin
- k := k + 1;
- Result := Result + Format('%.7g, (%.5g), ', [Cabs(WindingCurrents^[k]), Cdang(WindingCurrents^[k])]);
- k := k + 1;
- // Skip currents from other end of the winding
- end;
+ DoSimpleMsg(Format(_('Error filling voltage buffer in GetAllWindingCurrents for Circuit Element: %s'), [FullName]) + CRLF +
+ _('Probable Cause: Invalid definition of element.') + CRLF +
+ _('System Error Message: ') + E.Message, 100114);
end;
-
- Reallocmem(WindingCurrents, 0); // throw away temp array
-
end;
procedure TTransfObj.GetWindingVoltages(iWind: Integer; VBuffer: pComplexArray);
-
// Voltages across indicated winding
// Fill Vbuffer array which must be adequately allocated by calling routine
// Order is Number of Phases
-
var
i, ii, k, NeutTerm: Integer;
-
begin
-
try
{return Zero if winding number improperly specified}
@@ -1874,20 +1456,22 @@ procedure TTransfObj.GetWindingVoltages(iWind: Integer; VBuffer: pComplexArray);
case Winding^[iWind].Connection of
0:
begin // Wye
- VBuffer^[i] := Csub(Vterminal^[i + k], Vterminal^[NeutTerm]);
+ VBuffer^[i] := Vterminal^[i + k] - Vterminal^[NeutTerm];
end;
1:
begin // Delta
ii := RotatePhases(i); // Get next phase in sequence
- VBuffer^[i] := CSub(Vterminal^[i + k], Vterminal^[ii + k]);
+ VBuffer^[i] := Vterminal^[i + k] - Vterminal^[ii + k];
end
end; {CASE}
except
On E: Exception do
- DoSimpleMsg('Error filling voltage buffer in GetWindingVoltages for Circuit Element:Transformer.' + Name + CRLF +
- 'Probable Cause: Invalid definition of element.' + CRLF +
- 'System Error Message: ' + E.Message, 114);
+ DoSimpleMsg(
+ _('Error filling voltage buffer in GetWindingVoltages for Circuit Element: %s') + CRLF +
+ _('Probable Cause: Invalid definition of element.') + CRLF +
+ _('System Error Message: %s'),
+ [FullName, E.Message], 114);
end;
end;
@@ -1900,8 +1484,6 @@ function TTransfObj.Get_BaseVoltage(i: Integer): Double;
Result := Winding^[i].VBase;
end;
-{============================== GetLosses Override ===============================}
-
procedure TTransfObj.GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Complex);
var
cTempIterminal: pComplexArray;
@@ -1915,243 +1497,23 @@ procedure TTransfObj.GetLosses(var TotalLosses, LoadLosses, NoLoadLosses: Comple
Exit;
end;
- {Calculates losses in watts, vars}
+ // Calculates losses in watts, vars
TotalLosses := Losses; // Side effect: computes Iterminal
- {Compute No load losses in Yprim_Shunt}
+ // Compute No load losses in Yprim_Shunt
cTempIterminal := AllocMem(Sizeof(Complex) * Yorder);
ComputeVterminal;
Yprim_Shunt.MVmult(cTempIterminal, Vterminal);
- {No Load Losses are sum of all powers coming into YPrim_Shunt from each terminal}
+ // No Load Losses are sum of all powers coming into YPrim_Shunt from each terminal
NoLoadLosses := CZERO;
for i := 1 to Yorder do
- Caccum(NoLoadLosses, Cmul(VTerminal^[i], conjg(cTempIterminal^[i])));
+ NoLoadLosses += VTerminal^[i] * cong(cTempIterminal^[i]);
- LoadLosses := CSub(TotalLosses, NoLoadLosses);
+ LoadLosses := TotalLosses - NoLoadLosses;
Reallocmem(cTempIterminal, 0);
-
end;
-function TTransfObj.GetPropertyValue(Index: Integer): String;
-
-{ gets the property for the active winding ; Set the active winding before calling}
-
-var
- i, k: Integer;
- TempStr: String;
-begin
- case Index of
- 12..16, 20, 37:
- Result := '[';
- else
- Result := '';
- end;
-
- case Index of
- 1:
- Result := IntToStr(nPhases);
- 2:
- Result := IntToStr(NumWindings);
- 3:
- Result := IntToStr(ActiveWinding); // return active winding
- 4:
- Result := Getbus(ActiveWinding); // return bus spec for active winding
- 5:
- case Winding^[ActiveWinding].Connection of
- 0:
- Result := 'wye ';
- 1:
- Result := 'Delta ';
- else
- end;
- 6:
- Result := Format('%.7g', [Winding^[ActiveWinding].kVLL]);
- 7:
- Result := Format('%.7g', [Winding^[ActiveWinding].kVA]);
- 8:
- Result := Format('%.7g', [Winding^[ActiveWinding].puTap]);
- 9:
- Result := Format('%.7g', [Winding^[ActiveWinding].Rpu * 100.0]); // %R
- 10:
- Result := Format('%.7g', [Winding^[ActiveWinding].Rneut]);
- 11:
- Result := Format('%.7g', [Winding^[ActiveWinding].Xneut]);
-
- 12:
- for i := 1 to NumWindings do
- Result := Result + GetBus(i) + ', ';
- 13:
- for i := 1 to NumWindings do
- case Winding^[i].Connection of
- 0:
- Result := Result + 'wye, ';
- 1:
- Result := Result + 'delta, ';
- else
- end;
- 14:
- for i := 1 to NumWindings do
- Result := Result + Format('%.7g, ', [Winding^[i].kVLL]);
- 15:
- for i := 1 to NumWindings do
- Result := Result + Format('%.7g, ', [Winding^[i].kVA]);
- 16:
- for i := 1 to NumWindings do
- Result := Result + Format('%.7g, ', [Winding^[i].puTap]);// InterpretAllTaps(Param);
- 17:
- Result := Format('%.7g', [XHL * 100.0]);
- 18:
- Result := Format('%.7g', [XHT * 100.0]);
- 19:
- Result := Format('%.7g', [XLT * 100.0]);
- 20:
- for i := 1 to (NumWindings - 1) * NumWindings div 2 do
- Result := Result + Format('%-g, ', [Xsc^[i] * 100.0]);// Parser.ParseAsVector(((NumWindings - 1)*NumWindings div 2), Xsc);
- 26:
- Result := Format('%.7g', [pctLoadLoss]);
- 27:
- Result := Format('%.7g', [pctNoLoadLoss]);
- 28:
- Result := Format('%.7g', [NormMaxHkVA]);
- 29:
- Result := Format('%.7g', [EmergMaxHkVA]);
- 31:
- Result := Format('%.7g', [Winding^[ActiveWinding].MaxTap]);
- 32:
- Result := Format('%.7g', [Winding^[ActiveWinding].MinTap]);
- 33:
- Result := Format('%-d', [Winding^[ActiveWinding].NumTaps]);
- 35:
- Result := Format('%.7g', [pctImag]);
- 36:
- Result := Format('%.7g', [ppm_FloatFactor / 1.0e-6]);
- 37:
- for i := 1 to NumWindings do
- Result := Result + Format('%.7g, ', [Winding^[i].rpu * 100.0]);
- 40:
- Result := StrYorN(XRconst);
- 41:
- Result := Format('%.7g', [XHL * 100.0]);
- 42:
- Result := Format('%.7g', [XHT * 100.0]);
- 43:
- Result := Format('%.7g', [XLT * 100.0]);
-
- 45:
- Result := GetWindingCurrentsResult;
- 46:
- case CoreType of
- 0:
- Result := 'shell';
- 1:
- Result := '1-phase';
- 3:
- Result := '3-leg';
- 5:
- Result := '5-Leg';
- end;
- 47:
- Result := Format('%.7g', [Winding^[ActiveWinding].RdcOhms]);
- 48:
- Result := inttostr(NumAmpRatings);
- 49:
- begin
- TempStr := '[';
- for k:= 1 to NumAmpRatings do
- TempStr := TempStr + floattoStrf(kVARatings[k-1], ffGeneral, 8, 4) + ',';
- TempStr := TempStr + ']';
- Result := TempStr;
- end;
- else
- Result := inherited GetPropertyValue(index);
- end;
-
- // Overrides
- case (Index - NumPropsThisClass) of
- 1:
- Result := Format('%-.5g', [normamps]); //Normamps
- 2:
- Result := Format('%-.5g', [emergamps]); //emergamps
- end;
-
- case Index of
- 12..16, 20, 37:
- Result := Result + ']';
- else
- end;
-
-end;
-
-procedure TTransfObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := '3'; //'phases';
- PropertyValue[2] := '2'; //'windings';
- // Winding Definition
- PropertyValue[3] := '1'; //'wdg';
- PropertyValue[4] := Getbus(1); //'bus';
- PropertyValue[5] := 'wye'; // 'conn';
- PropertyValue[6] := '12.47'; // IF 2or 3-phase: phase-phase ELSE actual winding
- PropertyValue[7] := '1000';
- PropertyValue[8] := '1.0';
- PropertyValue[9] := '0.2';
- PropertyValue[10] := '-1';
- PropertyValue[11] := '0';
-
- // General Data
- PropertyValue[12] := '';
- PropertyValue[13] := '';
- PropertyValue[14] := ''; // IF 1-phase: actual winding rating; ELSE phase-phase
- PropertyValue[15] := ''; // IF 1-phase: actual winding rating; ELSE phase-phase
- PropertyValue[16] := '';
- PropertyValue[17] := '7';
- PropertyValue[18] := '35';
- PropertyValue[19] := '30';
- PropertyValue[20] := ''; // x12 13 14... 23 24.. 34 ..
- PropertyValue[21] := '2';
- PropertyValue[22] := '.8';
- PropertyValue[23] := '.8';
- PropertyValue[24] := '65';
- PropertyValue[25] := '15';
- PropertyValue[26] := Format('%.7g', [pctLoadLoss]);
- PropertyValue[27] := Format('%.7g', [pctNoLoadLoss]); // Defaults to zero
- PropertyValue[28] := '';
- PropertyValue[29] := '';
- PropertyValue[30] := 'n'; // =y/n
- PropertyValue[31] := '1.10';
- PropertyValue[32] := '0.90';
- PropertyValue[33] := '32';
- PropertyValue[34] := '';
- PropertyValue[35] := '0';
- PropertyValue[36] := '1';
- PropertyValue[37] := '';
- PropertyValue[38] := '';
- PropertyValue[39] := '';
- PropertyValue[40] := 'NO';
- PropertyValue[41] := '7'; // Same as XHT ...
- PropertyValue[42] := '35';
- PropertyValue[43] := '30';
- PropertyValue[44] := 'Lag';
- PropertyValue[45] := '0';
- PropertyValue[46] := 'shell';
- PropertyValue[47] := '26.4'; // ohms
-
-
- inherited InitPropertyValues(NumPropsThisClass);
-
- // Override some Inherited properties
- PropertyValue[NumPropsThisClass + 1] := '400'; //Normamps
- PropertyValue[NumPropsThisClass + 2] := '600'; //emergamps
- PropertyValue[NumPropsThisClass + 3] := '0.007'; //Fault rate
- PropertyValue[NumPropsThisClass + 4] := '100'; // Pct Perm
- PropertyValue[NumPropsThisClass + 5] := '36'; // Hrs to repair
-
- ClearPropSeqArray; // so the overrides don't show up on save
-
-end;
-
-
function TTransfObj.RotatePhases(iPhs: Integer): Integer;
// For Delta connections or Line-Line voltages
begin
@@ -2172,30 +1534,24 @@ function TTransfObj.RotatePhases(iPhs: Integer): Integer;
end;
-procedure TTransfObj.MakePosSequence;
-{
- Converts default 3-phase transformer model into equivalent positive-sequence
- using scripting
-}
+procedure TTransfObj.MakePosSequence();
+// Converts default 3-phase transformer model into equivalent positive-sequence
var
- iW,
- i,
- N: Integer;
- S: String;
- Nodes: array[1..50] of Integer; // Big integer buffer
+ iW, i, N: Integer;
+ Nodes: array[1..50] of Integer; // big integer buffer
OnPhase1: Boolean;
+ new_norm, new_emerg: Double;
+ new_conns: Array of Integer;
+ new_buses: Array of String;
+ new_kVs, new_kVAs: Array of Double;
begin
-
- {First, determine if we can convert this one.}
+ // First, determine if we can convert this one.
if (FnPhases = 1) or (FNphases = 2) then
- begin //disable if any terminal not connected to phase one
+ begin // disable if any terminal not connected to phase one
for iW := 1 to NumWindings do
begin
OnPhase1 := FALSE;
- {Load up auxiliary parser}
- AuxParser.CmdString := GetBus(iW);
- AuxParser.NextParam;
- S := AuxParser.ParseAsBusName(N, pIntegerArray(@Nodes));
+ DSS.AuxParser.ParseAsBusName(GetBus(iW), N, pIntegerArray(@Nodes));
if N = 0 then
OnPhase1 := TRUE;
for i := 1 to N do
@@ -2209,36 +1565,42 @@ procedure TTransfObj.MakePosSequence;
end;
end;
- {Construct transformer definition string }
- S := 'Phases=1 Conns=(';
- for i := 1 to NumWindings do
- S := S + 'Wye ';
- S := S + ') buses=(';
+ // Construct AutoTrans definition string
+ SetLength(new_conns, NumWindings);
+ SetLength(new_buses, NumWindings);
+ SetLength(new_kVs, NumWindings);
+ SetLength(new_kVAs, NumWindings);
for i := 1 to NumWindings do
- S := S + Getbus(i) + ' ';
- S := S + ') kVS=(';
+ new_conns[i - 1] := 0;
+ for i := 1 to NumWindings do
+ new_buses[i - 1] := Getbus(i);
for i := 1 to NumWindings do
with Winding^[i] do
if (NPhases > 1) or (Connection <> 0) then
- S := S + Format(' %-.5g', [kVLL / SQRT3])
+ new_kVs[i - 1] := kVLL / SQRT3
else
- S := S + Format(' %-.5g', [kVLL]);
- S := S + ') kVAs=(';
+ new_kVs[i - 1] := kVLL;
for i := 1 to NumWindings do
with Winding^[i] do
- S := S + Format(' %-.5g', [kVA / FNPhases]);
- S := S + ')';
-
- S := S + ' NormHkVA=' + Format(' %-.5g %-.5g', [NormMaxHkVA / FNPhases, EmergMaxHkVA / FNPhases]);
-
- Parser.CmdString := S;
- Edit;
+ new_kVAs[i - 1] := kVA / FNPhases;
+
+ new_norm := NormMaxHkVA / FNPhases;
+ new_emerg := EmergMaxHkVA / FNPhases;
+
+ BeginEdit(True);
+ SetInteger(ord(TProp.Phases), 1);
+ SetIntegers(ord(TProp.Conns), new_conns);
+ SetStrings(ord(TProp.Buses), new_buses);
+ SetDoubles(ord(TProp.kVs), new_kVs);
+ SetDoubles(ord(TProp.kVAs), new_kVAs);
+ SetDouble(ord(TProp.NormHkVA), new_norm);
+ SetDouble(ord(TProp.EmergHkVA), new_emerg);
+ EndEdit(7);
inherited;
-
end;
procedure TTransfObj.AddNeutralToY(FreqMultiplier: Double);
@@ -2247,7 +1609,7 @@ procedure TTransfObj.AddNeutralToY(FreqMultiplier: Double);
Value: complex;
j: Integer;
begin
- {Account for neutral impedances}
+ // Account for neutral impedances
with YPrim_Series do
begin
for i := 1 to NumWindings do
@@ -2256,15 +1618,15 @@ procedure TTransfObj.AddNeutralToY(FreqMultiplier: Double);
begin
if Connection = 0 then
begin
- // handle wye, but ignore delta (and open wye)
+ // handle wye, but ignore delta (and open wye)
if Rneut >= 0 then
begin
- // <0 is flag for open neutral (Ignore)
+ // <0 is flag for open neutral (Ignore)
if (Rneut = 0) and (Xneut = 0) then
- // Solidly Grounded
+ // Solidly Grounded
Value := Cmplx(1000000, 0)
else
- // 1 microohm resistor
+ // 1 microohm resistor
Value := Cinv(Cmplx(Rneut, XNeut * FreqMultiplier));
j := i * fNconds;
AddElement(j, j, Value);
@@ -2272,11 +1634,11 @@ procedure TTransfObj.AddNeutralToY(FreqMultiplier: Double);
else
begin
- // Bump up neutral admittance a bit in case neutral is floating
+ // Bump up neutral admittance a bit in case neutral is floating
j := i * fNconds;
if ppm_FloatFactor <> 0.0 then
- SetElement(j, j, Cadd(GetElement(j, j), Cmplx(0.0, Y_PPM)));
- { SetElement(j, j, CmulReal_im(GetElement(j, j), ppm_FloatFactorPlusOne));}
+ SetElement(j, j, GetElement(j, j) + Cmplx(0.0, Y_PPM));
+ // SetElement(j, j, CmulReal_im(GetElement(j, j), ppm_FloatFactorPlusOne));
end;
end;
@@ -2296,15 +1658,15 @@ procedure TTransfObj.BuildYPrimComponent(YPrim_Component, Y_Terminal: TCMatrix);
begin
with YPrim_Component do
begin
- { Now, Put in Yprim matrix }
- {have to add every element of Y_terminal into Yprim somewhere}
+ // Now, Put in Yprim matrix
+ // have to add every element of Y_terminal into Yprim somewhere
NW2 := 2 * NumWindings;
for i := 1 to NW2 do
begin
for j := 1 to i do
begin
Value := Y_Terminal.GetElement(i, j);
- // This value goes in Yprim nphases times
+ // This value goes in Yprim nphases times
for k := 0 to Fnphases - 1 do
AddElemSym(TermRef^[i + k * NW2], TermRef^[j + k * NW2], Value);
end;
@@ -2320,47 +1682,42 @@ function TTransfObj.Get_BasekVLL(i: Integer): Double;
procedure TTransfObj.GICBuildYTerminal;
// Build YTerminal considering on resistance and no coupling to other winding.
-
var
i, j, idx: Integer;
yR: Complex;
Yadder: Complex;
-
begin
-
Y_Term.Clear;
Y_Term_NL.Clear;
for i := 1 to NumWindings do
begin
- {Use Rdc to build GIC model}
+ // Use Rdc to build GIC model
yR := Cmplx(1.0 / (Winding^[i].Rdcohms), 0.0); // convert to Siemens
with Y_Term do
begin
idx := 2 * i - 1;
SetElement(idx, idx, yR);
SetElement(idx + 1, idx + 1, yR);
- SetElemSym(idx, idx + 1, Cnegate(yR)); // set off-diagonals
+ SetElemSym(idx, idx + 1, -yR); // set off-diagonals
end;
end;
- {For GIC add a small *Conductance* to both conductors of each winding so that
- the matrix will always invert even if the user neglects to define a voltage
- reference on all sides}
+ // For GIC add a small *Conductance* to both conductors of each winding so that
+ // the matrix will always invert even if the user neglects to define a voltage
+ // reference on all sides
if ppm_FloatFactor <> 0.0 then
with Y_Term do
for i := 1 to NumWindings do
begin
Yadder := cmplx(-Winding^[i].Y_PPM, 0.0); // G + j0
for j := (2 * i - 1) to (2 * i) do
- SetElement(j, j, Cadd(GetElement(j, j), Yadder));
-{ SetElement(j, j, CmulReal_im(GetElement(j, j) , ppm_FloatFactorPlusOne));}
+ SetElement(j, j, GetElement(j, j) + Yadder);
+ // SetElement(j, j, CmulReal_im(GetElement(j, j) , ppm_FloatFactorPlusOne));
end;
-
end;
procedure TTransfObj.CalcY_Terminal(FreqMult: Double);
-
var
i,
j,
@@ -2372,8 +1729,8 @@ procedure TTransfObj.CalcY_Terminal(FreqMult: Double);
AT: TcMatrix;
Yadder: Complex;
Rmult: Double;
- {Function to fix a specification of a pu tap of 0.0}
- {Regcontrol can attempt to force zero tap position in some models}
+ // Function to fix a specification of a pu tap of 0.0
+ // Regcontrol can attempt to force zero tap position in some models
function ZeroTapFix(const tapvalue: Double): Double;
begin
if TapValue = 0.0 then
@@ -2383,27 +1740,23 @@ procedure TTransfObj.CalcY_Terminal(FreqMult: Double);
end;
begin
-
if ActiveCircuit.Solution.Frequency < 0.51 then
- {Build Yterminal for GIC ~dc simulation}
-
+ // Build Yterminal for GIC ~dc simulation
GICBuildYTerminal
-
else
- begin {Normal Y matrix build}
-
+ begin
+ // Normal Y matrix build
if XRConst then
RMult := FreqMult
else
RMult := 1.0;
-
- // Construct ZBMatrix;
+ // Construct ZBMatrix;
ZB.Clear;
ZBase := 1.0 / (VABase / Fnphases); // base ohms on 1.0 volt basis
for i := 1 to Numwindings - 1 do
- { convert pu to ohms on one volt base as we go... }
- ZB.SetElement(i, i, CmulReal(Cmplx(Rmult * (Winding^[1].Rpu + Winding^[i + 1].Rpu), Freqmult * XSC^[i]), ZBase));
+ // convert pu to ohms on one volt base as we go...
+ ZB.SetElement(i, i, Cmplx(Rmult * (Winding^[1].Rpu + Winding^[i + 1].Rpu), Freqmult * XSC^[i]) * ZBase);
// Off diagonals
k := NumWindings;
@@ -2413,11 +1766,14 @@ procedure TTransfObj.CalcY_Terminal(FreqMult: Double);
for j := i + 1 to Numwindings - 1 do
begin
SetElemSym(i, j,
- CmulReal(
- Csub(CAdd(GetElement(i, i), GetElement(j, j)),
- CmulReal(Cmplx(Rmult * (Winding^[i + 1].Rpu + Winding^[j + 1].Rpu), Freqmult * XSC^[k]),
- ZBase)
- ), 0.5));
+ (
+ GetElement(i, i)
+ + GetElement(j, j)
+ - Cmplx(
+ Rmult * (Winding^[i + 1].Rpu + Winding^[j + 1].Rpu),
+ Freqmult * XSC^[k]
+ ) * ZBase
+ ) * 0.5);
Inc(k);
end;
end;
@@ -2426,26 +1782,26 @@ procedure TTransfObj.CalcY_Terminal(FreqMult: Double);
if ZB.InvertError > 0 then
begin
- DoErrorMsg('TTransformerObj.CalcYPrim', 'Matrix Inversion Error for Transformer "' + Name + '"',
- 'Invalid impedance specified. Replaced with tiny conductance to ground.', 117);
+ DoErrorMsg('TTransformerObj.CalcYPrim',
+ Format(_('Matrix Inversion Error for Transformer "%s"'), [Name]),
+ _('Invalid impedance specified. Replaced with tiny conductance to ground.'), 117);
ZB.Clear;
for i := 1 to ZB.Order do
ZB.SetElement(i, i, Cmplx(EPSILON, 0.0));
end;
- // Now construct Y_Oneturn = AT * ZB.Invert * A
- { -1 1 0 ...
- A = -1 0 1 .. order: N-1 x N N = NumWindings
- ...
- -1 -1 ...
- AT = Transpose of A = 1 0 ... N X N-1
- 0 1 ..
- }
+ // Now construct Y_Oneturn = AT * ZB.Invert * A
+ // -1 1 0 ...
+ // A = -1 0 1 .. order: N-1 x N N = NumWindings
+ // ...
+ // -1 -1 ...
+ // AT = Transpose of A = 1 0 ... N X N-1
+ // 0 1 ..
Y_1Volt.Clear;
Y_1Volt_NL.Clear;
- {Allocate temp complex arrays}
+ // Allocate temp complex arrays
ctempArray1 := AllocMem(SizeOf(Complex) * NumWindings * 2);
ctempArray2 := AllocMem(SizeOf(Complex) * NumWindings * 2);
@@ -2468,22 +1824,21 @@ procedure TTransfObj.CalcY_Terminal(FreqMult: Double);
A^[k] := cONE
else
A^[k] := cZERO;
- ZB.MVmult(ctemparray1, A); {Zb.invert * A}
- AT.MVmult(ctempArray2, ctemparray1); {AT * Result}
+ ZB.MVmult(ctemparray1, A); // Zb.invert * A
+ AT.MVmult(ctempArray2, ctemparray1); // AT * Result
for j := 1 to NumWindings do
Y_1Volt.SetElement(j, i, ctempArray2^[j]);
end;
- {Add magnetizing Reactance to 2nd winding, assuming it is closest to the core
- Add both resistive element representing core losses and a reactive element representing
- magnetizing current
- }
+ // Add magnetizing Reactance to 2nd winding, assuming it is closest to the core
+ // Add both resistive element representing core losses and a reactive element representing
+ // magnetizing current
Y_1Volt_NL.AddElement(2, 2, Cmplx((pctNoLoadLoss / 100.0 / Zbase), -pctImag / 100.0 / Zbase / Freqmult));
- // should have admittance of one phase of the transformer on a one-volt, wye-connected base
+ // should have admittance of one phase of the transformer on a one-volt, wye-connected base
- // Now make into terminal admittance matrix and correct for actual voltage ratings
- // Y_Terminal = AT * Y_onevolt * A where V_onevolt = A * V_terminal
+ // Now make into terminal admittance matrix and correct for actual voltage ratings
+ // Y_Terminal = AT * Y_onevolt * A where V_onevolt = A * V_terminal
AT.Free;
@@ -2491,8 +1846,6 @@ procedure TTransfObj.CalcY_Terminal(FreqMult: Double);
Y_Term_NL.Clear;
AT := TcMatrix.Creatematrix(NumWindings * 2);
- // 8/22/2013 Added ZeroTapFix so that regcontrol can set a tap to zero
-
for i := 1 to NumWindings do
with Winding^[i] do
AT.SetElement(2 * i - 1, i, Cmplx(1.0 / (VBase * ZeroTapFix(puTap)), 0.0));
@@ -2515,106 +1868,88 @@ procedure TTransfObj.CalcY_Terminal(FreqMult: Double);
else
A^[k] := cZERO;
end;
- {Main Transformer part}
+ // Main Transformer part
Y_1Volt.MVmult(ctemparray1, A);
- AT.MVmult(ctemparray2, ctemparray1); {AT * Result}
+ AT.MVmult(ctemparray2, ctemparray1); // AT * Result
for j := 1 to 2 * NumWindings do
Y_Term.SetElement(j, i, ctemparray2^[j]);
- {No Load part}
+
+ // No Load part
Y_1Volt_NL.MVmult(ctemparray1, A);
- AT.MVmult(ctemparray2, ctemparray1); {AT * Result}
+ AT.MVmult(ctemparray2, ctemparray1); // AT * Result
for j := 1 to 2 * NumWindings do
Y_Term_NL.SetElement(j, i, ctemparray2^[j]);
end;
- {Add a small Admittance to both conductors of each winding so that
- the matrix will always invert even if the user neglects to define a voltage
- reference on all sides}
+ // Add a small Admittance to both conductors of each winding so that
+ // the matrix will always invert even if the user neglects to define a voltage
+ // reference on all sides
if ppm_FloatFactor <> 0.0 then
with Y_Term do
for i := 1 to NumWindings do
begin
Yadder := cmplx(0.0, Winding^[i].Y_PPM);
for j := (2 * i - 1) to (2 * i) do
- SetElement(j, j, Cadd(GetElement(j, j), Yadder));
-{ SetElement(j, j, CmulReal_im(GetElement(j, j) , ppm_FloatFactorPlusOne));}
+ SetElement(j, j, GetElement(j, j) + Yadder);
+ // SetElement(j, j, CmulReal_im(GetElement(j, j) , ppm_FloatFactorPlusOne));
end;
AT.Free;
Reallocmem(A, 0);
Reallocmem(ctemparray1, 0);
Reallocmem(ctemparray2, 0);
-
end;
Y_Terminal_FreqMult := Freqmult;
-
end;
-procedure TTransfObj.FetchXfmrCode(const Code: String);
+procedure TTransfObj.FetchXfmrCode();
var
Obj: TXfmrCodeObj;
i: Integer;
begin
- if DSS.XfmrCodeClass.SetActive(Code) then
- begin
- Obj := DSS.XfmrCodeClass.GetActiveObj;
- XfmrCode := LowerCase(Code);
+ if XfmrCodeObj = NIL then
+ Exit;
+
+ Obj := TXfmrCodeObj(XfmrCodeObj);
// set sizes and copy parameters
- Nphases := Obj.Fnphases;
- SetNumWindings(Obj.NumWindings);
- NConds := Fnphases + 1; // forces reallocation of terminals and conductors
- for i := 1 to NumWindings do
- with Winding^[i] do
- begin
- Connection := Obj.Winding^[i].Connection;
- kVLL := Obj.Winding^[i].kVLL;
- VBase := Obj.Winding^[i].VBase;
- kVA := Obj.Winding^[i].kVA;
- puTAP := Obj.Winding^[i].puTAP;
- Rpu := Obj.Winding^[i].Rpu;
- Rdcohms := Obj.Winding^[i].Rdcohms;
- RdcSpecified := Obj.Winding^[i].RdcSpecified;
- RNeut := Obj.Winding^[i].RNeut;
- Xneut := Obj.Winding^[i].Xneut;
- TapIncrement := Obj.Winding^[i].TapIncrement;
- MinTap := Obj.Winding^[i].MinTap;
- MaxTap := Obj.Winding^[i].MaxTap;
- NumTaps := Obj.Winding^[i].NumTaps;
- end;
- SetTermRef;
+ FNphases := Obj.Fnphases;
+ SetNumWindings(Obj.NumWindings);
+ NConds := Fnphases + 1; // forces reallocation of terminals and conductors
+ for i := 1 to NumWindings do
+ // Records can be copied
+ Winding^[i] := Obj.Winding^[i];
+
+ SetTermRef;
// Parameters for all windings
- XHL := Obj.XHL;
- XHT := Obj.XHT;
- XLT := Obj.XLT;
- for i := 1 to (NumWindings * (NumWindings - 1) div 2) do
- XSc^[i] := Obj.XSC^[i];
-
- ThermalTimeConst := Obj.ThermalTimeConst;
- n_thermal := Obj.n_thermal;
- m_thermal := Obj.m_thermal;
- FLrise := Obj.FLrise;
- HSrise := Obj.HSrise;
- pctLoadLoss := Obj.pctLoadLoss;
- pctNoLoadLoss := Obj.pctNoLoadLoss;
- pctImag := Obj.pctImag; // Omission corrected 12-14-18
- NormMaxHkVA := Obj.NormMaxHkVA;
- EmergMaxHkVA := Obj.EmergMaxHkVA;
- ppm_FloatFactor := Obj.ppm_FloatFactor;
- Yorder := fNConds * fNTerms;
- YPrimInvalid := TRUE;
- Y_Terminal_FreqMult := 0.0;
-
- NumAmpRatings := Obj.NumkVARatings;
- SetLength(kVARatings, NumAmpRatings);
- for i := 0 to High(kVARatings) do
- kVARatings[i] := Obj.kVARatings[i];
-
- RecalcElementData
- end
- else
- DoSimpleMsg('Xfmr Code:' + Code + ' not found.', 180);
+ XHL := Obj.XHL;
+ XHT := Obj.XHT;
+ XLT := Obj.XLT;
+ for i := 1 to (NumWindings * (NumWindings - 1) div 2) do
+ XSc^[i] := Obj.XSC^[i];
+
+ ThermalTimeConst := Obj.ThermalTimeConst;
+ n_thermal := Obj.n_thermal;
+ m_thermal := Obj.m_thermal;
+ FLrise := Obj.FLrise;
+ HSrise := Obj.HSrise;
+ pctLoadLoss := Obj.pctLoadLoss;
+ pctNoLoadLoss := Obj.pctNoLoadLoss;
+ pctImag := Obj.pctImag; // Omission corrected 12-14-18
+ NormMaxHkVA := Obj.NormMaxHkVA;
+ EmergMaxHkVA := Obj.EmergMaxHkVA;
+ ppm_FloatFactor := Obj.ppm_FloatFactor;
+ Yorder := fNConds * fNTerms;
+ YPrimInvalid := TRUE;
+ Y_Terminal_FreqMult := 0.0;
+
+ NumAmpRatings := Obj.NumkVARatings;
+ SetLength(kVARatings, NumAmpRatings);
+ for i := 0 to High(kVARatings) do
+ kVARatings[i] := Obj.kVARatings[i];
+
+ RecalcElementData
end;
-end.
+end.
\ No newline at end of file
diff --git a/src/PDElements/fuse.pas b/src/PDElements/fuse.pas
index 1283b349e..e4b60b816 100644
--- a/src/PDElements/fuse.pas
+++ b/src/PDElements/fuse.pas
@@ -6,20 +6,13 @@
All rights reserved.
----------------------------------------------------------
}
-
-{
- Created 11-1-00 from Recloser Control
-}
-{
- A Fuse is a control element that is connected to a terminal of a
- circuit element and controls the switches in the same or another terminal.
-
- The control is usually placed in the
- terminal of a line or transformer, but it could be any element
-
- CktElement to be controlled must already exist.
-
-}
+// A Fuse is a control element that is connected to a terminal of a
+// circuit element and controls the switches in the same or another terminal.
+//
+// The control is usually placed in the
+// terminal of a line or transformer, but it could be any element
+//
+// CktElement to be controlled must already exist.
interface
@@ -31,7 +24,7 @@ interface
CktElement,
DSSClass,
Arraydef,
- ucomplex,
+ UComplex, DSSUcomplex,
utilities,
TCC_Curve,
Math;
@@ -40,56 +33,60 @@ interface
FUSEMAXDIM = 6;
type
+{$SCOPEDENUMS ON}
+ TFuseProp = (
+ INVALID = 0,
+ MonitoredObj = 1,
+ MonitoredTerm = 2,
+ SwitchedObj = 3,
+ SwitchedTerm = 4,
+ FuseCurve = 5,
+ RatedCurrent = 6,
+ Delay = 7,
+ Action = 8,
+ Normal = 9,
+ State = 10
+ );
+{$SCOPEDENUMS OFF}
+
pStateArray = ^StateArray;
StateArray = array[1..FUSEMAXDIM] of EControlAction;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TFuse = class(TControlClass)
PRIVATE
-
+ TCC_CurveClass: TDSSClass;
PROTECTED
- procedure DefineProperties;
- function MakeLike(const FuseName: String): Integer; OVERRIDE;
+ procedure DefineProperties; override;
PUBLIC
constructor Create(dssContext: TDSSContext);
destructor Destroy; OVERRIDE;
-
- function Edit: Integer; OVERRIDE; // uses global parser
- function NewObject(const ObjName: String): Integer; OVERRIDE;
-
+ Function NewObject(const ObjName: String; Activate: Boolean = True): Pointer; OVERRIDE;
end;
-// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TFuseObj = class(TControlElem)
PRIVATE
+ PreviousControlledElement: TDSSCktElement;
+ //MonitoredElement: TDSSCktElement; // TODO: check why this exists (TControlElem already has one)
- MonitoredElement: TDSSCktElement;
-
- hAction: array[1..FUSEMAXDIM] of Integer; // handle to control queue actions
- FPresentState, FNormalState: pStateArray;
+ hAction: array[1..FUSEMAXDIM] of Integer; // handle to control queue actions
ReadyToBlow: array[1..FUSEMAXDIM] of Boolean;
CondOffset: Integer; // Offset for monitored terminal
cBuffer: pComplexArray; // Complexarray buffer
- NormalStateSet: Boolean;
-
- procedure InterpretFuseState(const param: string; const PropertyName: string);
function get_States(Idx: Integer): EControlAction;
- procedure set_States(Idx: Integer; const Value: EControlAction);
- function get_NormalStates(Idx: Integer): EControlAction;
- procedure set_NormalStates(Idx: Integer; const Value: EControlAction);
-
PUBLIC
-
FuseCurve: TTCC_CurveObj;
RatedCurrent: Double;
DelayTime: Double;
- MonitoredElementName: String;
MonitoredElementTerminal: Integer;
+ FPresentState, FNormalState: pStateArray;
+ NormalStateSet: Boolean;
constructor Create(ParClass: TDSSClass; const FuseName: String);
destructor Destroy; OVERRIDE;
+ procedure PropertySideEffects(Idx: Integer; previousIntVal: Integer = 0); override;
+ procedure MakeLike(OtherPtr: Pointer); override;
procedure RecalcElementData; OVERRIDE;
procedure CalcYPrim; OVERRIDE; // Always Zero for a Fuse
@@ -98,23 +95,13 @@ TFuseObj = class(TControlElem)
procedure DoPendingAction(const Phs, ProxyHdl: Integer); OVERRIDE; // Do the action that is pending from last sample
procedure Reset; OVERRIDE; // Reset to initial defined state
-
procedure GetCurrents(Curr: pComplexArray); OVERRIDE; // Get present value of terminal Curr
-
- function GetPropertyValue(Index: Integer): String; OVERRIDE;
- procedure InitPropertyValues(ArrayOffset: Integer); OVERRIDE;
- procedure DumpProperties(F: TFileStream; Complete: Boolean); OVERRIDE;
-
- property States[Idx: Integer]: EControlAction read get_States write set_States;
- property NormalStates[Idx: Integer]: EControlAction read get_NormalStates write set_NormalStates;
-
+ property States[Idx: Integer]: EControlAction read get_States;
end;
-{--------------------------------------------------------------------------}
implementation
uses
- ParserDel,
DSSClassDefs,
DSSGlobals,
Circuit,
@@ -125,277 +112,202 @@ implementation
DSSObjectHelper,
TypInfo;
+type
+ TObj = TFuseObj;
+ TProp = TFuseProp;
const
-
- NumPropsThisClass = 10;
-
+ NumPropsThisClass = Ord(High(TProp));
var
- TCC_CurveClass: TDSSClass;
-
-{General Module Function}
-
-function GetTccCurve(DSS: TDSSContext; const CurveName: String): TTCC_CurveObj;
+ PropInfo: Pointer = NIL;
+ ActionEnum, StateEnum: TDSSEnum;
+constructor TFuse.Create(dssContext: TDSSContext);
begin
+ if PropInfo = NIL then
+ begin
+ PropInfo := TypeInfo(TProp);
+ ActionEnum := TDSSEnum.Create('Fuse: Action', False, 1, 1,
+ ['close', 'open'], [ord(CTRL_CLOSE), ord(CTRL_OPEN)]);
+ StateEnum := TDSSEnum.Create('Fuse: State', False, 1, 1,
+ ['closed', 'open'], [ord(CTRL_CLOSE), ord(CTRL_OPEN)]);
+ end;
- Result := TCC_CurveClass.Find(CurveName);
-
- if Result = NIL then
- DoSimpleMsg(DSS, 'TCC Curve object: "' + CurveName + '" not found.', 401);
-
+ TCC_CurveClass := GetDSSClassPtr(dssContext, 'TCC_Curve');
+ inherited Create(dssContext, FUSE_CONTROL, 'Fuse');
end;
-
-{--------------------------------------------------------------------------}
-constructor TFuse.Create(dssContext: TDSSContext); // Creates superstructure for all Fuse objects
+destructor TFuse.Destroy;
begin
- inherited Create(dssContext);
-
- Class_name := 'Fuse';
- DSSClassType := DSSClassType + FUSE_CONTROL;
-
- DefineProperties;
-
- CommandList := TCommandList.Create(SliceProps(PropertyName, NumProperties));
- CommandList.Abbrev := TRUE;
-
- TCC_CurveClass := GetDSSClassPtr(DSS, 'TCC_Curve');
+ inherited Destroy;
end;
-{--------------------------------------------------------------------------}
-destructor TFuse.Destroy;
+procedure DoAction(obj: TObj; action: EControlAction);
+var
+ i: Integer;
+begin
+ case action of
+ CTRL_OPEN:
+ for i := 1 to FUSEMAXDIM do
+ Obj.FPresentState[i] := CTRL_OPEN;
+ CTRL_CLOSE:
+ for i := 1 to FUSEMAXDIM do
+ Obj.FPresentState[i] := CTRL_CLOSE;
+ end;
+ Obj.PropertySideEffects(ord(TProp.State));
+end;
+function GetFuseStateSize(Obj: TObj): Integer;
begin
- inherited Destroy;
+ Result := FUSEMAXDIM;
+ if Obj.ControlledElement <> NIL then
+ Result := Obj.ControlledElement.NPhases;
end;
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TFuse.DefineProperties;
+var
+ obj: TObj = NIL; // NIL (0) on purpose
begin
-
Numproperties := NumPropsThisClass;
- CountProperties; // Get inherited property count
- AllocatePropertyArrays;
-
-
- // Define Property names
-
- PropertyName[1] := 'MonitoredObj';
- PropertyName[2] := 'MonitoredTerm';
- PropertyName[3] := 'SwitchedObj';
- PropertyName[4] := 'SwitchedTerm';
- PropertyName[5] := 'FuseCurve';
- PropertyName[6] := 'RatedCurrent';
- PropertyName[7] := 'Delay';
- PropertyName[8] := 'Action';
- PropertyName[9] := 'Normal';
- PropertyName[10] := 'State';
-
- PropertyHelp[1] := 'Full object name of the circuit element, typically a line, transformer, load, or generator, ' +
- 'to which the Fuse is connected.' +
- ' This is the "monitored" element. ' +
- 'There is no default; must be specified.';
- PropertyHelp[2] := 'Number of the terminal of the circuit element to which the Fuse is connected. ' +
- '1 or 2, typically. Default is 1.';
- PropertyHelp[3] := 'Name of circuit element switch that the Fuse controls. ' +
- 'Specify the full object name.' +
- 'Defaults to the same as the Monitored element. ' +
- 'This is the "controlled" element.';
- PropertyHelp[4] := 'Number of the terminal of the controlled element in which the switch is controlled by the Fuse. ' +
- '1 or 2, typically. Default is 1. Assumes all phases of the element have a fuse of this type.';
- PropertyHelp[5] := 'Name of the TCC Curve object that determines the fuse blowing. Must have been previously defined as a TCC_Curve object.' +
- ' Default is "Tlink". ' +
- 'Multiplying the current values in the curve by the "RatedCurrent" value gives the actual current.';
- PropertyHelp[6] := 'Multiplier or actual phase amps for the phase TCC curve. Defaults to 1.0.';
- PropertyHelp[7] := 'Fixed delay time (sec) added to Fuse blowing time determined from the TCC curve. Default is 0.0. Used to represent fuse clearing time or any other delay.';
- PropertyHelp[8] := 'DEPRECATED. See "State" property.';
- PropertyHelp[9] := 'ARRAY of strings {Open | Closed} representing the Normal state of the fuse in each phase of the controlled element. ' +
- 'The fuse reverts to this state for reset, change of mode, etc. ' +
- 'Defaults to "State" if not specifically declared.';
- PropertyHelp[10] := 'ARRAY of strings {Open | Closed} representing the Actual state of the fuse in each phase of the controlled element. ' +
- 'Upon setting, immediately forces state of fuse(s). Simulates manual control on Fuse. Defaults to Closed for all phases.';
+ CountPropertiesAndAllocate();
+ PopulatePropertyNames(0, NumPropsThisClass, PropInfo);
+
+ // object properties
+ PropertyType[ord(TProp.FuseCurve)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.FuseCurve)] := ptruint(@obj.FuseCurve);
+ PropertyOffset2[ord(TProp.FuseCurve)] := ptruint(TCC_CurveClass);
+
+ PropertyType[ord(TProp.MonitoredObj)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.MonitoredObj)] := ptruint(@obj.MonitoredElement);
+ //PropertyWriteFunction[ord(TProp.MonitoredObj)] := @SetMonitoredElement;
+ //PropertyFlags[ord(TProp.MonitoredObj)] := [TPropertyFlag.WriteByFunction];//[TPropertyFlag.CheckForVar]; // not required for general cktelements
+
+ PropertyType[ord(TProp.SwitchedObj)] := TPropertyType.DSSObjectReferenceProperty;
+ PropertyOffset[ord(TProp.SwitchedObj)] := ptruint(@obj.FControlledElement);
+ PropertyWriteFunction[ord(TProp.SwitchedObj)] := @SetControlledElement;
+ PropertyFlags[ord(TProp.SwitchedObj)] := [TPropertyFlag.WriteByFunction];// [TPropertyFlag.CheckForVar]; // not required for general cktelements
+
+ // integer properties
+ PropertyType[ord(TProp.MonitoredTerm)] := TPropertyType.IntegerProperty;
+ PropertyType[ord(TProp.SwitchedTerm)] := TPropertyType.IntegerProperty;
+ PropertyOffset[ord(TProp.MonitoredTerm)] := ptruint(@obj.MonitoredElementTerminal);
+ PropertyOffset[ord(TProp.SwitchedTerm)] := ptruint(@obj.ElementTerminal);
+
+ // double properties
+ PropertyOffset[ord(TProp.RatedCurrent)] := ptruint(@obj.RatedCurrent);
+ PropertyOffset[ord(TProp.Delay)] := ptruint(@obj.DelayTime);
+
+ // enum action
+ PropertyType[ord(TProp.Action)] := TPropertyType.StringEnumActionProperty;
+ PropertyOffset[ord(TProp.Action)] := ptruint(@DoAction);
+ PropertyOffset2[ord(TProp.Action)] := PtrInt(ActionEnum);
+
+ PropertyType[ord(TProp.Normal)] := TPropertyType.MappedStringEnumArrayProperty;
+ PropertyOffset[ord(TProp.Normal)] := ptrint(@obj.FNormalState);
+ PropertyOffset2[ord(TProp.Normal)] := ptrint(StateEnum);
+ PropertyOffset3[ord(TProp.Normal)] := ptrint(@GetFuseStateSize);
+ PropertyFlags[ord(TProp.Normal)] := [TPropertyFlag.SizeIsFunction]; // FControlledElement.NPhases
+
+ PropertyType[ord(TProp.State)] := TPropertyType.MappedStringEnumArrayProperty;
+ PropertyOffset[ord(TProp.State)] := ptrint(@obj.FPresentState); //TODO: why GetPropertyValue doesn't use get_State(x) in the original codebase?
+ PropertyOffset2[ord(TProp.State)] := ptrint(StateEnum);
+ PropertyOffset3[ord(TProp.State)] := ptrint(@GetFuseStateSize);
+ PropertyFlags[ord(TProp.State)] := [TPropertyFlag.SizeIsFunction]; // FControlledElement.NPhases
ActiveProperty := NumPropsThisClass;
- inherited DefineProperties; // Add defs of inherited properties to bottom of list
-
+ inherited DefineProperties;
end;
-{--------------------------------------------------------------------------}
-function TFuse.NewObject(const ObjName: String): Integer;
+function TFuse.NewObject(const ObjName: String; Activate: Boolean): Pointer;
+var
+ Obj: TObj;
begin
- // Make a new Fuse and add it to Fuse class list
- with ActiveCircuit do
- begin
- ActiveCktElement := TFuseObj.Create(Self, ObjName);
- Result := AddObjectToList(ActiveDSSObject);
- end;
+ Obj := TObj.Create(Self, ObjName);
+ if Activate then
+ ActiveCircuit.ActiveCktElement := Obj;
+ Obj.ClassIndex := AddObjectToList(Obj, Activate);
+ Result := Obj;
end;
-{--------------------------------------------------------------------------}
-
-
-{--------------------------------------------------------------------------}
-function TFuse.Edit: Integer;
+procedure TFuseObj.PropertySideEffects(Idx: Integer; previousIntVal: Integer);
var
- ParamPointer, i: Integer;
- ParamName: String;
- Param: String;
-
+ i: Integer;
begin
-
- // continue parsing WITH contents of Parser
- DSS.ActiveFuseObj := ElementList.Active;
- ActiveCircuit.ActiveCktElement := DSS.ActiveFuseObj;
-
- Result := 0;
-
- with DSS.ActiveFuseObj do
- begin
-
- ParamPointer := 0;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
- while Length(Param) > 0 do
+ case Idx of
+ // Default the controlled element to the monitored element
+ ord(TProp.MonitoredObj):
+ ControlledElement := MonitoredElement;
+ ord(TProp.MonitoredTerm):
+ ElementTerminal := MonitoredElementTerminal;
+ ord(TProp.Normal):
+ NormalStateSet := TRUE;
+ ord(TProp.State):
begin
- if Length(ParamName) = 0 then
- Inc(ParamPointer)
- else
- ParamPointer := CommandList.GetCommand(ParamName);
-
- if (ParamPointer > 0) and (ParamPointer <= NumProperties) then
- PropertyValue[ParamPointer] := Param;
-
- case ParamPointer of
- 0:
- DoSimpleMsg('Unknown parameter "' + ParamName + '" for Object "' + Class_Name + '.' + Name + '"', 402);
- 1:
- MonitoredElementName := lowercase(param);
- 2:
- MonitoredElementTerminal := Parser.IntValue;
- 3:
- ElementName := lowercase(param);
- 4:
- ElementTerminal := Parser.IntValue;
- 5:
- FuseCurve := GetTCCCurve(DSS, Param);
- 6:
- RatedCurrent := Parser.Dblvalue;
- 7:
- DelayTime := Parser.DblValue;
- 9:
- begin
- InterpretFuseState(Param, ParamName); // set the normal state
- NormalStateSet := TRUE;
- end;
- 8, 10:
- InterpretFuseState(Param, ParamName); // set the present state
- else
- // Inherited parameters
- ClassEdit(DSS.ActiveFuseObj, ParamPointer - NumPropsthisClass)
- end;
-
- case ParamPointer of
- {Default the controlled element to the monitored element}
- 1:
- ElementName := MonitoredElementName;
- 2:
- ElementTerminal := MonitoredElementTerminal;
- 10:
- begin
- for i := 1 to FNPhases do
- if not NormalStateSet then
- FNormalState[i] := FPresentState[i];
-
- NormalStateSet := TRUE; // normal state will default to state only the 1st state is specified.
- end;
+ if not NormalStateSet then
+ begin
+ for i := 1 to FNPhases do
+ FNormalState[i] := FPresentState[i];
+
+ NormalStateSet := TRUE; // normal state will default to state only the 1st state is specified.
end;
+ if ControlledElement = NIL then
+ Exit;
- ParamName := Parser.NextParam;
- Param := Parser.StrValue;
+ ControlledElement.ActiveTerminalIdx := ElementTerminal;
+ for i := 1 to ControlledElement.NPhases do
+ if FPresentState[i] = CTRL_OPEN then
+ ControlledElement.Closed[i] := FALSE
+ else
+ ControlledElement.Closed[i] := TRUE;
end;
-
- RecalcElementData;
end;
-
+ inherited PropertySideEffects(Idx, previousIntVal);
end;
-
-{--------------------------------------------------------------------------}
-function TFuse.MakeLike(const FuseName: String): Integer;
+procedure TFuseObj.MakeLike(OtherPtr: Pointer);
var
- OtherFuse: TFuseObj;
+ Other: TObj;
i: Integer;
begin
- Result := 0;
- {See if we can find this Fuse name in the present collection}
- OtherFuse := Find(FuseName);
- if OtherFuse = NIL then
- begin
- DoSimpleMsg('Error in Fuse MakeLike: "' + FuseName + '" Not Found.', 403);
- Exit;
- end;
+ Other := TObj(OtherPtr);
+ FNPhases := Other.Fnphases;
+ NConds := Other.Fnconds; // Force Reallocation of terminal stuff
- with DSS.ActiveFuseObj do
- begin
- NPhases := OtherFuse.Fnphases;
- NConds := OtherFuse.Fnconds; // Force Reallocation of terminal stuff
-
- ElementName := OtherFuse.ElementName;
- ElementTerminal := OtherFuse.ElementTerminal;
- ControlledElement := OtherFuse.ControlledElement; // Pointer to target circuit element
-
- MonitoredElement := OtherFuse.MonitoredElement; // Pointer to target circuit element
- MonitoredElementName := OtherFuse.MonitoredElementName; // Pointer to target circuit element
- MonitoredElementTerminal := OtherFuse.MonitoredElementTerminal; // Pointer to target circuit element
+ ElementTerminal := Other.ElementTerminal;
+ ControlledElement := Other.ControlledElement; // Pointer to target circuit element
- FuseCurve := OtherFuse.FuseCurve;
- RatedCurrent := OtherFuse.RatedCurrent;
-
- for i := 1 to Min(FUSEMAXDIM, ControlledElement.Nphases) do
- begin
- FPresentState[i] := OtherFuse.FPresentState[i];
- FNormalState[i] := OtherFuse.FNormalState[i];
- end;
- CondOffset := OtherFuse.CondOffset;
+ MonitoredElement := Other.MonitoredElement; // Pointer to target circuit element
+ MonitoredElementTerminal := Other.MonitoredElementTerminal; // Pointer to target circuit element
- for i := 1 to ParentClass.NumProperties do
- PropertyValue[i] := OtherFuse.PropertyValue[i];
+ FuseCurve := Other.FuseCurve;
+ RatedCurrent := Other.RatedCurrent;
- end
+ for i := 1 to Min(FUSEMAXDIM, ControlledElement.Nphases) do
+ begin
+ FPresentState[i] := Other.FPresentState[i];
+ FNormalState[i] := Other.FNormalState[i];
+ end;
+ CondOffset := Other.CondOffset;
end;
-
-{==========================================================================}
-{ TFuseObj }
-{==========================================================================}
-
-
-{--------------------------------------------------------------------------}
constructor TFuseObj.Create(ParClass: TDSSClass; const FuseName: String);
-
var
i: Integer;
-
begin
inherited Create(ParClass);
- Name := LowerCase(FuseName);
+ Name := AnsiLowerCase(FuseName);
DSSObjType := ParClass.DSSClassType;
- NPhases := 3; // Directly set conds and phases
+ FNPhases := 3; // Directly set conds and phases
Fnconds := 3;
- Nterms := 1; // this forces allocation of terminals and conductors
- // in base class
-
-
- ElementName := '';
+ Nterms := 1; // this forces allocation of terminals and conductors in base class
ControlledElement := NIL;
ElementTerminal := 1;
- MonitoredElementName := '';
MonitoredElementTerminal := 1;
MonitoredElement := NIL;
+ PreviousControlledElement := NIL;
- FuseCurve := GetTccCurve(DSS, 'tlink');
+ FuseCurve := TFuse(ParClass).TCC_CurveClass.Find('tlink');//TODO: is an error message ever required for this?
RatedCurrent := 1.0;
@@ -419,16 +331,11 @@ constructor TFuseObj.Create(ParClass: TDSSClass; const FuseName: String);
DSSObjType := ParClass.DSSClassType; //cap_CONTROL;
- InitPropertyValues(0);
-
-
// RecalcElementData;
-
end;
destructor TFuseObj.Destroy;
begin
- MonitoredElementName := '';
Reallocmem(cBuffer, 0);
ReallocMem(FPresentState, 0);
ReallocMem(FNormalState, 0);
@@ -436,51 +343,46 @@ destructor TFuseObj.Destroy;
inherited Destroy;
end;
-{--------------------------------------------------------------------------}
procedure TFuseObj.RecalcElementData;
-
var
- DevIndex, i: Integer;
-
+ i: Integer;
begin
-
- Devindex := GetCktElementIndex(MonitoredElementName); // Global function
- if DevIndex > 0 then
+ if MonitoredElement <> NIL then
begin
- MonitoredElement := ActiveCircuit.CktElements.Get(DevIndex);
- Nphases := MonitoredElement.NPhases; // Force number of phases to be same
+ FNphases := MonitoredElement.NPhases; // Force number of phases to be same
if Fnphases > FUSEMAXDIM then
- DosimpleMsg('Warning: Fuse ' + Self.Name + ': Number of phases > Max fuse dimension.', 404);
+ DoSimpleMsg('Warning: Fuse %s: Number of phases > Max fuse dimension.', [Self.Name], 404);
if MonitoredElementTerminal > MonitoredElement.Nterms then
begin
- DoErrorMsg('Fuse: "' + Name + '"',
- 'Terminal no. "' + '" does not exist.',
- 'Re-specify terminal no.', 404);
+ DoErrorMsg(Format(_('Fuse: "%s"'), [Name]),
+ Format(_('Terminal no. "%d" does not exist.'), [MonitoredElementTerminal]),
+ _('Re-specify terminal no.'), 404);
end
else
begin
- // Sets name of i-th terminal's connected bus in Fuse's buslist
+ // Sets name of i-th terminal's connected bus in Fuse's buslist
Setbus(1, MonitoredElement.GetBus(MonitoredElementTerminal));
- // Allocate a buffer bigenough to hold everything from the monitored element
+ // Allocate a buffer big enough to hold everything from the monitored element
ReAllocMem(cBuffer, SizeOF(cbuffer^[1]) * MonitoredElement.Yorder);
CondOffset := (MonitoredElementTerminal - 1) * MonitoredElement.NConds; // for speedy sampling
end;
end;
-{Check for existence of Controlled Element}
+ // Check for existence of Controlled Element
- // If previously assigned, reset HasOCPDevice flag in case this is a move
- if Assigned(ControlledElement) then
- ControlledElement.HasOCPDevice := FALSE;
+ // If previously assigned, reset HasOCPDevice flag in case this is a move
+ if (PreviousControlledElement <> NIL) then
+ begin
+ Exclude(PreviousControlledElement.Flags, Flg.HasOCPDevice);
+ PreviousControlledElement := ControlledElement;
+ end;
- Devindex := GetCktElementIndex(ElementName); // Global function
- if DevIndex > 0 then
+ if ControlledElement <> NIL then
begin // Both CktElement and monitored element must already exist
- ControlledElement := ActiveCircuit.CktElements.Get(DevIndex);
ControlledElement.ActiveTerminalIdx := ElementTerminal; // Make the 1 st terminal active
if Enabled then
- ControlledElement.HasOCPDevice := TRUE; // For Reliability calcs
+ Include(ControlledElement.Flags, Flg.HasOCPDevice); // For Reliability calcs
// Open/Close State of controlled element based on state assigned to the control
for i := 1 to Min(FUSEMAXDIM, ControlledElement.Nphases) do
@@ -496,13 +398,13 @@ procedure TFuseObj.RecalcElementData;
end
else
begin
- ControlledElement := NIL; // element not found
- DoErrorMsg('Fuse: "' + Self.Name + '"', 'CktElement Element "' + ElementName + '" Not Found.',
- ' Element must be defined previously.', 405);
+ // element not found
+ DoErrorMsg(Format(_('Fuse: "%s"'), [Self.Name]),
+ _('CktElement for SwitchedObj is not set.'),
+ _('Element must be defined previously.'), 405);
end;
end;
-{--------------------------------------------------------------------------}
procedure TFuseObj.CalcYPrim;
begin
// leave YPrims as nil and they will be ignored
@@ -510,22 +412,19 @@ procedure TFuseObj.CalcYPrim;
// IF YPrim=nil THEN YPrim := TcMatrix.CreateMatrix(Yorder);
end;
-{--------------------------------------------------------------------------}
procedure TFuseObj.GetCurrents(Curr: pComplexArray);
var
i: Integer;
begin
-
for i := 1 to Fnconds do
Curr^[i] := CZERO;
-
end;
-{--------------------------------------------------------------------------}
procedure TFuseObj.DoPendingAction(const Phs, ProxyHdl: Integer);
// Do what we're instructed by the control queue
// Theoretically, there shouldn't be anything on the queue unless we have to do something
-{Only legal action is to open one phase}
+
+// Only legal action is to open one phase
begin
if Phs > FUSEMAXDIM then
Exit;
@@ -543,66 +442,11 @@ procedure TFuseObj.DoPendingAction(const Phs, ProxyHdl: Integer);
end;
end;
-{--------------------------------------------------------------------------}
-procedure TFuseObj.InterpretFuseState(const param: String; const PropertyName: String);
-var
- i: Integer;
- DataStr2: String;
-begin
- if (LowerCase(PropertyName[1]) = 'a') then
- begin // action (deprecated) Will be removed
- for i := 1 to FUSEMAXDIM do
- begin
- case LowerCase(PropertyName[1]) of
- 'o':
- States[i] := CTRL_OPEN;
- 'c':
- States[i] := CTRL_CLOSE;
- end;
- end;
-
- Exit;
- end;
-
- AuxParser.CmdString := param; // Load up Parser
- {DataStr1 :=} AuxParser.NextParam; // ignore
- DataStr2 := AuxParser.StrValue;
-
- i := 1;
- while (Length(DataStr2) > 0) and (i < FUSEMAXDIM) do
- begin
- if (LowerCase(PropertyName[1]) = 's') then
- begin // state
- case LowerCase(DataStr2)[1] of
- 'o':
- States[i] := CTRL_OPEN;
- 'c':
- States[i] := CTRL_CLOSE;
- end;
- end
- else // 'normal'
- begin
- case LowerCase(DataStr2)[1] of
- 'o':
- NormalStates[i] := CTRL_OPEN;
- 'c':
- NormalStates[i] := CTRL_CLOSE;
- end;
- end;
-
- {DataStr1 :=} AuxParser.NextParam; // ignore
- DataStr2 := AuxParser.StrValue;
- inc(i);
- end;
-End;
-
-{--------------------------------------------------------------------------}
procedure TFuseObj.Sample;
var
i: Integer;
Cmag: Double;
TripTime: Double;
-
begin
ControlledElement.ActiveTerminalIdx := ElementTerminal;
MonitoredElement.GetCurrents(cBuffer);
@@ -620,7 +464,7 @@ procedure TFuseObj.Sample;
begin
TripTime := -1.0;
- {Check Phase Trip, if any}
+ // Check Phase Trip, if any
if FuseCurve <> NIL then
begin
@@ -635,7 +479,7 @@ procedure TFuseObj.Sample;
begin // Then arm for an open operation
hAction[i] := ControlQueue.Push(Solution.DynaVars.intHour, Solution.DynaVars.t + TripTime + Delaytime, i, 0, Self);
ReadyToBlow[i] := TRUE;
- end; {With}
+ end;
end
else
begin
@@ -645,67 +489,8 @@ procedure TFuseObj.Sample;
ReadyToBlow[i] := FALSE;
end;
end;
-
- end; {IF PresentState=CLOSE}
- end; {With}
-end;
-
-
-{--------------------------------------------------------------------------}
-procedure TFuseObj.DumpProperties(F: TFileStream; Complete: Boolean);
-
-var
- i: Integer;
-
-begin
- inherited DumpProperties(F, Complete);
-
- with ParentClass do
- for i := 1 to NumProperties do
- begin
- FSWriteln(F, '~ ' + PropertyName^[i] + '=' + PropertyValue[i]);
+ end; // IF PresentState=CLOSE
end;
-
- if Complete then
- FSWriteln(F);
-
-end;
-
-function TFuseObj.GetPropertyValue(Index: Integer): String;
-var
- i: Integer;
-begin
- case Index of
- 9..10:
- Result := '[';
- else
- Result := '';
- end;
-
- case index of // Special cases
- 10:
- if ControlledElement <> Nil Then
- for i := 1 to ControlledElement.NPhases do
- if FPresentState[i] = CTRL_OPEN then
- Result := Result + 'open' + ', '
- else
- Result := Result + 'closed' + ', ';
- 9:
- if ControlledElement <> Nil Then
- for i := 1 to ControlledElement.NPhases do
- if FNormalState[i] = CTRL_OPEN then
- Result := Result + 'open' + ', '
- else
- Result := Result + 'closed' + ', ';
-
- else
- Result := inherited GetPropertyValue(index);
- end;
-
- case Index of
- 9..10:
- Result := Result + ']';
- end;
end;
procedure TFuseObj.Reset;
@@ -743,51 +528,6 @@ function TFuseObj.get_States(Idx: Integer): EControlAction;
Result := FPresentState[Idx];
end;
-procedure TFuseObj.set_States(Idx: Integer; const Value: EControlAction);
-begin
- if States[Idx] = Value then
- Exit;
-
- FPresentState[Idx] := Value;
-
- if ControlledElement = NIL then
- Exit;
-
- ControlledElement.ActiveTerminalIdx := ElementTerminal;
- if Value = CTRL_OPEN then
- ControlledElement.Closed[Idx] := FALSE
- else
- ControlledElement.Closed[Idx] := TRUE;
-end;
-
-procedure TFuseObj.set_NormalStates(Idx: Integer; const Value: EControlAction);
-begin
- //TODO: validate Idx if exposed through the end-user API
- FNormalState[Idx] := Value;
- NormalStateSet := TRUE;
-end;
-
-procedure TFuseObj.InitPropertyValues(ArrayOffset: Integer);
-begin
-
- PropertyValue[1] := ''; //'element';
- PropertyValue[2] := '1'; //'terminal';
- PropertyValue[3] := '';
- PropertyValue[4] := '1'; //'terminal';
- PropertyValue[5] := 'Tlink';
- PropertyValue[6] := '1.0';
- PropertyValue[7] := '0';
- PropertyValue[8] := ''; // action
- PropertyValue[9] := '[close, close, close]'; // normal
- PropertyValue[10] := '[close,close,close]'; // state
-
- inherited InitPropertyValues(NumPropsThisClass);
-
-end;
-
-function TFuseObj.get_NormalStates(Idx: Integer): EControlAction;
-begin
- Result := FNormalState[Idx];
-end;
-
+finalization ActionEnum.Free;
+ StateEnum.Free;
end.
diff --git a/src/Parallel_Lib/Parallel_Lib.pas b/src/Parallel_Lib/Parallel_Lib.pas
deleted file mode 100644
index bb9473e99..000000000
--- a/src/Parallel_Lib/Parallel_Lib.pas
+++ /dev/null
@@ -1,173 +0,0 @@
-unit Parallel_Lib;
-
-{**********************Parallel Library for OpenDSS*****************************
-* This library gives acces to the processor to handle the affinity of the
-* Specified process and thread to a specific processor core
-* This library gives access to the Windows API for such purpose
-* Written by Davis Montenegro 06-17-2016
-*
-* Used for multi-core processing in OpenDSS
-********************************************************************************
-}
-
-
-interface
-
-uses
-{$IFNDEF FPC}
- Winapi.Windows, Winapi.Messages, vcl.Dialogs,
-{$ELSE}
- {$IFDEF MSWINDOWS}
- windows,
- {$ELSE}
- initc, cpucount, BaseUnix, Unix,
- {$ENDIF}
-{$ENDIF}
- SysUtils, Variants, Classes, math;
-
-const REALTIME_PRIORITY_CLASS = 16;
-
-{$IFDEF MSWINDOWS}
-const THREAD_PRIORITY_TIME_CRITICAL = 15;
-type
- TParallel_Lib = class(TObject)
- public
- function Set_Thread_Affinity(Hnd : THandle; CPU : integer): Integer;
- function Set_Process_Priority(Hnd: THandle; P_priority : integer): Integer;
- function Set_Thread_Priority(Hnd: THandle; T_priority : integer): Integer;
- function Get_Thread_Priority(Hnd: THandle): String;
- function Get_Number_of_CPUs(): Integer;
- end;
-{$ELSE}
-const THREAD_PRIORITY_TIME_CRITICAL = tpTimeCritical;
-type
- TParallel_Lib = class(TObject)
- public
- function Set_Thread_Affinity(Hnd: TThreadId; CPU: integer): Integer;
- function Set_Process_Priority(Hnd: TPid; P_priority: integer): Integer;
- function Set_Thread_Priority(thread: TThread; T_priority: TThreadPriority): Integer;
- function Get_Thread_Priority(Hnd: TThreadId): String;
- function Get_Number_of_CPUs(): Integer;
- end;
-
-{$ENDIF}
-
-implementation
-{$IFNDEF UNIX}
- function TParallel_Lib.Set_Thread_Affinity(Hnd : THandle; CPU : integer): Integer;
- var
- CPU_bit : integer;
- Op_Result : Dword;
- begin
- CPU_bit := floor(power(2, CPU));
- Op_Result := SetThreadAffinityMask(Hnd,CPU_bit);
- if Op_Result = 0 then raise Exception.Create('Error setting thread affinity mask : ' + IntToStr(GetLastError));
- Result := Op_Result;
- end;
- function TParallel_Lib.Set_Process_Priority(Hnd: THandle; P_priority : integer):Integer;
- var
- Op_result : bool;
- begin
- Result := 0;
- Op_Result := SetPriorityClass(Hnd, P_priority);
- if Op_result=false then
- {$IFNDEF FPC}ShowMessage{$ELSE}WriteLn{$ENDIF}('Impossible to set the Process Priority');
- if Op_result then Result :=1;
- end;
- function TParallel_Lib.Set_Thread_Priority(Hnd: THandle; T_priority : integer):Integer;
- var
- Op_result : bool;
- begin
- Result := 0;
- Op_Result := SetThreadPriority(Hnd,T_priority);
- if Op_Result = false then
- {$IFNDEF FPC}ShowMessage{$ELSE}WriteLn{$ENDIF}('Impossible to set the Thread Priority');
- if Op_result then Result :=1;
- end;
- function TParallel_Lib.Get_Thread_Priority(Hnd: THandle): String;
- var
- Num_priority : integer;
- begin
- Num_Priority := GetThreadPriority(Hnd);
- case Num_Priority of
- 0: Result := 'Normal';
- 1: Result := 'Above Normal';
- 2: Result := 'Highest';
- 15: Result := 'Time Critical';
- -1: Result := 'Below Normal';
- -2: Result := 'Lowest';
- else
- Result := 'Not known';
- end;
- end;
- function TParallel_Lib.Get_Number_of_CPUs(): Integer;
- begin
- Result := CPUCount;
- end;
-{$ELSE}
- //function pthread_setaffinity_np(pid : Ptruint; cpusetsize : QWord; cpuset : pointer) : longint; cdecl; external;
-
- function TParallel_Lib.Set_Thread_Affinity(Hnd: TThreadId; CPU: integer): Integer;
- // The following commented code segfaults but it's based on
- // http://free-pascal-general.1045716.n5.nabble.com/GetAffinity-SetAffinity-tp3351231p5717539.html
- // An alternative may be to include a separate C file to handle this using the correct macros
- {const
- cpu_SetSize = 8; // 64 cores max
- var
- cpu_set : QWord; //cpu_set_type sufficient for 64-core CPU
- Op_Result : longint;
- begin
- cpu_set := 1 shl CPU;
- Op_Result := pthread_setaffinity_np(Hnd,cpu_SetSize,@cpu_set);
- if Op_Result = 0 then raise Exception.Create('Error setting thread affinity mask');
- Result := Op_Result;
- end;}
- begin
- Result := 0;
- end;
-
- function TParallel_Lib.Set_Thread_Priority(thread: TThread; T_priority : TThreadPriority): Integer;
- begin
- Result := 0;
- try
- thread.Priority := T_priority;
- if thread.Priority = T_priority then
- Result := 1;
- except
- WriteLn('Impossible to set the Thread Priority');
- end;
- end;
-
- function TParallel_Lib.Set_Process_Priority(Hnd: TPid; P_priority : integer):Integer;
- var
- Op_result : Integer;
- begin
- Result := 0;
- Op_Result := fpsetpriority (prio_process,Hnd,P_priority);
- if Op_result = -1 then
- WriteLn('Impossible to set the Process Priority');
- if Op_result <> 0 then Result :=1;
- end;
-
- function TParallel_Lib.Get_Thread_Priority(Hnd: TThreadId): String;
- var
- Num_priority : integer;
- begin
- Num_Priority := ThreadGetPriority(Hnd);
- case Num_Priority of
- 0: Result := 'Normal';
- 1: Result := 'Above Normal';
- 2: Result := 'Highest';
- 15: Result := 'Time Critical';
- -1: Result := 'Below Normal';
- -2: Result := 'Lowest';
- else
- Result := 'Not known';
- end;
- end;
- function TParallel_Lib.Get_Number_of_CPUs(): Integer;
- begin
- Result := GetLogicalCpuCount();
- end;
-{$ENDIF}
-end.
diff --git a/src/Parser/ParserDel.pas b/src/Parser/ParserDel.pas
index 9987ffe79..4ad2a92e3 100644
--- a/src/Parser/ParserDel.pas
+++ b/src/Parser/ParserDel.pas
@@ -1,26 +1,15 @@
unit ParserDel;
-
{
----------------------------------------------------------
Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
All rights reserved.
----------------------------------------------------------
}
-{
- Command Line Parser Class
-
- This Version is a Simple version for embedding in Delphi Programs.
-
- 3/20/01 Added Quote char properties and strings
-}
-
-{$M+}
-
interface
uses
Arraydef,
- classes,{controls,}
+ classes,
CmdForms,
Sysutils,
RPN,
@@ -29,7 +18,7 @@ interface
type
EParserProblem = class(Exception);
- {Class for keeping a list of variablel and associate values}
+ // Class for keeping a list of variablel and associate values
TParserVar = class(Tobject)
PRIVATE
ActiveVariable: Cardinal;
@@ -42,7 +31,6 @@ TParserVar = class(Tobject)
procedure set_value(const Value: String);
function Get_VarString(Idx: Cardinal): String;
PUBLIC
-
NumVariables: Cardinal;
constructor Create(InitSize: Cardinal);
@@ -55,7 +43,7 @@ TParserVar = class(Tobject)
end;
- TParser = class(TObject)
+ TDSSParser = class(TObject)
PRIVATE
ParserVars: TParserVar; // reference to global parser vars
CmdBuffer: String;
@@ -84,8 +72,6 @@ TParser = class(TObject)
function IsCommentChar(const LineBuffer: String; var LinePos: Integer): Boolean;
function GetToken(const LineBuffer: String; var LinePos: Integer): String;
function InterpretRPNString(var Code: Integer): Double;
- PROTECTED
-
PUBLIC
constructor Create;
destructor Destroy; OVERRIDE;
@@ -95,17 +81,19 @@ TParser = class(TObject)
property Token: String READ TokenBuffer WRITE TokenBuffer;
property Remainder: String READ Get_Remainder;
property NextParam: String READ GetNextParam;
- function ParseAsBusName(var NumNodes: Integer; NodeArray: pIntegerArray): String;
+ function ParseAsBusName(Param: String; var NumNodes: Integer; NodeArray: pIntegerArray): String;//TODO: make it a separate function
function ParseAsVector(ExpectedSize: Integer; VectorBuffer: pDoubleArray): Integer;
function ParseAsVector(VectorBuffer: Array of Double): Integer;
+
+ // TODO: remove, not used in the main code, only in the COM API
function ParseAsMatrix(ExpectedOrder: Integer; MatrixBuffer: pDoubleArray): Integer;
- function ParseAsMatrix(MatrixBuffer: Array of Double): Integer;
- function ParseAsSymMatrix(ExpectedOrder: Integer; MatrixBuffer: pDoubleArray): Integer;
- function ParseAsSymMatrix(MatrixBuffer: Array of Double): Integer;
+
+ function ParseAsSymMatrix(ExpectedOrder: Integer; MatrixBuffer: pDoubleArray; Stride: Integer = 1; Scale: Double = 1): Integer;
+ function ParseAsSymMatrix(MatrixBuffer: Array of Double; Stride: Integer = 1; Scale: Double = 1): Integer;
procedure ResetDelims; // resets delimiters to default
- procedure CheckforVar(var TokenBuffer: String);
+ procedure CheckforVar(var TokenBuffer_: String);
procedure SetVars(vars: TParserVar);
- PUBLISHED
+
property CmdString: String READ CmdBuffer WRITE SetCmdString;
property Position: Integer READ FPosition WRITE FPosition; // to save and restore
property Delimiters: String READ DelimChars WRITE DelimChars;
@@ -119,35 +107,29 @@ implementation
uses
DSSClass,
- DSSHelper
- ;
+ DSSHelper;
const
Commentchar = '!';
VariableDelimiter = '@'; // first character of a variable
-{=======================================================================================================================}
function ProcessRPNCommand(const TokenBuffer: String; RPN: TRPNCalc): Integer;
-
var
S: String;
Number: Double;
-
begin
Result := 0; // Error Code on conversion error
-
-
- {First Try to make a valid number. If that fails, check for RPN command}
+
+ // First Try to make a valid number. If that fails, check for RPN command
Val(TokenBuffer, Number, Result);
if Result = 0 then
RPN.X := Number // Enters number in X register
-
else
- begin {Check for RPN command. }
+ begin // Check for RPN command.
Result := 0; // reset error return
- S := LowerCase(TokenBuffer);
+ S := AnsiLowerCase(TokenBuffer);
with RPN do
if CompareStr(S, '+') = 0 then
Add
@@ -220,57 +202,44 @@ function ProcessRPNCommand(const TokenBuffer: String; RPN: TRPNCalc): Integer;
Result := 1; // error
end;
end;
-
end;
-{=======================================================================================================================}
-
function StriptoDotPos(Dotpos: Integer; var S: String): String;
-
-{Strips off everything up to a period.}
-
+// Strips off everything up to a period.
begin
-
if dotpos = 0 then
Result := S;
Result := Copy(S, 1, dotpos - 1);
end;
-{=======================================================================================================================}
-
-procedure TParser.CheckforVar(var TokenBuffer: String);
+procedure TDSSParser.CheckforVar(var TokenBuffer_: String);
var
VariableValue,
VariableName: String;
DotPos,
CaratPos: Integer;
- {-------------------------------------}
procedure ReplaceToDotPos(const S: String);
begin
if DotPos > 0 then
- TokenBuffer := S + Copy(TokenBuffer, Dotpos, Length(TokenBuffer) - DotPos + 1)
+ TokenBuffer_ := S + Copy(TokenBuffer_, Dotpos, Length(TokenBuffer_) - DotPos + 1)
else
- TokenBuffer := S;
+ TokenBuffer_ := S;
end;
-
- {-------------------------------------}
-
begin
-
- {Replace TokenBuffer with Variable value if first character is VariableDelimiter character}
- if Length(TokenBuffer) > 1 then
- if TokenBuffer[1] = VariableDelimiter then // looking for '@'
+ // Replace TokenBuffer_ with Variable value if first character is VariableDelimiter character
+ if Length(TokenBuffer_) > 1 then
+ if TokenBuffer_[1] = VariableDelimiter then // looking for '@'
begin
- Dotpos := pos('.', TokenBuffer);
- CaratPos := pos('^', TokenBuffer);
+ Dotpos := pos('.', TokenBuffer_);
+ CaratPos := pos('^', TokenBuffer_);
if CaratPos > 0 then
DotPos := CaratPos; // Carat takes precedence
if Dotpos > 0 then
- VariableName := StripToDotPos(DotPos, TokenBuffer)
+ VariableName := StripToDotPos(DotPos, TokenBuffer_)
else
- VariableName := TokenBuffer;
+ VariableName := TokenBuffer_;
if ParserVars.Lookup(VariableName) > 0 then
begin
@@ -286,9 +255,7 @@ procedure TParser.CheckforVar(var TokenBuffer: String);
end;
end;
-{=======================================================================================================================}
-
-constructor TParser.Create;
+constructor TDSSParser.Create;
begin
inherited Create;
@@ -301,31 +268,23 @@ constructor TParser.Create;
MatrixRowTerminator := '|';
FAutoIncrement := FALSE;
RPNCalculator := TRPNCalc.Create;
-
-
end;
-{=======================================================================================================================}
-
-destructor TParser.Destroy;
+destructor TDSSParser.Destroy;
begin
RPNCalculator.Free;
inherited Destroy;
end;
-{=======================================================================================================================}
-
-procedure TParser.SetCmdString(const Value: String);
+procedure TDSSParser.SetCmdString(const Value: String);
begin
CmdBuffer := Value + ' '; // add some white space at end to get last param
FPosition := 1;
SkipWhiteSpace(CmdBuffer, FPosition); // position at first non whitespace character
end;
-{=======================================================================================================================}
-
-procedure TParser.ResetDelims;
+procedure TDSSParser.ResetDelims;
begin
DelimChars := ',=';
WhiteSpaceChars := ' ' + #9;
@@ -334,9 +293,7 @@ procedure TParser.ResetDelims;
FEndQuoteChars := ')"'']}';
end;
-{=======================================================================================================================}
-
-function TParser.IsWhiteSpace(ch: Char): Boolean;
+function TDSSParser.IsWhiteSpace(ch: Char): Boolean;
var
i: Integer;
begin
@@ -351,15 +308,11 @@ function TParser.IsWhiteSpace(ch: Char): Boolean;
end;
end;
-
-{=======================================================================================================================}
-
-function TParser.IsDelimiter(const LineBuffer: String; var LinePos: Integer): Boolean;
+function TDSSParser.IsDelimiter(const LineBuffer: String; var LinePos: Integer): Boolean;
var
i: Integer;
ch: Char;
begin
-
Result := FALSE;
if IsCommentChar(LineBuffer, LinePos) then
@@ -390,13 +343,9 @@ function TParser.IsDelimiter(const LineBuffer: String; var LinePos: Integer): Bo
Exit;
end;
end;
-
end;
-
-{=======================================================================================================================}
-
-function TParser.IsDelimChar(ch: Char): Boolean;
+function TDSSParser.IsDelimChar(ch: Char): Boolean;
var
i: Integer;
begin
@@ -411,25 +360,19 @@ function TParser.IsDelimChar(ch: Char): Boolean;
end;
end;
-{=======================================================================================================================}
-
-procedure TParser.SkipWhiteSpace(const LineBuffer: String; var LinePos: Integer);
+procedure TDSSParser.SkipWhiteSpace(const LineBuffer: String; var LinePos: Integer);
begin
while (LinePos < Length(LineBuffer)) and
IsWhiteSpace(LineBuffer[LinePos]) do
Inc(LinePos);
end;
-{=======================================================================================================================}
-
-function TParser.GetToken(const LineBuffer: String; var LinePos: Integer): String;
+function TDSSParser.GetToken(const LineBuffer: String; var LinePos: Integer): String;
var
TokenStart: Integer;
CmdBufLength: Integer;
QuoteIndex: Integer; // value of quote character found
-
- {---------------- Local Function -----------------------}
procedure ParseToEndChar(Endchar: Char);
begin
Inc(LinePos);
@@ -442,14 +385,12 @@ function TParser.GetToken(const LineBuffer: String; var LinePos: Integer): Strin
Inc(LinePos); // Increment past endchar
end;
- {---------------- Local Function -----------------------}
procedure ParseToEndQuote;
begin
ParseToEndChar(FEndQuoteChars[QuoteIndex]);
IsQuotedString := TRUE;
end;
- {---------------- Local Function -----------------------}
function IsBeginQuote(ch: Char): Boolean;
begin
QuoteIndex := Pos(ch, FBeginQuoteChars);
@@ -464,12 +405,11 @@ function TParser.GetToken(const LineBuffer: String; var LinePos: Integer): Strin
CmdBufLength := Length(LineBuffer);
if LinePos <= CmdBufLength then
begin
-
- {Handle Quotes and Parentheses around tokens}
+ // Handle Quotes and Parentheses around tokens
IsQuotedString := FALSE;
if IsBeginQuote(LineBuffer[LinePos]) then
ParseToEndQuote
- else { Copy to next delimiter or whitespace}
+ else // Copy to next delimiter or whitespace
begin
TokenStart := LinePos;
while (LinePos < CmdBufLength) and not IsDelimiter(LineBuffer, LinePos) do
@@ -478,16 +418,14 @@ function TParser.GetToken(const LineBuffer: String; var LinePos: Integer): Strin
Result := Copy(LineBuffer, TokenStart, (LinePos - TokenStart));
end;
+ // Check for stop on comment
- { Check for stop on comment }
-
- // if stop on comment, ignore rest of line.
+ // if stop on comment, ignore rest of line.
if LastDelimiter = CommentChar then
LinePos := Length(LineBuffer) + 1
else
begin
-
- {Get Rid of Trailing White Space}
+ // Get Rid of Trailing White Space
if LastDelimiter = ' ' then
SkipWhiteSpace(LineBuffer, LinePos);
if IsDelimchar(LineBuffer[LinePos]) then
@@ -500,13 +438,8 @@ function TParser.GetToken(const LineBuffer: String; var LinePos: Integer): Strin
end;
end;
-
-{=======================================================================================================================}
-
-function TParser.GetNextParam: String;
-
+function TDSSParser.GetNextParam: String;
begin
-
if FPosition <= Length(CmdBuffer) then
begin
LastDelimiter := ' ';
@@ -530,21 +463,16 @@ function TParser.GetNextParam: String;
CheckForVar(TokenBuffer);
Result := ParameterBuffer;
-
end;
-{=======================================================================================================================}
-
-function TParser.ParseAsBusName(var NumNodes: Integer; NodeArray: pIntegerArray): String;
-
-{ Looking for "BusName.1.2.3" in the TokenBuffer
- Assumes NodeArray is big enough to hold the numbers}
-
+function TDSSParser.ParseAsBusName(Param: String; var NumNodes: Integer; NodeArray: pIntegerArray): String;
+// Looking for "BusName.1.2.3" in the TokenBuffer
+// Assumes NodeArray is big enough to hold the numbers
var
DotPos, NodeBufferPos: Integer;
NodeBuffer, DelimSave, TokenSave: String;
-
begin
+ TokenBuffer := Param;
if FAutoIncrement then
GetNextParam;
NumNodes := 0;
@@ -555,7 +483,7 @@ function TParser.ParseAsBusName(var NumNodes: Integer; NodeArray: pIntegerArray)
begin
Result := Trim(Copy(TokenBuffer, 1, DotPos - 1)); // Bus Name
TokenSave := TokenBuffer;
- {now Get nodes}
+ // now Get nodes
NodeBuffer := Copy(tokenBuffer, DotPos + 1, Length(tokenBuffer) - DotPos) + ' ';
NodeBufferPos := 1;
@@ -566,9 +494,9 @@ function TParser.ParseAsBusName(var NumNodes: Integer; NodeArray: pIntegerArray)
while Length(TokenBuffer) > 0 do
begin
inc(NumNodes);
- NodeArray^[NumNodes] := MakeInteger;
+ NodeArray[NumNodes] := MakeInteger;
if ConvertError then
- NodeArray^[NumNodes] := -1; // Indicate an error
+ NodeArray[NumNodes] := -1; // Indicate an error
TokenBuffer := GetToken(NodeBuffer, NodeBufferPos);
end;
except
@@ -579,22 +507,18 @@ function TParser.ParseAsBusName(var NumNodes: Integer; NodeArray: pIntegerArray)
DelimChars := DelimSave; //restore to original delimiters
TokenBuffer := TokenSave;
end;
-
end;
-{=======================================================================================================================}
-function TParser.ParseAsVector(VectorBuffer: Array of Double): Integer;
+function TDSSParser.ParseAsVector(VectorBuffer: Array of Double): Integer;
begin
Result := ParseAsVector(Length(VectorBuffer), pDoubleArray(@VectorBuffer[0]));
end;
-function TParser.ParseAsVector(ExpectedSize: Integer; VectorBuffer: pDoubleArray): Integer;
+function TDSSParser.ParseAsVector(ExpectedSize: Integer; VectorBuffer: pDoubleArray): Integer;
var
ParseBufferPos, NumElements, i: Integer;
ParseBuffer, DelimSave: String;
-
begin
-
if FAutoIncrement then
GetNextParam;
@@ -604,7 +528,7 @@ function TParser.ParseAsVector(ExpectedSize: Integer; VectorBuffer: pDoubleArray
for i := 1 to ExpectedSize do
VectorBuffer^[i] := 0.0;
- {now Get Vector values}
+ // now Get Vector values
ParseBuffer := TokenBuffer + ' ';
ParseBufferPos := 1;
@@ -632,26 +556,15 @@ function TParser.ParseAsVector(ExpectedSize: Integer; VectorBuffer: pDoubleArray
DSSMessageDlg('Vector Buffer in ParseAsVector Probably Too Small: ' + E.Message, TRUE);
end;
-
DelimChars := DelimSave; //restore to original delimiters
TokenBuffer := copy(ParseBuffer, ParseBufferPos, Length(ParseBuffer)); // prepare for next trip
-
end;
-{=======================================================================================================================}
-function TParser.ParseAsMatrix(MatrixBuffer: Array of Double): Integer;
-begin
- Result := ParseAsMatrix(Length(MatrixBuffer), pDoubleArray(@MatrixBuffer[0]));
-end;
-
-function TParser.ParseAsMatrix(ExpectedOrder: Integer; MatrixBuffer: pDoubleArray): Integer;
-
+function TDSSParser.ParseAsMatrix(ExpectedOrder: Integer; MatrixBuffer: pDoubleArray): Integer;
var
i, j, k, ElementsFound: Integer;
RowBuf: pDoubleArray;
-
begin
-
if FAutoIncrement then
GetNextParam;
@@ -665,10 +578,9 @@ function TParser.ParseAsMatrix(ExpectedOrder: Integer; MatrixBuffer: pDoubleArra
for i := 1 to ExpectedOrder do
begin
-
ElementsFound := ParseAsVector(ExpectedOrder, RowBuf);
- { Returns matrix in Column Order (Fortran order) }
+ // Returns matrix in Column Order (Fortran order)
k := i;
for j := 1 to ElementsFound do
begin
@@ -688,28 +600,17 @@ function TParser.ParseAsMatrix(ExpectedOrder: Integer; MatrixBuffer: pDoubleArra
result := ExpectedOrder;
end;
-{=======================================================================================================================}
-function TParser.ParseAsSymMatrix(MatrixBuffer: Array of Double): Integer;
+function TDSSParser.ParseAsSymMatrix(MatrixBuffer: Array of Double; Stride: Integer; Scale: Double): Integer;
begin
- Result := ParseAsSymMatrix(Length(MatrixBuffer), pDoubleArray(@MatrixBuffer[0]));
+ Result := ParseAsSymMatrix(Length(MatrixBuffer), pDoubleArray(@MatrixBuffer[0]), Stride);
end;
-function TParser.ParseAsSymMatrix(ExpectedOrder: Integer; MatrixBuffer: pDoubleArray): Integer;
-
+function TDSSParser.ParseAsSymMatrix(ExpectedOrder: Integer; MatrixBuffer: pDoubleArray; Stride: Integer; Scale: Double): Integer;
var
- i, j,
+ i, j, pos,
ElementsFound: Integer;
RowBuf: pDoubleArray;
-
- {---------------- Local Function -----------------------}
- function ElementIndex(ii, jj: Integer): Integer;
- begin
- Result := (jj - 1) * ExpectedOrder + ii;
- end;
-
begin
-
-
if FAutoIncrement then
GetNextParam;
@@ -718,22 +619,25 @@ function TParser.ParseAsSymMatrix(ExpectedOrder: Integer; MatrixBuffer: pDoubleA
try
RowBuf := Allocmem(Sizeof(Double) * ExpectedOrder);
- for i := 1 to (ExpectedOrder * ExpectedOrder) do
- MatrixBuffer^[i] := 0.0;
+ for i := 0 to (ExpectedOrder * ExpectedOrder) - 1 do
+ MatrixBuffer[i * Stride + 1] := 0.0;
- for i := 1 to ExpectedOrder do
+ for i := 0 to (ExpectedOrder - 1) do
begin
-
ElementsFound := ParseAsVector(ExpectedOrder, RowBuf);
- { Returns matrix in Column Order (Fortran order) }
- for j := 1 to ElementsFound do
+ for j := 0 to (ElementsFound - 1) do
begin
- MatrixBuffer^[ElementIndex(i, j)] := RowBuf^[j];
- if i <> j then
- MatrixBuffer^[ElementIndex(j, i)] := RowBuf^[j];
- end;
+ pos := (j * ExpectedOrder + i) * Stride + 1;
+
+ MatrixBuffer^[pos] := RowBuf^[j + 1] * Scale;
+
+ if i = j then
+ continue;
+ pos := (i * ExpectedOrder + j) * Stride + 1;
+ MatrixBuffer^[pos] := RowBuf^[j + 1] * Scale;
+ end;
end;
except
@@ -744,13 +648,9 @@ function TParser.ParseAsSymMatrix(ExpectedOrder: Integer; MatrixBuffer: pDoubleA
if Assigned(RowBuf) then
FreeMem(RowBuf, (Sizeof(Double) * ExpectedOrder));
Result := ExpectedOrder;
-
end;
-
-{=======================================================================================================================}
-
-function TParser.MakeString: String;
+function TDSSParser.MakeString: String;
begin
if FAutoIncrement then
GetNextParam;
@@ -758,9 +658,7 @@ function TParser.MakeString: String;
Result := TokenBuffer;
end;
-{=======================================================================================================================}
-
-function TParser.MakeInteger: Integer;
+function TDSSParser.MakeInteger: Integer;
// Hex integers must be preceeded by "$"
var
Code: Integer;
@@ -792,18 +690,15 @@ function TParser.MakeInteger: Integer;
begin
// not needed with Raise ... Result := 0;
ConvertError := TRUE;
- raise EParserProblem.Create('Integer number conversion error for string: "' + TokenBuffer + '"');
+ raise EParserProblem.Create(Format('Integer number conversion error for string: "%s"', [TokenBuffer]));
end
else
Result := Round(Temp);
- ;
end;
end;
end;
-{=======================================================================================================================}
-
-function TParser.MakeDouble: Double;
+function TDSSParser.MakeDouble: Double;
var
Code: Integer;
begin
@@ -826,22 +721,15 @@ function TParser.MakeDouble: Double;
raise EParserProblem.Create('Floating point number conversion error for string: "' + TokenBuffer + '"');
end;
end;
-
end;
-{=======================================================================================================================}
-
-function TParser.Get_Remainder: String;
+function TDSSParser.Get_Remainder: String;
begin
Result := Copy(CmdBuffer, FPosition, Length(CmdBuffer) - FPosition + 1)
end;
-{=======================================================================================================================}
-
-function TParser.IsCommentChar(const LineBuffer: String; var LinePos: Integer): Boolean;
-
-{Checks for CommentChar and '//'}
-
+function TDSSParser.IsCommentChar(const LineBuffer: String; var LinePos: Integer): Boolean;
+// Checks for CommentChar and '//'
begin
case LineBuffer[LinePos] of
CommentChar:
@@ -856,19 +744,13 @@ function TParser.IsCommentChar(const LineBuffer: String; var LinePos: Integer):
else
Result := FALSE;
end;
-
-
end;
-{=======================================================================================================================}
-
-function TParser.InterpretRPNString(var Code: Integer): Double;
+function TDSSParser.InterpretRPNString(var Code: Integer): Double;
var
ParseBufferPos: Integer;
ParseBuffer: String;
-
begin
-
Code := 0;
ParseBuffer := TokenBuffer + ' ';
ParseBufferPos := 1;
@@ -879,7 +761,6 @@ function TParser.InterpretRPNString(var Code: Integer): Double;
while Length(TokenBuffer) > 0 do
begin
-
Code := ProcessRPNCommand(TokenBuffer, RPNCalculator);
if Code > 0 then
Break; // Stop on any floating point error
@@ -891,13 +772,8 @@ function TParser.InterpretRPNString(var Code: Integer): Double;
Result := RPNCalculator.X;
TokenBuffer := copy(ParseBuffer, ParseBufferPos, Length(ParseBuffer)); // prepare for next trip
-
end;
-{===================================== Variable Support =============================================================}
-
-{ TParserVar }
-
procedure ReallocStr(var S: pStringArray; oldSize, NewSize: Integer);
// Make a bigger block to hold the pointers to the strings
var
@@ -912,8 +788,6 @@ procedure ReallocStr(var S: pStringArray; oldSize, NewSize: Integer);
S := X;
end;
-{=======================================================================}
-
function TParserVar.Add(const VarName, VarValue: String): Integer;
var
idx: Cardinal;
@@ -925,7 +799,6 @@ function TParserVar.Add(const VarName, VarValue: String): Integer;
end;
begin
-
// First, check to see if the varname already exists
// if so, just change the value
idx := Varnames.Find(Varname);
@@ -941,8 +814,7 @@ function TParserVar.Add(const VarName, VarValue: String): Integer;
end;
end;
- {If a variable used in the definition of a variable, enclose in quotes.}
-
+ // If a variable used in the definition of a variable, enclose in quotes.
if pos('@', VarValue) > 0 then
VarDefinition := EncloseQuotes(VarValue)
else
@@ -951,14 +823,10 @@ function TParserVar.Add(const VarName, VarValue: String): Integer;
VarValues^[idx] := VarDefinition;
NumVariables := VarNames.Count;
Result := idx;
-
end;
-{=======================================================================}
-
constructor TParserVar.Create(InitSize: Cardinal);
begin
-
VarNames := THashList.Create(InitSize);
VarValues := AllocStringArray(InitSize);
StringArraySize := InitSize;
@@ -981,11 +849,8 @@ constructor TParserVar.Create(InitSize: Cardinal);
VarValues^[ActiveVariable] := 'null'; // null value
NumVariables := Varnames.Count;
-
end;
-{=======================================================================}
-
destructor TParserVar.Destroy;
begin
VarNames.Free;
@@ -994,8 +859,6 @@ destructor TParserVar.Destroy;
inherited;
end;
-{=======================================================================}
-
function TParserVar.get_value: String;
begin
if ActiveVariable > 0 then
@@ -1030,10 +893,9 @@ procedure TParserVar.set_value(const Value: String);
begin
if (ActiveVariable > 0) and (ActiveVariable <= NumVariables) then
VarValues^[ActiveVariable] := Value;
-
end;
-procedure TParser.SetVars(vars: TParserVar);
+procedure TDSSParser.SetVars(vars: TParserVar);
begin
ParserVars := vars;
end;
diff --git a/src/Parser/RPN.pas b/src/Parser/RPN.pas
index 683bb1129..4f67095bd 100644
--- a/src/Parser/RPN.pas
+++ b/src/Parser/RPN.pas
@@ -7,9 +7,7 @@
----------------------------------------------------------
}
-{RPN Calculator}
-
-{$M+}
+// RPN Calculator
interface
@@ -61,8 +59,6 @@ TRPNCalc = class(TObject)
constructor Create;
destructor Destroy; OVERRIDE;
- PUBLISHED
-
end;
@@ -71,10 +67,9 @@ implementation
uses
Math;
-
-{ TRPNCalc }
-var
- DegToRad, RadToDeg: Double;
+const
+ DegToRad: Double = 3.14159265359 / 180.0;
+ RadToDeg: Double = 180.0 / 3.14159265359;
procedure TRPNCalc.aCosdeg;
begin
@@ -120,7 +115,6 @@ constructor TRPNCalc.Create;
destructor TRPNCalc.Destroy;
begin
inherited;
-
end;
procedure TRPNCalc.Divide;
@@ -250,9 +244,4 @@ procedure TRPNCalc.Inv; // invert 1/X
FStack[1] := 1.0 / FStack[1];
end;
-initialization
-
- DegToRad := 3.14159265359 / 180.0;
- RadToDeg := 1.0 / DegToRad;
-
end.
diff --git a/src/Shared/Arraydef.pas b/src/Shared/Arraydef.pas
index 5a93b73ce..beb758db4 100644
--- a/src/Shared/Arraydef.pas
+++ b/src/Shared/Arraydef.pas
@@ -10,13 +10,31 @@
interface
uses
- CAPI_Types;
+ CAPI_Types,
+ UComplex;
type
-{ Define arrays with dummy dimension of 100 so we can hard code
- constants for accessing small order elements; Otherwise, always
- allocate arrays of these types before using}
+{$PUSH}
+{$SCOPEDENUMS ON}
+{$Z4} // keep enums as int32 values
+ TGeneralConnection = (
+ Wye = 0, // wye, star, line-neutral connection
+ Y = 0, // wye, star, line-neutral connection
+ LN = 0, // wye, star, line-neutral connection
+ Delta = 1, // delta, line-line connection
+ LL = 1 // delta, line-line connection
+ );
+{$SCOPEDENUMS OFF}
+{$POP}
+
+ TCBuffer24 = array[1..24] of Complex;
+ PCBuffer24 = ^TCBuffer24;
+
+
+// Define arrays with dummy dimension of 100 so we can hard code
+// constants for accessing small order elements; Otherwise, always
+// allocate arrays of these types before using
pSmallIntArray = ^SmallIntArray;
SmallIntArray = array[1..100] of Smallint;
pIntegerArray = ^LongIntArray;
@@ -31,19 +49,16 @@ interface
pStringArray = ^StringArray;
StringArray = array[1..100] of String;
+ pPtrIntArray = ^PtrIntArray;
+ PtrIntArray = array[1..100] of PtrInt;
+
PDoubleArray0 = CAPI_Types.PDoubleArray0;
PSingleArray0 = CAPI_Types.PSingleArray0;
ArrayOfString = Array of String;
- //pDouble = ^Double;
- //pSingle = ^Single;
- //pSmallInt = ^Smallint;
- //pLongInt = ^Longint;
-
function AllocStringArray(Size: Integer): pStringArray;
procedure FreeStringArray(var pS: pStringArray; Size: Integer);
-{--------------------------------------------------------------------------}
implementation
diff --git a/src/Shared/CktTree.pas b/src/Shared/CktTree.pas
index 3f363207e..f6532d9be 100644
--- a/src/Shared/CktTree.pas
+++ b/src/Shared/CktTree.pas
@@ -6,12 +6,6 @@
All rights reserved.
----------------------------------------------------------
}
-{ Change Log
-
- 8/12/99 Added level number to node
-}
-
-{$M+}
interface
@@ -23,29 +17,25 @@ interface
CktElement;
type
-
TAdjArray = array of TList;
-
TCktTreeNode = class(TObject)
PRIVATE
-
FChildBranches: TDSSPointerList; // List of CktTreeNode pointers
NumToBuses, ToBusPtr: Integer;
ToBusList: pIntegerArray;
- function Get_FirstChild: TCktTreeNode;
- function Get_NextChild: TCktTreeNode;
- function Get_Parent: TCktTreeNode;
- procedure Set_AddChild(const Value: TCktTreeNode);
- procedure Set_AddObject(Value: Pointer);
- function Get_NumChildren: Integer;
- function Get_NumObjects: Integer;
- function Get_ToBusReference: Integer;
- procedure Set_ToBusReference(const Value: Integer);
- function Get_FirstObject: Pointer;
- function Get_NextObject: Pointer;
+ function Get_FirstChild: TCktTreeNode; inline;
+ function Get_NextChild: TCktTreeNode; inline;
+ function Get_Parent: TCktTreeNode; inline;
+ procedure Set_AddChild(const Value: TCktTreeNode); inline;
+ function Get_NumChildren: Integer; inline;
+ function Get_NumObjects: Integer; inline;
+ function Get_ToBusReference: Integer; inline;
+ procedure Set_ToBusReference(const Value: Integer); inline;
+ function Get_FirstObject: Pointer; inline;
+ function Get_NextObject: Pointer; inline;
PROTECTED
ChildAdded: Boolean;
@@ -66,7 +56,7 @@ TCktTreeNode = class(TObject)
procedure ResetToBusList;
property AddChildBranch: TCktTreeNode WRITE Set_AddChild;
- property AddShuntObject: Pointer WRITE Set_AddObject;
+ procedure AddShuntObject(Value: Pointer);
property FirstChildBranch: TCktTreeNode READ Get_FirstChild;
property NextChildBranch: TCktTreeNode READ Get_NextChild;
property FirstShuntObject: Pointer READ Get_FirstObject;
@@ -76,9 +66,6 @@ TCktTreeNode = class(TObject)
property NumChildBranches: Integer READ Get_NumChildren; // Number of children at present node
property NumShuntObjects: Integer READ Get_NumObjects; // Number of objects at present node
property ToBusReference: Integer READ Get_ToBusReference WRITE Set_ToBusReference;
-
- PUBLISHED
-
end;
@@ -87,8 +74,6 @@ TZoneEndsList = class(Tobject)
EndNodeList: TDSSPointerList;
EndBuses: pIntegerArray;
- PROTECTED
-
PUBLIC
NumEnds: Integer;
@@ -97,13 +82,10 @@ TZoneEndsList = class(Tobject)
procedure Add(const Node: TCktTreeNode; EndBusRef: Integer);
function Get(i: Integer; var Node: TCktTreeNode): Integer;
- PUBLISHED
-
end;
TCktTree = class(TObject)
-
PRIVATE
FirstNode: TCktTreeNode;
@@ -117,13 +99,9 @@ TCktTree = class(TObject)
function Get_NextObject: Pointer;
function Get_Active: Pointer;
function Get_Level: Integer;
- procedure Set_New(Value: Pointer);
- procedure Set_NewObject(Value: Pointer);
procedure Set_Active(p: Pointer); // Set present node to this value
procedure PushAllChildren;
- PROTECTED
-
PUBLIC
PresentBranch: TCktTreeNode;
ZoneEndsList: TZoneEndsList;
@@ -134,10 +112,10 @@ TCktTree = class(TObject)
procedure StartHere; // Start Forward Search at the present location
// can also use active
procedure AddNewChild(Value: Pointer; BusRef, TerminalNo: Integer);
-
- property New: Pointer WRITE Set_New; // Adds Child and makes it present
+ procedure Add(Value: Pointer); // Adds Child and makes it present -- previously "New"
//Property NewChild :Pointer Write Set_NewChild; // Adds child to present, but doesn't change present
- property NewObject: Pointer WRITE Set_NewObject; // Adds a pointer to an object to be associated with the current node
+
+ procedure AddNewObject(Value: Pointer); // Adds a pointer to an object to be associated with the current node
property First: Pointer READ Get_First; // Returns pointer to first cktobject
property Parent: Pointer READ Get_Parent;
property FirstObject: Pointer READ Get_FirstObject;
@@ -146,9 +124,6 @@ TCktTree = class(TObject)
property GoBackward: Pointer READ Get_Backward;
property Active: Pointer READ Get_Active WRITE Set_Active;
property Level: Integer READ Get_Level; {Get lexical level of present node}
-
- PUBLISHED
-
end;
// build a tree of connected elements beginning at StartElement
@@ -186,7 +161,6 @@ constructor TcktTreeNode.Create(const pParent: TCktTreeNode; const pSelfobj: Poi
ToBusList := NIL;
ToBusPtr := 0;
ChildAdded := FALSE;
- // TEMc - initialize some topology variables, 10/2009
IsDangling := TRUE;
IsLoopedHere := FALSE;
IsParallel := FALSE;
@@ -214,13 +188,13 @@ destructor TcktTreeNode.Destroy;
procedure TcktTreeNode.Set_AddChild(const Value: TCktTreeNode);
begin
- FChildBranches.New := Value;
+ FChildBranches.Add(Value);
ChildAdded := TRUE;
end;
-procedure TcktTreeNode.Set_AddObject(Value: Pointer);
+procedure TcktTreeNode.AddShuntObject(Value: Pointer);
begin
- FShuntObjects.New := Value;
+ FShuntObjects.Add(Value);
end;
function TcktTreeNode.Get_FirstChild: TCktTreeNode;
@@ -238,15 +212,13 @@ function TcktTreeNode.Get_Parent: TCktTreeNode;
Result := FParentBranch;
end;
-
constructor TcktTree.Create;
begin
inherited create;
FirstNode := NIL;
PresentBranch := NIL;
ZoneEndsList := TZoneEndsList.Create;
- ForwardStack := Tpstack.Create(20);
-
+ ForwardStack := Tpstack.Create(200);
end;
destructor TcktTree.Destroy;
@@ -259,9 +231,7 @@ destructor TcktTree.Destroy;
inherited Destroy;
end;
-
-procedure TcktTree.Set_New(Value: Pointer);
-
+procedure TcktTree.Add(Value: Pointer);
begin
PresentBranch := TcktTreeNode.Create(PresentBranch, Value);
if FirstNode = NIL then
@@ -272,10 +242,9 @@ procedure TcktTree.AddNewChild(Value: Pointer; BusRef, TerminalNo: Integer);
var
TempNode: TCktTreeNode;
begin
-
if PresentBranch = NIL then
begin
- Set_New(Value);
+ Add(Value);
end
else
begin
@@ -288,14 +257,13 @@ procedure TcktTree.AddNewChild(Value: Pointer; BusRef, TerminalNo: Integer);
PresentBranch.AddChildBranch := TempNode;
end;
-
end;
-procedure TcktTree.Set_NewObject(Value: Pointer);
+procedure TcktTree.AddNewObject(Value: Pointer);
begin
if PresentBranch <> NIL then
begin
- PresentBranch.AddShuntObject := Value;
+ PresentBranch.AddShuntObject(Value);
end;
end;
@@ -318,14 +286,14 @@ procedure TcktTree.PushAllChildren;
function TcktTree.Get_Forward: Pointer;
begin
-// MoveForward from Present node
+ // MoveForward from Present node
-// If we have added children to the present node since we opened it push em on
+ // If we have added children to the present node since we opened it push em on
if PresentBranch <> NIL then
if PresentBranch.ChildAdded then
PushAllChildren;
- // If the forward stack is empty push stuff on it to get started
+ // If the forward stack is empty push stuff on it to get started
if ForwardStack.Size = 0 then
PushAllChildren;
@@ -338,21 +306,17 @@ function TcktTree.Get_Forward: Pointer;
end;
function TcktTree.Get_Backward: Pointer;
-
begin
-
-// Move Backwardfrom Present node and reset forward stack
+ // Move Backwardfrom Present node and reset forward stack
PresentBranch := PresentBranch.ParentBranch;
ForwardStack.Clear;
if PresentBranch <> NIL then
Result := PresentBranch.CktObject
else
Result := NIL;
-
end;
function TcktTree.Get_Parent: Pointer;
-
begin
if PresentBranch.FParentBranch <> NIL then
Result := PresentBranch.FParentBranch.CktObject
@@ -360,7 +324,6 @@ function TcktTree.Get_Parent: Pointer;
Result := NIL;
end;
-
function TcktTree.Get_First: Pointer;
begin
// go to beginning and reset forward stack
@@ -371,7 +334,6 @@ function TcktTree.Get_First: Pointer;
Result := PresentBranch.CktObject
else
Result := NIL;
-
end;
function TcktTree.Get_FirstObject: Pointer;
@@ -390,7 +352,6 @@ function TcktTree.Get_NextObject: Pointer;
Result := NIL;
end;
-
function TcktTree.Get_Active: Pointer;
begin
if PresentBranch <> NIL then
@@ -400,12 +361,9 @@ function TcktTree.Get_Active: Pointer;
end;
procedure TcktTree.Set_Active(p: Pointer);
-
var
Temp: Pointer;
-
begin
-
Temp := Get_First;
while Temp <> NIL do
begin
@@ -415,7 +373,6 @@ procedure TcktTree.Set_Active(p: Pointer);
end;
ForwardStack.Clear;
-
end;
procedure TcktTree.StartHere;
@@ -445,12 +402,11 @@ function TCktTreeNode.Get_NumObjects: Integer;
Result := FShuntObjects.Count;
end;
-{ TZoneEndsList }
procedure TZoneEndsList.Add(const Node: TCktTreeNode; EndBusRef: Integer);
begin
Inc(NumEnds);
- EndnodeList.New := Node;
+ EndnodeList.Add(Node);
Reallocmem(EndBuses, Sizeof(EndBuses) * NumEnds);
EndBuses^[NumEnds] := EndBusRef;
end;
@@ -464,25 +420,20 @@ constructor TZoneEndsList.Create;
destructor TZoneEndsList.Destroy;
begin
-
EndnodeList.Free;
Reallocmem(EndBuses, 0);
inherited;
-
end;
function TZoneEndsList.Get(i: Integer; var Node: TCktTreeNode): Integer;
begin
-
Node := EndnodeList.Get(i);
Result := EndBuses^[i];
-
end;
function TCktTreeNode.Get_ToBusReference: Integer;
{Sequentially access the To Bus list if more than one with each invocation of the property}
begin
-
if NumToBuses = 1 then
begin
Result := ToBusList^[1]; // Always return the first
@@ -540,26 +491,26 @@ procedure GetSourcesConnectedToBus(Ckt: TDSSCircuit; BusNum: Integer; BranchList
begin
if psrc.Enabled then
begin
- if Analyze or (not psrc.Checked) then
+ if Analyze or (not (Flg.Checked in psrc.Flags)) then
begin
if (psrc.Terminals[0].BusRef = BusNum) then
begin // ?Connected to this bus ?
if Analyze then
begin
- psrc.IsIsolated := FALSE;
+ Exclude(psrc.Flags, Flg.IsIsolated);
BranchList.PresentBranch.IsDangling := FALSE;
end;
- if not psrc.checked then
+ if not (Flg.Checked in psrc.Flags) then
begin
- BranchList.NewObject := psrc;
- psrc.Checked := TRUE;
+ BranchList.AddNewObject(psrc);
+ Include(psrc.Flags, Flg.Checked);
end;
end;
end;
end;
psrc := Sources.Next;
end;
- end;{With}
+ end;
end;
procedure GetPCElementsConnectedToBus(adjLst: TList; BranchList: TCktTree; Analyze: Boolean);
@@ -574,13 +525,13 @@ procedure GetPCElementsConnectedToBus(adjLst: TList; BranchList: TCktTree; Analy
begin
if Analyze then
begin
- p.IsIsolated := FALSE;
+ Exclude(p.Flags, Flg.IsIsolated);
BranchList.PresentBranch.IsDangling := FALSE;
end;
- if not p.Checked then
+ if not (Flg.Checked in p.Flags) then
begin
- BranchList.NewObject := p;
- p.Checked := TRUE;
+ BranchList.AddNewObject(p);
+ Include(p.Flags, Flg.Checked);
end;
end;
end;
@@ -597,7 +548,7 @@ procedure FindAllChildBranches(adjLst: TList; BusNum: Integer; BranchList: TCktT
p := adjLst[i];
if p.Enabled and not (p = ActiveBranch) then
begin
- if Analyze or (not p.Checked) then
+ if Analyze or (not (Flg.Checked in p.Flags)) then
begin
if (not IsShuntElement(p)) and AllTerminalsClosed(p) then
begin
@@ -607,9 +558,9 @@ procedure FindAllChildBranches(adjLst: TList; BusNum: Integer; BranchList: TCktT
begin
if Analyze then
begin
- p.IsIsolated := FALSE;
+ Exclude(p.Flags, Flg.IsIsolated);
BranchList.PresentBranch.IsDangling := FALSE;
- if p.Checked and (BranchList.Level > 0) then
+ if (Flg.Checked in p.Flags) and (BranchList.Level > 0) then
begin
BranchList.PresentBranch.IsLoopedHere := TRUE;
BranchList.PresentBranch.LoopLineObj := p;
@@ -618,12 +569,12 @@ procedure FindAllChildBranches(adjLst: TList; BusNum: Integer; BranchList: TCktT
BranchList.PresentBranch.IsParallel := TRUE;
end;
end;
- if not p.Checked then
+ if not (Flg.Checked in p.Flags) then
begin
BranchList.AddNewChild(p, BusNum, j);
p.TerminalsChecked[j - 1] := TRUE;
- p.Checked := TRUE;
- Break; {For}
+ Include(p.Flags, Flg.Checked);
+ Break; // For
end;
end;
end;
@@ -645,13 +596,13 @@ procedure GetShuntPDElementsConnectedToBus(adjLst: TList; BranchList: TCktTree;
begin
if Analyze then
begin
- p.IsIsolated := FALSE;
+ Exclude(p.Flags, Flg.IsIsolated);
BranchList.PresentBranch.IsDangling := FALSE;
end;
- if not p.Checked then
+ if not (Flg.Checked in p.Flags) then
begin
- BranchList.NewObject := p;
- p.Checked := TRUE;
+ BranchList.AddNewObject(p);
+ Include(p.Flags, Flg.Checked);
end;
end;
end;
@@ -674,17 +625,17 @@ function GetIsolatedSubArea(Circuit: TObject; StartElement: TDSSCktElement; Anal
BranchList := TCktTree.Create;
TestElement := StartElement;
- BranchList.New := TestElement;
+ BranchList.Add(TestElement);
if Analyze then
- TestElement.IsIsolated := FALSE;
- TestElement.LastTerminalChecked := 0; // We'll check things connected to both sides
+ Exclude(TestElement.Flags, Flg.IsIsolated);
+ // TestElement.LastTerminalChecked := 0; // We'll check things connected to both sides
- // Check off this element so we don't use it again
- TestElement.Checked := TRUE;
+ // Check off this element so we don't use it again
+ Include(TestElement.Flags, Flg.Checked);
- // Now start looking for other branches
- // Finds any branch connected to the TestBranch and adds it to the list
- // Goes until end of circuit, another energy meter, an open terminal, or disabled device.
+ // Now start looking for other branches
+ // Finds any branch connected to the TestBranch and adds it to the list
+ // Goes until end of circuit, another energy meter, an open terminal, or disabled device.
TestBranch := TestElement;
while TestBranch <> NIL do
begin
@@ -692,8 +643,8 @@ function GetIsolatedSubArea(Circuit: TObject; StartElement: TDSSCktElement; Anal
begin
if not TestBranch.TerminalsChecked[iTerm - 1] then
begin
- // Now find all pc Elements connected to the bus on this end of branch
- // attach them as generic objects to cktTree node.
+ // Now find all pc Elements connected to the bus on this end of branch
+ // attach them as generic objects to cktTree node.
TestBusNum := TestBranch.Terminals[iTerm - 1].BusRef;
BranchList.PresentBranch.ToBusReference := TestBusNum; // Add this as a "to" bus reference
if TestBusNum > 0 then
@@ -705,9 +656,9 @@ function GetIsolatedSubArea(Circuit: TObject; StartElement: TDSSCktElement; Anal
FindAllChildBranches(lstPD[TestBusNum], TestBusNum, BranchList, Analyze, TestBranch);
end;
end;
- end; {FOR iTerm}
+ end;
TestBranch := BranchList.GoForward;
- end; {WHILE}
+ end;
Result := BranchList;
end;
@@ -719,7 +670,7 @@ procedure BuildActiveBusAdjacencyLists(Circuit: TObject; var lstPD, lstPC: TAdjA
begin
Ckt := TDSSCircuit(Circuit);
nBus := Ckt.NumBuses;
- // Circuit.Buses is effectively 1-based; bus 0 is ground
+ // Circuit.Buses is effectively 1-based; bus 0 is ground
SetLength(lstPD, nBus + 1);
SetLength(lstPC, nBus + 1);
for i := 0 to nBus do
@@ -740,7 +691,7 @@ procedure BuildActiveBusAdjacencyLists(Circuit: TObject; var lstPD, lstPC: TAdjA
end;
pCktElement := Ckt.PDElements.First;
- {Put only eligible PDElements in the list}
+ // Put only eligible PDElements in the list
while pCktElement <> NIL do
begin
if pCktElement.Enabled then
diff --git a/src/Shared/Command.pas b/src/Shared/Command.pas
index da8fdca99..091a3c58c 100644
--- a/src/Shared/Command.pas
+++ b/src/Shared/Command.pas
@@ -7,9 +7,6 @@
----------------------------------------------------------
}
-
-{$M+}
-
interface
uses
@@ -21,24 +18,20 @@ TCommandList = class(TObject)
CommandList: TCommandHashListType;
AbbrevAllowed: Boolean;
function Get_NumCommands: Integer;
- PROTECTED
-
PUBLIC
- constructor Create(Commands: array of String);
+ constructor Create(Commands: array of String; AllowAbbrev: Boolean = True);
destructor Destroy; OVERRIDE;
procedure AddCommand(const cmd: String);
function Getcommand(const Cmd: String): Integer;
function Get(i: Integer): String;
property Abbrev: Boolean READ AbbrevAllowed WRITE AbbrevAllowed;
property NumCommands: Integer READ Get_NumCommands;
- PUBLISHED
-
end;
implementation
-constructor TCommandList.Create(Commands: array of String);
+constructor TCommandList.Create(Commands: array of String; AllowAbbrev: Boolean = True);
var
i: Integer;
begin
@@ -51,7 +44,7 @@ constructor TCommandList.Create(Commands: array of String);
CommandList.Add(Commands[i]);
end;
- AbbrevAllowed := TRUE;
+ AbbrevAllowed := AllowAbbrev;
end;
destructor TCommandList.Destroy;
@@ -69,14 +62,12 @@ procedure TCommandList.AddCommand(const cmd: String);
function TCommandList.Getcommand(const Cmd: String): Integer;
begin
-
Result := CommandList.Find(Cmd);
-{If no match found on whole command, check for abbrev}
-{This routine will generally be faster if one enters the whole command}
+ // If no match found on whole command, check for abbrev
+ // This routine will generally be faster if one enters the whole command
if Result = 0 then
if AbbrevAllowed then
Result := CommandList.FindAbbrev(Cmd);
-
end;
diff --git a/src/Shared/DSSPointerList.pas b/src/Shared/DSSPointerList.pas
index ec41315e7..186f5c3de 100644
--- a/src/Shared/DSSPointerList.pas
+++ b/src/Shared/DSSPointerList.pas
@@ -1,6 +1,5 @@
unit DSSPointerList;
-{$M+}
{
----------------------------------------------------------
Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
@@ -26,8 +25,6 @@ TDSSPointerList = class(TObject)
function Get_First: Pointer;
function Get_Next: Pointer;
function Get_Active: Pointer;
- procedure Set_New(value: Pointer);
-
PUBLIC
constructor Create(Size: Integer);
destructor Destroy; OVERRIDE;
@@ -37,43 +34,36 @@ TDSSPointerList = class(TObject)
function Add(p: Pointer): Integer; // Returns index of item
function Get(i: Integer): Pointer; // Changes active item
function At(i: Integer): Pointer; // Does not change the active item
-
property First: Pointer READ Get_First;
property Next: Pointer READ Get_Next;
property Count: Integer READ NumInList;
- property New: Pointer WRITE Set_New;
property Active: Pointer READ Get_Active;
property ActiveIndex: Integer READ ActiveItem;
- PUBLISHED
-
+ property InternalPointer: pPointerArray read List;
end;
-
implementation
constructor TDSSPointerList.Create(Size: Integer);
-//-------------------------------------------------------------------------
begin
inherited Create;
MaxAllocated := Size;
if MaxAllocated <= 0 then
MaxAllocated := 10; // Default Size & Increment
- List := AllocMem(SizeOf(List^[1]) * MaxAllocated);
+ List := AllocMem(SizeOf(Pointer) * MaxAllocated);
NumInList := 0;
ActiveItem := 0;
IncrementSize := MaxAllocated; // Increment is equal to original allocation
end;
-//-------------------------------------------------------------------------
destructor TDSSPointerList.Destroy;
begin
- Freemem(List, Sizeof(List^[1]) * MaxAllocated);
+ Freemem(List, Sizeof(Pointer) * MaxAllocated);
inherited Destroy;
end;
-//-------------------------------------------------------------------------
function TDSSPointerList.Add(p: Pointer): Integer;
begin
Inc(NumInList);
@@ -87,13 +77,6 @@ function TDSSPointerList.Add(p: Pointer): Integer;
ActiveItem := Result;
end;
-//-------------------------------------------------------------------------
-procedure TDSSPointerList.Set_New(value: Pointer);
-begin
- Add(Value);
-end;
-
-//-------------------------------------------------------------------------
function TDSSPointerList.Get_Active: Pointer;
begin
if (ActiveItem > 0) and (ActiveItem <= NumInList) then
@@ -102,7 +85,6 @@ function TDSSPointerList.Get_Active: Pointer;
Result := NIL;
end;
-//-------------------------------------------------------------------------
function TDSSPointerList.Get_First: Pointer;
begin
if NumInList > 0 then
@@ -117,7 +99,6 @@ function TDSSPointerList.Get_First: Pointer;
end;
end;
-//-------------------------------------------------------------------------
function TDSSPointerList.Get_Next: Pointer;
begin
if NumInList > 0 then
@@ -138,7 +119,6 @@ function TDSSPointerList.Get_Next: Pointer;
end;
end;
-//-------------------------------------------------------------------------
function TDSSPointerList.Get(i: Integer): Pointer;
begin
if (i < 1) or (i > NumInList) then
@@ -150,7 +130,6 @@ function TDSSPointerList.Get(i: Integer): Pointer;
end;
end;
-//-------------------------------------------------------------------------
function TDSSPointerList.At(i: Integer): Pointer;
begin
if (i < 1) or (i > NumInList) then
@@ -161,7 +140,6 @@ function TDSSPointerList.At(i: Integer): Pointer;
end;
end;
-//-------------------------------------------------------------------------
procedure TDSSPointerList.Clear;
begin
ActiveItem := 0;
diff --git a/src/Shared/DSSUcomplex.pas b/src/Shared/DSSUcomplex.pas
new file mode 100644
index 000000000..e0239802b
--- /dev/null
+++ b/src/Shared/DSSUcomplex.pas
@@ -0,0 +1,145 @@
+unit DSSUcomplex;
+
+{
+ ----------------------------------------------------------
+ Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
+ All rights reserved.
+ ----------------------------------------------------------
+}
+
+interface
+
+uses UComplex;
+
+type
+ Complex = UComplex.Complex;
+ PComplex = UComplex.PComplex;
+ pComplexArray = ^ComplexArray;
+ ComplexArray = array [1..100] of Complex;
+
+ polar = record
+ mag, ang: Double;
+ end;
+
+const
+ CDOUBLEONE: Complex = (re: 1.0; im: 1.0);
+ cZERO: Complex = (re: 0.0; im: 0.0);
+ cONE: Complex = (re: 1.0; im: 0.0);
+
+function cmplx(const a, b: Double): complex; inline;
+function cabs(const a: complex): Double; inline;
+Function cabs2(const a:complex):double; // best when you don't need sqrt -- TODO: rename?
+function cang(const a: complex): Double;
+function cdang(const a: complex): Double; // angle of complex number, degrees
+function ctopolar(const a: complex): polar;
+function ctopolardeg(const a: complex): polar; // complex to polar, degrees
+function topolar(const a, b: Double): polar; // scalar to polar
+function ptocomplex(const a: polar): complex;
+function pdegtocomplex(const magn, angle: Double): complex;
+function pclx(const magn, angle: Double): complex;
+
+implementation
+
+function CMPLX(const a, b: Double): complex; inline;
+begin
+ Result.RE := A;
+ Result.IM := B
+end;
+
+function Cabs(const a: complex): Double; inline;
+begin
+ Result := SQRT(A.RE * A.RE + A.IM * A.IM)
+end;
+
+function Cabs2(const a: complex): Double;
+begin
+ Result := (A.RE * A.RE + A.IM * A.IM)
+end;
+
+function ATAN2(x, iy: Double): Double;
+const
+ PI = 3.14159265359; { 180 DEGREES } // TODO: remove for 0.13
+begin
+ if (x < 0.0) and (iy >= 0) then
+ Result := arctan(iy / x) + PI
+ else
+ if (x < 0.0) and (iy < 0) then
+ Result := arctan(iy / x) - PI
+ else
+ if (x > 0.0) then
+ Result := arctan(iy / x)
+ else
+ if (iy < 0.0) then
+ Result := -PI / 2
+ else
+ if (iy > 0.0) then
+ Result := PI / 2
+ else
+ Result := 0.0
+end;
+
+function CANG(const a: complex): Double;
+begin
+ Result := ATAN2(A.RE, A.IM)
+end;
+
+function CDANG(const a: complex): Double;
+begin
+ Result := ATAN2(A.RE, A.IM) * 57.29577951;
+end;
+
+function CtoPOLAR(const a: complex): polar;
+begin
+ with Result do
+ begin
+ MAG := Cabs(A);
+ ANG := CANG(A)
+ end;
+end;
+
+function CtoPOLARdeg(const a: complex): polar;
+begin
+ with Result do
+ begin
+ MAG := Cabs(A);
+ ANG := CDANG(A)
+ end;
+end;
+
+function toPOLaR(const a, b: Double): polar;
+begin
+ with Result do
+ begin
+ MAG := A;
+ ANG := B;
+ end;
+end;
+
+function PCLX(const magn, angle: Double): complex;
+begin
+ Result.RE := Magn * Cos(Angle);
+ Result.IM := Magn * Sin(Angle);
+end;
+
+function PDEGtoCompLeX(const magn, angle: Double): complex;
+var
+ Ang: Double;
+begin
+ Ang := Angle / 57.29577951;
+ with Result do
+ begin
+ RE := Magn * Cos(Ang);
+ IM := Magn * Sin(Ang);
+ end;
+end;
+
+function PtoCOMPLEX(const a: polar): complex;
+begin
+ with Result do
+ begin
+ RE := A.MAG * COS(A.ANG);
+ IM := A.MAG * SIN(A.ANG);
+ end;
+end;
+
+end.
diff --git a/src/Shared/Dynamics.pas b/src/Shared/Dynamics.pas
index a3819df52..dc108ba5f 100644
--- a/src/Shared/Dynamics.pas
+++ b/src/Shared/Dynamics.pas
@@ -7,11 +7,11 @@
----------------------------------------------------------
}
-{Definitions of constants and structures for the Solution object and user-written dynamic models}
+// Definitions of constants and structures for the Solution object and user-written dynamic models
interface
-{Solution modes}
+// Solution modes
type
{$PUSH}
@@ -40,14 +40,13 @@ interface
{$SCOPEDENUMS OFF}
{$POP}
- {Variables needed for dynamics and user-written models.}
+ // Variables needed for dynamics and user-written models.
TDynamicsRec = {$IFNDEF DSS_CAPI_NO_PACKED_RECORDS}packed{$ENDIF} record
- {time vars}
h, // Time step size in sec for dynamics
t, // sec from top of hour
tstart,
tstop: Double;
- IterationFlag: Integer; {0=New Time Step; 1= Same Time Step as last iteration}
+ IterationFlag: Integer; // 0=New Time Step; 1= Same Time Step as last iteration
SolutionMode: TSolveMode; // PEAKSNAP, DAILYMODE, YEARLYMODE, MONTECARLO, etc. (see DSSGlobals)
intHour: Integer; // time, in hours as an integer
dblHour: Double; // time, in hours as a floating point number including fractional part
diff --git a/src/Shared/HashList.pas b/src/Shared/HashList.pas
index 1be47df47..6a457d2e0 100644
--- a/src/Shared/HashList.pas
+++ b/src/Shared/HashList.pas
@@ -7,25 +7,17 @@
----------------------------------------------------------
}
-{
- This Hash list module is designed to make searches on arrays of strings more
- efficient. The list actually consists of several short linear lists. When a string
- is added, it is hashed and placed at the end of one of the lists.
-
- The list may by searched by string or by index. When by string, the string
- is hashed and the search is restricted to the resulting linear list. When by
- index, it simply goes to that index in the array of pointers that points to the
- individual strings.
-
- All strings are saved in lower case and tested with case sensitivity. This
- actually makes the search insensitive to case because everything is lower case.
-
- Modified 4/18/98 to allocate on demand. That way, you can create it for a certain
- number of hash lists, without caring about how many actual there will be.
-
-}
-
-{$M+}
+// This Hash list module is designed to make searches on arrays of strings more
+// efficient. The list actually consists of several short linear lists. When a string
+// is added, it is hashed and placed at the end of one of the lists.
+//
+// The list may by searched by string or by index. When by string, the string
+// is hashed and the search is restricted to the resulting linear list. When by
+// index, it simply goes to that index in the array of pointers that points to the
+// individual strings.
+//
+// All strings are saved in lower case and tested with case sensitivity. This
+// actually makes the search insensitive to case because everything is lower case.
interface
@@ -34,10 +26,6 @@ interface
ArrayDef;
type
-
- //pStringArray = ^StringArray;
- // StringArray = Array[1..100] of String;
-
TSubList = record
Nelem: Cardinal;
NAllocated: Cardinal;
@@ -63,8 +51,6 @@ THashList = class(TObject)
procedure ResizeSubList(var SubList: TSubList);
function Hash(const S: String): Cardinal;
procedure ResizeStrPtr;
- PROTECTED
-
PUBLIC
InitialAllocation: Cardinal;
constructor Create(Nelements: Cardinal);
@@ -78,8 +64,6 @@ THashList = class(TObject)
procedure DumpToFile(const fname: String);
procedure Clear;
property Count: Cardinal READ NumElements;
- PUBLISHED
-
end;
{$IFDEF DSS_CAPI_HASHLIST}
@@ -103,6 +87,7 @@ TAltHashList = class (TFPHashList)
implementation
uses
+ BufStream,
Classes,
Utilities,
Sysutils,
@@ -146,7 +131,6 @@ destructor THashList.Destroy;
var
i, j: Integer;
begin
-
for i := 1 to NumLists do
begin
{DeAllocated Sublists}
@@ -196,7 +180,6 @@ procedure THashList.ResizeSubList(var SubList: TSubList);
ReallocStr(Str, Sizeof(Str^[1]) * OldAllocation, Sizeof(Str^[1]) * Nallocated);
Reallocmem(Idx, Sizeof(Idx^[1]) * Nallocated);
end;
-
end;
(* New supposedly fast hash method *)
@@ -244,7 +227,7 @@ function THashList.Add(const S: String): Integer;
HashNum: Cardinal;
SS: String;
begin
- SS := LowerCase(S);
+ SS := AnsiLowerCase(S);
HashNum := Hash(SS);
Inc(NumElements);
@@ -265,7 +248,6 @@ function THashList.Add(const S: String): Integer;
StringPtr^[NumElements] := SS; // increments count to string
Idx^[Nelem] := NumElements;
end;
-
end;
@@ -273,8 +255,7 @@ function THashList.Find(const S: String): Integer;
var
i: Integer;
begin
-
- LastSearchString := LowerCase(S);
+ LastSearchString := AnsiLowerCase(S);
LastHash := Hash(LastSearchString);
Result := 0;
LastFind := 0;
@@ -291,7 +272,6 @@ function THashList.Find(const S: String): Integer;
end;
end;
end;
-
end;
function THashList.FindNext: Integer;
@@ -300,7 +280,6 @@ function THashList.FindNext: Integer;
var
i: Integer;
begin
-
Result := 0; // Default return
Inc(LastFind); // Start with next item in hash list
@@ -317,7 +296,6 @@ function THashList.FindNext: Integer;
end;
end;
end;
-
end;
@@ -332,7 +310,7 @@ function THashList.FindAbbrev(const S: String): Integer;
if Length(S) = 0 then
Exit;
- Test1 := LowerCase(S);
+ Test1 := AnsiLowerCase(S);
for i := 1 to NumElements do
begin
@@ -361,7 +339,7 @@ procedure THashList.DumpToFile(const fname: String);
i, j: Integer;
sout: String;
begin
- F := TFileStream.Create(fname, fmCreate);
+ F := TBufferedFileStream.Create(fname, fmCreate);
FSWriteln(F, Format('Number of Hash Lists = %d, Number of Elements = %d', [NumLists, NumElements]));
FSWriteln(F);
@@ -395,7 +373,6 @@ procedure THashList.DumpToFile(const fname: String);
FSWriteln(F, sout);
end;
FreeAndNil(F);
-
end;
procedure THashList.Clear;
@@ -430,12 +407,12 @@ destructor TAltHashList.Destroy;
end;
function TAltHashList.Add(const S: String): Integer; inline;
begin
- inherited Add(LowerCase(s), Pointer(self.Count + 1));
+ inherited Add(AnsiLowerCase(s), Pointer(self.Count + 1));
Result := self.Count;
end;
function TAltHashList.Find(const S: String): Integer; inline;
begin
- Result := Integer(inherited Find(LowerCase(s)));
+ Result := Integer(inherited Find(AnsiLowerCase(s)));
end;
function TAltHashList.NameOfIndex(i: Integer): String; inline;
begin
@@ -465,7 +442,7 @@ function TAltHashList.FindAbbrev(const S: String): Integer;
if Length(S) = 0 then
Exit;
- Test1 := LowerCase(S);
+ Test1 := AnsiLowerCase(S);
for i := 1 to Count do
begin
diff --git a/src/Shared/LineUnits.pas b/src/Shared/LineUnits.pas
index 5922c2db6..fce52302c 100644
--- a/src/Shared/LineUnits.pas
+++ b/src/Shared/LineUnits.pas
@@ -43,7 +43,6 @@ function GetUnitsCode(const S: String): Integer;
Stest: String;
begin
-
Result := 0;
Stest := Copy(S, 1, 2); // copy first 2 chars for MOST OF the test
if CompareText(Stest, 'no') = 0 then
@@ -80,7 +79,6 @@ function GetUnitsCode(const S: String): Integer;
function LineUnitsStr(Units: Integer): String;
begin
-
case Units of
0:
Result := 'none';
@@ -107,10 +105,9 @@ function LineUnitsStr(Units: Integer): String;
function To_Meters(Units: Integer): Double;
begin
-
case Units of
UNITS_MILES:
- Result := 1609.3;
+ Result := 1609.344;
UNITS_KFT:
Result := 304.8;
UNITS_KM:
diff --git a/src/Shared/Pstcalc.pas b/src/Shared/Pstcalc.pas
index 694a063bb..c81b87356 100644
--- a/src/Shared/Pstcalc.pas
+++ b/src/Shared/Pstcalc.pas
@@ -16,21 +16,14 @@ interface
// Adapted from Jeff Smith's original C++ code
// note: the scaling factor being used has been corrected. It is scaling the output of Block 4 to
// 1.0 for the values given in Table 1 and Table 2. The last table in the std should be used for checking Pst
-// 4/12/05 The meter is verified using Table 2 and Table 5 (Pinst and Pst tables)
-// 7/28/05: This cpp is designed to receive a 6-cycle rms data stream from the dss and return Pst
-// 8/3/05: Updated to receive single to 6-cycle rms data streams. The rms window "DeltaT" determines which scaling factor to use
-// 8/4/05: added back functionality to receive AC data. Now these are 2 separate loops
-// 8/31/11 Converted to Object Pascal
-// 9/6/11 Removed PstAC code
-// 1/22/2013 added RMS flickermeter implementation (temc)
// Note: allocates result array of doubles!!!
function PstRMS(var PstResult: pDoubleArray; pVoltages: pdoubleArray; Freqbase: Double; NcyclesperSample, Npts, Lamp: Integer): Integer;
- // returns number of Pst elements computed in PstResult array
- // That is the number of 10-minute intervals
- // will automatically clean up and reallocate PstStruct when this function is called
- // Init PstResult to Nil in calling routine.
- // Dispose of result in colling routine when done with it.
+// returns number of Pst elements computed in PstResult array
+// That is the number of 10-minute intervals
+// will automatically clean up and reallocate PstStruct when this function is called
+// Init PstResult to Nil in calling routine.
+// Dispose of result in colling routine when done with it.
// input: N points of RMS voltage in pT, pRms
// fBase (50 or 60) determines the weighting coefficients
@@ -97,7 +90,6 @@ function SB(y: Double; bins: pBinArray): Double;
found: Boolean;
begin
-
found := FALSE;
n := 0;
@@ -115,7 +107,6 @@ function SB(y: Double; bins: pBinArray): Double;
end
else
Result := 0.0;
-
end;
procedure ZeroOutBins;
@@ -140,7 +131,6 @@ function CalcPst: Double;
P01, P1s, P3s, P10s, P50s: Double;
begin
-
num_pts := 0;
for n := 0 to number_bins - 1 do
begin
@@ -172,7 +162,6 @@ function CalcPst: Double;
// This is the Pst
Result := sqrt(0.0314 * P01 + 0.0525 * P1s + 0.0657 * P3s + 0.28 * P10s + 0.08 * P50s);
-
end;
//////////////////////////////////////////////////////////////////////
@@ -184,7 +173,6 @@ procedure Set_Filter_Coefficients(input_type: Integer);
K, Lambda, W1, W2, W3, W4: Double;
begin
-
// Coefficients for Input Voltage Adapter
// L = 8.93125 H
// C = 35.725 F
@@ -313,7 +301,6 @@ procedure Sample_Shift;
procedure Get_Pinst;
begin
-
{RMS input}
RMSVin[0] := rms_reference * RMS_sample / rms_input; // per unitize rms value
@@ -336,11 +323,8 @@ procedure Get_Pinst;
// Sliding Mean filter
X9[0] := (X8[0] * X8[0] + X8[1] * X8[1] - (1 - 2 * SA / Tstep) * X9[1]) / (1 + 2 * SA / Tstep);
X10[0] := X9[0] / internal_reference;
-
end;
-//*******************************************************************
-//*******************************************************************
procedure Init6Array(var Y: Double6Array; V1, V2, V3, V4, V5, V6: Double);
begin
Y[0] := V1;
@@ -351,8 +335,6 @@ procedure Init6Array(var Y: Double6Array; V1, V2, V3, V4, V5, V6: Double);
Y[5] := V6;
end;
-//*******************************************************************
-//*******************************************************************
function _Pst(var PstResult: pDoubleArray; Varray: pDoubleArray; Npts: Integer): Integer;
var
@@ -369,7 +351,6 @@ function _Pst(var PstResult: pDoubleArray; Varray: pDoubleArray; Npts: Integer):
SynthesizedSamples: Integer;
SamplesPerDeltaT: Double; // this value is used when RMS data is used as input
begin
-
rms_reference := 120.0; // internal rms reference value (do not change)
init6Array(Vin, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
@@ -439,7 +420,6 @@ function _Pst(var PstResult: pDoubleArray; Varray: pDoubleArray; Npts: Integer):
// The following loop holds the rms input samples constant over the RMS period
for SynthesizedSamples := 1 to round(SamplesPerDeltaT) do
begin
-
Get_Pinst; // Computes what gets through filter (X10[0] )
{////////////// This starts the Pst calculations //////////////}
@@ -470,7 +450,6 @@ function _Pst(var PstResult: pDoubleArray; Varray: pDoubleArray; Npts: Integer):
reallocmem(Bins0, 0);
reallocmem(Bins1, 0);
-
end;
// Function call for executing PST calculator using RMS data
@@ -480,7 +459,6 @@ function PstRMS(var PstResult: pDoubleArray; pVoltages: pdoubleArray; Freqbase:
// will automatically clean up PstStruct when it is reallocated; Init to nil
begin
-
Fbase := Freqbase;
// lamp_type := 0; // 0 for 120V filters, 1 for 230V filters
@@ -498,13 +476,10 @@ function PstRMS(var PstResult: pDoubleArray; pVoltages: pdoubleArray; Freqbase:
DeltaT := NcyclesperSample / Fbase;
Result := _Pst(PstResult, pVoltages, Npts);
-
end;
/////////////////////////////////////////////////////////
-//
// RMS flickermeter implementation
-//
/////////////////////////////////////////////////////////
procedure Fhp(N: Integer; Ts: Single; whp: Single;
@@ -626,7 +601,7 @@ procedure FlickerMeter(N: Integer; fBase: Double; vBase: Double; pT: pSingleArra
var
i, ipst, ihst: Integer;
t, tPst: Single;
- // filter coefficients
+ // filter coefficients
whp, w1, w2, w3, w4, k, lam, tau, ts, cf: Single;
pBuf: pSingleArray;
hst: array of Single;
@@ -670,7 +645,7 @@ procedure FlickerMeter(N: Integer; fBase: Double; vBase: Double; pT: pSingleArra
for i := 1 to N do
pRms[i] := cf * pRms[i];
- // build the Blcok 5 Pst outputs from Block 4 instantaneous flicker levels
+ // build the Blcok 5 Pst outputs from Block 4 instantaneous flicker levels
SetLength(hst, trunc(600.0 / Ts) + 1);
ihst := Low(hst);
for i := 1 to N do
@@ -716,7 +691,4 @@ procedure FlickerMeter(N: Integer; fBase: Double; vBase: Double; pT: pSingleArra
end;
end;
-
-initialization
-
end.
diff --git a/src/Shared/Ucmatrix.pas b/src/Shared/Ucmatrix.pas
index e1ec42ef9..fd7a1f4dd 100644
--- a/src/Shared/Ucmatrix.pas
+++ b/src/Shared/Ucmatrix.pas
@@ -1,45 +1,39 @@
unit Ucmatrix;
-
{
----------------------------------------------------------
Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
- Copyright (c) 2018-2020, Paulo Meira
+ Copyright (c) 2018-2021, Paulo Meira
All rights reserved.
----------------------------------------------------------
}
-
interface
uses
- UComplex;
-
-{
- 12-4-99 Added MvMultAccum
- 2/4/03 Added Avg routines
-}
+ UComplex, DSSUcomplex;
type
+ PCMatrix = ^TCMatrix;
+
TcMatrix = class(TObject)
PRIVATE
- { Private declarations }
Norder: Integer;
Values: pComplexArray;
-
+ OwnsData: Boolean;
PUBLIC
- { Public declarations }
InvertError: Integer;
constructor CreateMatrix(N: Integer);
+ constructor CreateMatrixInplace(N: Integer; pValues: pComplex);
+
destructor Destroy; OVERRIDE;
procedure Invert;
procedure Negate;
function IsZero: Boolean;
function IsColRowZero(n: Integer): Boolean;
- procedure Clear; inline; {Zero out matrix}
+ procedure Clear; inline; // Zero out matrix
procedure AddFrom(OtherMatrix: TcMatrix);
- procedure SubtractOther(OtherMatrix: TcMatrix);
procedure CopyFrom(OtherMatrix: TcMatrix);
procedure SetElement(i, j: Integer; Value: Complex);
procedure SetElemsym(i, j: Integer; Value: Complex);
@@ -47,59 +41,63 @@ TcMatrix = class(TObject)
procedure AddElemsym(i, j: Integer; Value: Complex);
function GetElement(i, j: Integer): Complex;
function GetErrorCode: Integer;
- // function SumBlock(row1, row2, col1, col2: Integer): Complex;
- procedure MVmult(b, x: pComplexArray); {inline;} {b = Ax}
- // procedure MVmultAccum(b, x: pComplexArray); {b = Ax}
+ procedure MVmult(b, x: pComplexArray); inline; {b = Ax}
function GetValuesArrayPtr(var Order: Integer): pComplexArray;
procedure ZeroRow(iRow: Integer);
procedure ZeroCol(iCol: Integer);
function AvgDiagonal: Complex; // Average of Diagonal Elements
function AvgOffDiagonal: Complex;
- procedure MultByConst(x: Double); // Multiply all elements by a constant
function MtrxMult(B: TcMatrix): TcMatrix; // Multiply two square matrices of same order. Result = A*B
function Kron(EliminationRow: Integer): TcMatrix; // Perform Kron reduction on last row/col and return new matrix
property Order: Integer READ Norder;
-
end;
-{--------------------------------------------------------------------------}
-
-
implementation
uses
KLUSolve;
-{$R-} { Turn off range checking}
-{--------------------------------------------------------------------------}
constructor TcMatrix.CreateMatrix(N: Integer);
begin
+ try
+ inherited Create;
+ Norder := N;
+ InvertError := 0;
+ Values := Allocmem(Sizeof(Complex) * Norder * Norder); // alloc and fill with 0
+ OwnsData := True;
+ except
+ Destroy;
+ end;
+end;
+constructor TcMatrix.CreateMatrixInplace(N: Integer; pValues: pComplex);
+begin
try
inherited Create;
Norder := N;
InvertError := 0;
- Values := NIL;
- Reallocmem(Values, Sizeof(Complex) * Norder * Norder); {Allocate}
- FillByte(Values^, Sizeof(Complex) * Norder * Norder, 0);
+ Values := pComplexArray(pValues); // assume zeroed
+ OwnsData := False;
except
Destroy;
end;
end;
-{--------------------------------------------------------------------------}
+
+
destructor TcMatrix.Destroy;
begin
- Freemem(Values, Sizeof(Complex) * Norder * Norder);
+ if OwnsData then
+ Freemem(Values, Sizeof(Complex) * Norder * Norder);
inherited Destroy;
end;
-{--------------------------------------------------------------------------}
+
procedure TcMatrix.Clear; inline;
begin
FillByte(Values^, Sizeof(Complex) * Norder * Norder, 0);
end;
-{--------------------------------------------------------------------------}
+
function TcMatrix.IsZero: Boolean; // This only check for exactly zero, no epsilon is used on purpose
var
i: integer;
@@ -117,7 +115,7 @@ function TcMatrix.IsZero: Boolean; // This only check for exactly zero, no epsil
inc(v);
end;
end;
-{--------------------------------------------------------------------------}
+
function TcMatrix.IsColRowZero(n: Integer): Boolean; // This only check for exactly zero, no epsilon is used on purpose
var
i, j: integer;
@@ -144,8 +142,8 @@ function TcMatrix.IsColRowZero(n: Integer): Boolean; // This only check for exac
end;
end;
end;
-{--------------------------------------------------------------------------}
-procedure TcMatrix.MvMult(b, x: pComplexArray); {inline;}
+
+procedure TcMatrix.MvMult(b, x: pComplexArray); inline;
{$IFDEF DSS_CAPI_MVMULT}
begin
KLUSolve.mvmult(Norder, b, values, x);
@@ -160,40 +158,20 @@ procedure TcMatrix.MvMult(b, x: pComplexArray); {inline;}
Sum := Cmplx(0.0, 0.0);
for j := 1 to Norder do
begin
- Caccum(Sum, cmul(Values^[((j - 1) * Norder + i)], x^[j]));
+ Sum += (Values^[((j - 1) * Norder + i)]) * x^[j]);
end;
b^[i] := Sum;
end;
end;
{$ENDIF}
-{--------------------------------------------------------------------------}
-// procedure TcMatrix.MvMultAccum(b, x: pComplexArray);
-// // Same as MVMult except accumulates b
-// var
-// Sum: Complex;
-// i, j: Integer;
-// begin
-//
-// for i := 1 to Norder do
-// begin
-// Sum := Cmplx(0.0, 0.0);
-// for j := 1 to Norder do
-// begin
-// Caccum(Sum, cmul(Values^[((j - 1) * Norder + i)], x^[j]));
-// end;
-// Caccum(b^[i], Sum);
-// end;
-//
-// end;
-//
-{--------------------------------------------------------------------------}
+
procedure TcMatrix.Negate;
var i: integer;
begin
for i := 1 to Norder * Norder do
- Values^[i] := Cnegate(Values^[i]);
+ Values^[i] := -Values^[i];
end;
-{--------------------------------------------------------------------------}
+
procedure TcMatrix.Invert;
type
pIntArray = ^IntArray;
@@ -214,15 +192,13 @@ procedure TcMatrix.Invert;
begin
-
L := Norder;
InvertError := 0;
- A := Values; { Assign pointer to something we can use}
-
-{Allocate LT}
-// LT:=nil;
+ A := Values; // Assign pointer to something we can use
+ // Allocate LT
+ // LT:=nil;
GetMem(LT, SizeOf(Integer) * L);
if LT = NIL then
begin
@@ -230,31 +206,30 @@ procedure TcMatrix.Invert;
Exit;
end;
-{Zero LT}
+ // Zero LT
for j := 1 to L do
LT^[j] := 0;
T1 := Cmplx(0.0, 0.0);
K := 1;
-{M Loop }
-
+ // M Loop
for M := 1 to L do
begin
for LL := 1 to L do
begin
if LT^[LL] <> 1 then
begin
- RMY := Cabs(A^[Index(LL, LL)]) - CAbs(T1); {Will this work??}
+ RMY := Cabs(A^[Index(LL, LL)]) - CAbs(T1); // Will this work??
if RMY > 0.0 then
begin
T1 := A^[Index(LL, LL)];
K := LL;
- end; {RMY}
- end; {IF LT}
- end; {LL}
+ end;
+ end;
+ end;
-{Error Check. If RMY ends up zero, matrix is non-inversible}
+ // Error Check. If RMY ends up zero, matrix is non-inversible
RMY := Cabs(T1);
if RMY = 0.0 then
begin
@@ -269,90 +244,60 @@ procedure TcMatrix.Invert;
for j := 1 to L do
if j <> k then
A^[Index(i, j)] :=
- Csub(A^[Index(i, j)], Cdiv(Cmul(A^[Index(i, k)], A^[Index(k, j)]), A^[Index(k, k)]));
+ A^[Index(i, j)] - (A^[Index(i, k)] * A^[Index(k, j)]) / A^[Index(k, k)];
- A^[Index(k, k)] := Cnegate(Cinv(A^[Index(k, k)])); {Invert and negate k,k element}
+ A^[Index(k, k)] := -Cinv(A^[Index(k, k)]); // Invert and negate k,k element
for i := 1 to L do
if i <> k then
begin
- A^[Index(i, k)] := Cmul(A^[Index(i, k)], A^[Index(k, k)]);
- A^[Index(k, i)] := Cmul(A^[Index(k, i)], A^[Index(k, k)]);
- end; {if}
+ A^[Index(i, k)] := A^[Index(i, k)] * A^[Index(k, k)];
+ A^[Index(k, i)] := A^[Index(k, i)] * A^[Index(k, k)];
+ end;
- end; {M loop}
+ end; // M loop
for j := 1 to L do
for k := 1 to L do
- A^[Index(j, k)] := Cnegate(A^[Index(j, k)]);
-
- FreeMem(LT, SizeOF(LT^[1]) * L); {Dispose of LT}
+ A^[Index(j, k)] := -A^[Index(j, k)];
+ FreeMem(LT, SizeOF(LT^[1]) * L);
end;
-{--------------------------------------------------------------------------}
procedure TcMatrix.SetElement(i, j: Integer; Value: Complex);
begin
Values^[((j - 1) * Norder + i)] := Value;
end;
-{--------------------------------------------------------------------------}
procedure TcMatrix.AddElement(i, j: Integer; Value: Complex);
begin
- cAccum(Values^[((j - 1) * Norder + i)], Value);
+ Values^[((j - 1) * Norder + i)] += Value;
end;
-{--------------------------------------------------------------------------}
procedure TcMatrix.SetElemsym(i, j: Integer; Value: Complex);
begin
Values^[((j - 1) * Norder + i)] := Value;
if i <> j then
- Values^[((i - 1) * Norder + j)] := Value; {ensure symmetry}
+ Values^[((i - 1) * Norder + j)] := Value; // ensure symmetry
end;
-
- {--------------------------------------------------------------------------}
+
procedure TcMatrix.AddElemsym(i, j: Integer; Value: Complex);
begin
- cAccum(Values^[((j - 1) * Norder + i)], Value);
+ Values^[((j - 1) * Norder + i)] += Value;
if i <> j then
- cAccum(Values^[((i - 1) * Norder + j)], Value); {ensure symmetry}
+ Values^[((i - 1) * Norder + j)] += Value; // ensure symmetry
end;
-{--------------------------------------------------------------------------}
function TcMatrix.GetElement(i, j: Integer): Complex;
begin
Result := Values^[((j - 1) * Norder + i)];
end;
-{--------------------------------------------------------------------------}
function TcMatrix.GetErrorCode: Integer;
begin
Result := InvertError;
end;
-{--------------------------------------------------------------------------}
-// function TcMatrix.SumBlock(row1, row2, col1, col2: Integer): Complex;
-// { Sum all elements in a given block of the matrix}
-//
-// var
-// i, j, rowstart: Integer;
-// Sum: Complex;
-//
-// begin
-// Sum := Cmplx(0.0, 0.0);
-//
-// for j := col1 to col2 do
-// begin
-// Rowstart := (j - 1) * Norder;
-// for i := (rowstart + row1) to (rowstart + row2) do
-// Sum := Cadd(Sum, Values^[i]);
-// end;
-//
-// Result := Sum;
-//
-// end;
-//
-{--------------------------------------------------------------------------}
procedure TcMatrix.CopyFrom(OtherMatrix: TcMatrix);
var
i, j: Integer;
@@ -365,7 +310,6 @@ procedure TcMatrix.CopyFrom(OtherMatrix: TcMatrix);
end;
end;
-{--------------------------------------------------------------------------}
procedure TcMatrix.AddFrom(OtherMatrix: TcMatrix);
var
i, j: Integer;
@@ -378,32 +322,16 @@ procedure TcMatrix.AddFrom(OtherMatrix: TcMatrix);
end;
end;
-{--------------------------------------------------------------------------}
-procedure TcMatrix.SubtractOther(OtherMatrix: TcMatrix);
-var
- i, j: Integer;
-begin
- if Norder = OtherMatrix.Norder then
- for i := 1 to Norder do
- begin
- for j := 1 to Norder do
- AddElement(i, j, cNegate(OtherMatrix.GetElement(i, j)));
- end;
-end;
-
-{--------------------------------------------------------------------------}
function TcMatrix.GetValuesArrayPtr(var Order: Integer): pComplexArray;
begin
Result := Values;
Order := Norder;
end;
-{--------------------------------------------------------------------------}
procedure TcMatrix.ZeroRow(iRow: Integer);
var
i, j: Integer;
Zero: Complex;
-
begin
Zero := Cmplx(0.0, 0.0);
@@ -415,14 +343,11 @@ procedure TcMatrix.ZeroRow(iRow: Integer);
end;
end;
-{--------------------------------------------------------------------------}
procedure TcMatrix.ZeroCol(iCol: Integer);
-
var
i: Integer;
Zero: Complex;
begin
-
Zero := Cmplx(0.0, 0.0);
for i := ((iCol - 1) * Norder + 1) to (iCol * Norder) do
begin
@@ -434,16 +359,14 @@ function TcMatrix.AvgDiagonal: Complex;
var
i: Integer;
begin
-
Result := Cmplx(0.0, 0.0);
for i := 1 to Norder do
begin
- Caccum(Result, Values^[((i - 1) * Norder + i)]);
+ Result += Values^[((i - 1) * Norder + i)];
end;
if Norder > 0 then
- Result := CdivReal(Result, (Norder));
-
+ Result := Result / Norder;
end;
function TcMatrix.AvgOffDiagonal: Complex;
@@ -451,25 +374,22 @@ function TcMatrix.AvgOffDiagonal: Complex;
var
i, j, Ntimes: Integer;
begin
-
Result := Cmplx(0.0, 0.0);
Ntimes := 0;
for i := 1 to Norder do
for j := i + 1 to Norder do
begin
Inc(Ntimes);
- Caccum(Result, Values^[((j - 1) * Norder + i)]);
+ Result += Values^[((j - 1) * Norder + i)];
end;
if Ntimes > 0 then
- Result := CdivReal(Result, (Ntimes));
+ Result := Result / Ntimes;
end;
function TcMatrix.Kron(EliminationRow: Integer): TcMatrix;
-
-{Do Kron reduction on present matrix and return a new one}
-{Eliminates specified row/column}
-
+// Do Kron reduction on present matrix and return a new one
+// Eliminates specified row/column
var
i, j, N: Integer;
ii, jj: Integer;
@@ -492,7 +412,7 @@ function TcMatrix.Kron(EliminationRow: Integer): TcMatrix;
if j <> N then
begin
Inc(jj);
- Result.SetElement(ii, jj, CSub(GetElement(i, j), Cdiv(Cmul(GetElement(i, N), GetElement(N, j)), NNElement)));
+ Result.SetElement(ii, jj, GetElement(i, j) - GetElement(i, N) * GetElement(N, j) / NNElement);
end;
end;
end;
@@ -524,14 +444,4 @@ function TcMatrix.MtrxMult(B: TcMatrix): TCmatrix; //TODO: C++ version if better
end;
end;
-procedure TcMatrix.MultByConst(x: Double);
-var
- i: Integer;
-begin
- for i := 1 to Norder * Norder do
- begin
- Values^[i] := CmulReal(Values^[i], x);
- end;
-end;
-
end.
diff --git a/src/Shared/Ucomplex.pas b/src/Shared/Ucomplex.pas
deleted file mode 100644
index f8aadad5c..000000000
--- a/src/Shared/Ucomplex.pas
+++ /dev/null
@@ -1,306 +0,0 @@
-unit Ucomplex;
-
-{
- ----------------------------------------------------------
- Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
- All rights reserved.
- ----------------------------------------------------------
-}
-
-interface
-
-type
- pcomplex = ^complex;
-
- complex = record
- re, im: Double;
- end;
- pComplexArray = ^ComplexArray;
- ComplexArray = array [1..100] of Complex;
-
- polar = record
- mag, ang: Double;
- end;
-
- // 4-8-2010 added inlining selected often-used functions
-function cmplx(const a, b: Double): complex;
-function cinv(const A: COMPLEX): COMPLEX;
-function cabs(const a: complex): Double;
-Function cabs2(const a:complex):double; // best when you don't need sqrt -- TODO: rename?
-function cang(const a: complex): Double;
-function cdang(const a: complex): Double; // angle of complex number, degrees
-function ctopolar(const a: complex): polar;
-function ctopolardeg(const a: complex): polar; // complex to polar, degrees
-function cadd(const a, b: complex): complex;
-procedure caccum(var a: complex; const b: complex); {a := a + b}
-function csub(const a, b: complex): complex;
-function cmul(a, b: complex): complex;
-procedure caccumarray(a, b: pComplexArray; N: Smallint);
-function cmulreal(const a: complex; const b: Double): Complex; { := a*b }
-procedure cmulrealaccum(var a: complex; const b: Double); { a=a*b}
-function cdiv(a, b: complex): complex;
-function cdivreal(const a: complex; const b: Double): Complex; { := a /b}
-function conjg(const a: complex): complex;
-function cnegate(const a: complex): complex;
-function csqrt(const a: complex): complex;
-function cln(const a: complex): complex;
-function topolar(const a, b: Double): polar; // scalar to polar
-function prel(const a: polar): Double; // real part of polar number |a| cos()
-function pimg(const a: polar): Double; // imag part of polar number |a| sin()
-function ptocomplex(const a: polar): complex;
-function padd(const a, b: polar): polar;
-function psub(const a, b: polar): polar;
-function pmul(const a, b: polar): polar;
-function pdiv(const a, b: polar): polar;
-function pdegtocomplex(const magn, angle: Double): complex;
-function pclx(const magn, angle: Double): complex;
-
-var
- cZERO, cONE: Complex;
-
-implementation
-
-function CMPLX(const a, b: Double): complex;
-begin
- Result.RE := A;
- Result.IM := B
-end;
-
-function CInv(const A: COMPLEX): COMPLEX;
-var
- DNOM: Double;
-begin
- DNOM := A.RE * A.RE + A.IM * A.IM;
- Result.RE := A.RE / DNOM;
- Result.IM := (-A.IM) / DNOM
-end;
-
-function Cabs(const a: complex): Double;
-begin
- Result := SQRT(A.RE * A.RE + A.IM * A.IM)
-end;
-
-function Cabs2(const a: complex): Double;
-begin
- Result := (A.RE * A.RE + A.IM * A.IM)
-end;
-
-function Conjg(const a: complex): complex;
-begin
- Result.RE := A.RE;
- Result.im := -A.im;
-end;
-
-function ATAN2(x, iy: Double): Double;
-const
- PI = 3.14159265359; { 180 DEGREES }
-begin
- if (x < 0.0) and (iy >= 0) then
- Result := arctan(iy / x) + PI
- else
- if (x < 0.0) and (iy < 0) then
- Result := arctan(iy / x) - PI
- else
- if (x > 0.0) then
- Result := arctan(iy / x)
- else
- if (iy < 0.0) then
- Result := -PI / 2
- else
- if (iy > 0.0) then
- Result := PI / 2
- else
- Result := 0.0
-end; { ATAN2 }
-
-function CANG(const a: complex): Double;
-begin
- Result := ATAN2(A.RE, A.IM)
-end;
-
-function CDANG(const a: complex): Double;
-begin
- Result := ATAN2(A.RE, A.IM) * 57.29577951;
-end;
-
-function CtoPOLAR(const a: complex): polar;
-begin
- with Result do
- begin
- MAG := Cabs(A);
- ANG := CANG(A)
- end;
-end;
-
-function CtoPOLARdeg(const a: complex): polar;
-begin
- with Result do
- begin
- MAG := Cabs(A);
- ANG := CDANG(A)
- end;
-end;
-
-function CADD(const a, b: complex): complex;
-begin
- Result.RE := A.RE + B.RE;
- Result.IM := A.IM + B.IM
-end;
-
-procedure CACCUM(var a: complex; const b: complex);
-begin
- a.re := a.re + b.re;
- a.im := a.im + b.im;
-end;
-
-procedure CACCUMARRAY(a, b: pComplexArray; N: Smallint);
-var
- i: Integer;
-begin
- for i := 1 to N do
- begin
- a^[i].re := a^[i].re + b^[i].re;
- a^[i].im := a^[i].im + b^[i].im;
- end;
-end;
-
-
-function CSUB(const a, b: complex): complex;
-begin
- Result.RE := A.RE - B.RE;
- Result.IM := A.IM - B.IM
-end;
-
-function CMUL(a, b: complex): complex;
-begin
- Result.RE := A.RE * B.RE - A.IM * B.IM;
- Result.IM := A.RE * B.IM + A.IM * B.RE
-end;
-
-function cmulreal(const a: complex; const b: Double): Complex; { := a*b }
-begin
- Result.re := a.re * b;
- Result.im := a.im * b;
-end;
-
-procedure cmulrealaccum(var a: complex; const b: Double); { a=a*b}
-begin
- a.re := a.re * b;
- a.im := a.im * b;
-end;
-
-function CDIV(a, b: complex): complex;
-var
- DNOM: Double;
-begin
- DNOM := B.RE * B.RE + B.IM * B.IM;
- Result.RE := (A.RE * B.RE + A.IM * B.IM) / DNOM;
- Result.IM := (A.IM * B.RE - A.RE * B.IM) / DNOM
-end;
-
-function cdivreal(const a: complex; const b: Double): Complex; { := a /b}
-begin
- Result.re := a.re / b;
- Result.im := a.im / b;
-end;
-
-function cnegate(const a: complex): complex;
-
-begin
- Result.re := -a.re;
- Result.im := -a.im;
-end;
-
-function csqrt(const a: complex): complex;
-var
- x: Polar;
-begin
- // algorithm: sqrt of magnitude/ half the angle
- x := ctopolar(A);
- Result := ptocomplex(topolar(sqrt(x.mag), x.ang / 2.0));
-end;
-
-function cln(const a: complex): complex;
-var
- x: Polar;
-begin
- // algorithm: ln of mag + j(angle), radians
- x := ctopolar(A);
- Result := cmplx(ln(x.mag), x.ang);
-end;
-
-function toPOLaR(const a, b: Double): polar;
-begin
- with Result do
- begin
- MAG := A;
- ANG := B;
- end;
-end;
-
-function PREL(const a: polar): Double;
-begin
- Result := A.MAG * COS(A.ANG)
-end;
-
-function PIMG(const a: polar): Double;
-begin
- Result := A.MAG * SIN(A.ANG)
-end;
-
-function PCLX(const magn, angle: Double): complex;
-begin
- Result.RE := Magn * Cos(Angle);
- Result.IM := Magn * Sin(Angle);
-end;
-
-function PDEGtoCompLeX(const magn, angle: Double): complex;
-var
- Ang: Double;
-begin
- Ang := Angle / 57.29577951;
- with Result do
- begin
- RE := Magn * Cos(Ang);
- IM := Magn * Sin(Ang);
- end;
-end;
-
-function PtoCOMPLEX(const a: polar): complex;
-begin
- with Result do
- begin
- RE := A.MAG * COS(A.ANG);
- IM := A.MAG * SIN(A.ANG);
- end;
-end;
-
-function PADD(const A, B: POLAR): POLAR;
-begin
- Result := CtoPOLAR(CADD(PtoCOMPLEX(A), PtoCOMPLEX(B)))
-end;
-
-function PSUB(const a, b: polar): polar;
-begin
- Result := CtoPOLAR(CSUB(PtoCOMPLEX(A), PtoCOMPLEX(B)))
-end;
-
-function PMUL(const a, b: polar): polar;
-begin
- Result.MAG := A.MAG * B.MAG;
- Result.ANG := A.ANG + B.ANG
-end;
-
-function PDIV(const a, b: polar): polar;
-begin
- Result.MAG := A.MAG / B.MAG;
- Result.ANG := A.ANG - B.ANG
-end;
-
-
-initialization
-
- cZERO := cmplx(0.0, 0.0);
- cONE := cmplx(1.0, 0.0);
-
-end.
diff --git a/src/Shared/mathutil.pas b/src/Shared/mathutil.pas
index 3f34a7960..e063186b9 100644
--- a/src/Shared/mathutil.pas
+++ b/src/Shared/mathutil.pas
@@ -1,6 +1,6 @@
unit Mathutil;
- {Math utilities}
+// Math utilities
{
----------------------------------------------------------
Copyright (c) 2008-2015, Electric Power Research Institute, Inc.
@@ -12,7 +12,7 @@ interface
uses
Arraydef,
- uComplex,
+ UComplex, DSSUcomplex,
uCmatrix;
type
@@ -20,9 +20,8 @@ interface
PComplex3 = ^Complex3;
Var
- As2p, Ap2s, ClarkeF, ClarkeR: TcMatrix; {Symmetrical Component Conversion Matrices}
+ As2p, Ap2s, ClarkeF, ClarkeR: TcMatrix; // Symmetrical Component Conversion Matrices
-procedure AB02Phase(Vph, VaB0: pComplexArray); // Reverse Clarke
function Bessel_I0(const a: Complex): Complex;
function Bessel_I1(const x: Complex): Complex;
procedure CalcKPowers(kWkvar, V, I: pComplexArray; N: Integer);
@@ -30,7 +29,6 @@ procedure ETKInvert(A: pDoubleArray; Norder: Integer; var Error: Integer); // R
function Gauss(Mean, StdDev: Double): Double;
function GetXR(const A: Complex): Double;
function ParallelZ(const Z1, Z2: Complex): Complex;
-procedure Phase2AB0(Vph, VaB0: pComplexArray); // Forward Clarke
procedure Phase2SymComp(Vph, V012: PComplex3); overload;
procedure Phase2SymComp(Vph, V012: pComplexArray); overload;
function QuasiLogNormal(Mean: Double): Double;
@@ -39,7 +37,6 @@ procedure RCDMeanAndStdDevSingle(pData: Pointer; Ndata: Integer; var Mean, StdDe
procedure CurveMeanAndStdDev(pY: pDoubleArray; pX: pDoubleArray; N: Integer; var Mean, StdDev: Double);
procedure CurveMeanAndStdDevSingle(pY: pSingleArray; pX: pSingleArray; N: Integer; var Mean, StdDev: Double);
-// function RCDSum( Data:Pointer; Count:Integer): Extended; register;
procedure SymComp2Phase(Vph, V012: pComplexArray);
procedure SymComp2Phase(Vph, V012: PComplex3);
function TerminalPowerIn(V, I: pComplexArray; Nphases: Integer): Complex;
@@ -52,43 +49,34 @@ implementation
Math;
procedure ETKInvert(A: pDoubleArray; Norder: Integer; var Error: Integer);
-
-{
- Matrix= reference to matrix of DOUBLEs
- Norder= order of matrix (assumed square)
- Error = 0 if no error;
- = 1 if not enough heap to alloc temp array
- = 2 if matrix can't be inverted
-
- This routine will invert a non-symmetric matrix. Index is assumed to
- follow the FORTRAN standard, not the Pascal standard. That is the data
- are ordered by first subscript first, then second subscript. This routine
- computes its own indexing, leaving nothing to the whims of a cantankerous compiler.
-
- It assumes that the matrix is dimensioned to exactly the number of elements
- needed. Apologies to Fortran users who are accustomed to over dimensioning
- stuff.
-
-}
-
+// Matrix= reference to matrix of DOUBLEs
+// Norder= order of matrix (assumed square)
+// Error = 0 if no error;
+// = 1 if not enough heap to alloc temp array
+// = 2 if matrix can't be inverted
+//
+// This routine will invert a non-symmetric matrix. Index is assumed to
+// follow the FORTRAN standard, not the Pascal standard. That is the data
+// are ordered by first subscript first, then second subscript. This routine
+// computes its own indexing, leaving nothing to the whims of a cantankerous compiler.
+//
+// It assumes that the matrix is dimensioned to exactly the number of elements
+// needed. Apologies to Fortran users who are accustomed to over dimensioning
+// stuff.
var
j, k, L, LL, M, i: Integer;
LT: pIntegerArray;
RMY, T1: Double;
-
function Index(i, j: Integer): Integer;
begin
Index := (j - 1) * L + i;
end;
-
-
begin
-
L := Norder;
Error := 0;
-{Allocate LT}
+ // Allocate LT
LT := NIL;
Reallocmem(LT, SizeOf(LT^[1]) * L);
if LT = NIL then
@@ -97,13 +85,13 @@ procedure ETKInvert(A: pDoubleArray; Norder: Integer; var Error: Integer);
Exit;
end;
-{Zero LT}
+ // Zero LT
for j := 1 to L do
LT^[j] := 0;
T1 := 0.0;
-{M Loop }
+ // M Loop
// initialize a safe value of k
k := 1;
@@ -118,11 +106,11 @@ procedure ETKInvert(A: pDoubleArray; Norder: Integer; var Error: Integer);
begin
T1 := A^[Index(LL, LL)];
K := LL;
- end; {RMY}
- end; {IF LT}
- end; {LL}
+ end;
+ end;
+ end;
-{Error Check. If RMY ends up zero, matrix is non-inversible}
+ // Error Check. If RMY ends up zero, matrix is non-inversible
RMY := Abs(T1);
if RMY = 0.0 then
begin
@@ -146,20 +134,17 @@ procedure ETKInvert(A: pDoubleArray; Norder: Integer; var Error: Integer);
begin
A^[Index(i, k)] := A^[Index(i, k)] * A^[Index(k, k)];
A^[Index(k, i)] := A^[Index(k, i)] * A^[Index(k, k)];
- end; {if}
+ end;
- end; {M loop}
+ end; // M loop
for j := 1 to L do
for k := 1 to L do
A^[Index(j, k)] := -A^[Index(j, k)];
- Reallocmem(LT, 0); {Dispose of LT}
-
-end; {Proc Invert}
-
+ Reallocmem(LT, 0);
+end;
-{-------------------------------------------------------------}
procedure Phase2SymComp(Vph, V012: PComplex3);
begin
Ap2s.MvMult(PComplexArray(V012), PComplexArray(Vph)); // TODO: dedicated/optimized version?
@@ -169,7 +154,6 @@ procedure Phase2SymComp(Vph, V012: pComplexArray);
Ap2s.MvMult(V012, Vph);
end;
-{-------------------------------------------------------------}
procedure SymComp2Phase(Vph, V012: PComplex3);
begin
As2p.MvMult(PComplexArray(Vph), PComplexArray(V012)); // TODO: dedicated/optimized version?
@@ -180,100 +164,29 @@ procedure SymComp2Phase(Vph, V012: pComplexArray);
As2p.MvMult(Vph, V012);
end;
-{-------------------------------------------------------------}
-procedure SetClarkeMatrices;
-var
- Sin2pi3: Double;
-begin
- Sin2pi3 := Sin(2.0 * PI / 3.0);
- with ClarkeF do
- begin // Forward Clarke
- SetElement(1, 1, cmplx(1.0, 0.0));
- SetElement(1, 2, cmplx(-0.5, 0.0));
- SetElement(1, 3, cmplx(-0.5, 0.0));
-
- SetElement(2, 2, cmplx(Sin2pi3, 0.0));
- SetElement(2, 3, cmplx(-Sin2pi3, 0.0));
-
- SetElement(3, 1, Cmplx(0.5, 0.0));
- SetElement(3, 2, Cmplx(0.5, 0.0));
- SetElement(3, 3, Cmplx(0.5, 0.0));
-
- MultByConst(2.0 / 3.0); // multiply all elements by a const 2/3
- end;
-
- with ClarkeR do
- begin // Reverse Clarke
- SetElement(1, 1, cmplx(1.0, 0.0));
- SetElement(2, 1, cmplx(-0.5, 0.0));
- SetElement(3, 1, cmplx(-0.5, 0.0));
-
- SetElement(2, 2, cmplx(Sin2pi3, 0.0));
- SetElement(3, 2, cmplx(-Sin2pi3, 0.0));
-
- SetElement(1, 3, Cmplx(1.0, 0.0));
- SetElement(2, 3, Cmplx(1.0, 0.0));
- SetElement(3, 3, Cmplx(1.0, 0.0));
-
- end;
-
-end;
-
-{-------------------------------------------------------------}
-
-procedure Phase2AB0(Vph, VaB0: pComplexArray); // Forward Clarke
-
-begin
- with ClarkeF do
- begin
- MvMult(VaB0, Vph);
- end;
-end;
-
-
-{-------------------------------------------------------------}
-procedure AB02Phase(Vph, VaB0: pComplexArray); // Reverse Clarke
-
-begin
- with ClarkeR do
- begin
- MvMult(Vph, VaB0);
- end;
-end;
-
-
function TerminalPowerIn(V, I: pComplexArray; Nphases: Integer): Complex;
// Computes total complex power given terminal voltages and currents
-
var
j: Integer;
-
begin
Result := CZERO;
for j := 1 to Nphases do
begin
- Caccum(Result, Cmul(V^[j], Conjg(I^[j])));
+ Result += V^[j] * cong(I^[j]);
end;
-
end;
-{-------------------------------------------------------------}
procedure CalcKPowers(kWkvar, V, I: pComplexArray; N: Integer);
-
-{Compute complex power in kW and kvar in each phase}
-
+// Compute complex power in kW and kvar in each phase
var
j: Integer;
begin
-
for j := 1 to N do
begin
- kWkVAR^[j] := CMulReal(Cmul(V^[j], Conjg(I^[j])), 0.001);
+ kWkVAR^[j] := V^[j] * cong(I^[j]) * 0.001;
end;
-
end;
-{-------------------------------------------------------------}
procedure SetAMatrix(Amat: Tcmatrix);
var
a, aa: complex;
@@ -289,19 +202,16 @@ procedure SetAMatrix(Amat: Tcmatrix);
SetElement(3, 3, aa);
SetElemsym(2, 3, a);
end;
-
end;
-
-{-------------------------------------------------------------}
procedure SetAMatrix_inv(Amat_inv: Tcmatrix);
var
a_3, aa_3, one_3: complex;
i: Integer;
begin
- a_3 := CDiv(cmplx(-0.5, 0.8660254037844387), cmplx(3, 0));
- aa_3 := CDiv(cmplx(-0.5, -0.8660254037844387), cmplx(3, 0));
- one_3 := CDiv(CONE, cmplx(3, 0));
+ a_3 := cmplx(-0.5, 0.8660254037844387) / 3;
+ aa_3 := cmplx(-0.5, -0.8660254037844387) / 3;
+ one_3 := CONE / 3;
with Amat_inv do
begin
for i := 1 to 3 do
@@ -310,13 +220,10 @@ procedure SetAMatrix_inv(Amat_inv: Tcmatrix);
SetElement(3, 3, a_3);
SetElemsym(2, 3, aa_3);
end;
-
end;
-
-{-------------------------------------------------------------}
function Gauss(Mean, StdDev: Double): Double;
-{Returns a normally distributed random variable}
+// Returns a normally distributed random variable
var
i: Integer;
A: Double;
@@ -327,73 +234,21 @@ function Gauss(Mean, StdDev: Double): Double;
Result := (A - 6.0) * StdDev + Mean;
end;
-{-------------------------------------------------------------}
function QuasiLogNormal(Mean: Double): Double;
-
-{Generates a quasi-lognormal distribution with approx 50% of values from 0 to Mean and the remainder from Mean to infinity}
+// Generates a quasi-lognormal distribution with approx 50% of values from 0 to Mean and the remainder from Mean to infinity
begin
-
Result := exp(Gauss(0.0, 1.0)) * Mean;
-
-end;
-
-//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-{$IF NOT (Defined(CPUX64) or Defined(Darwin) or Defined(Unix))}
-function RCDSUM(Data: Pointer; Count: Integer): Extended; REGISTER;
-
-// Sums an array of doubles quickly
-
-{ With register convention first 3 parameters are passed EAX, EDX, ECX and
- remainder on stack}
-
-asm // IN: EAX = ptr to Data, EDX = High(Data) = Count - 1
- // Uses 4 accumulators to minimize read-after-write delays and loop overhead
- // 5 clocks per loop, 4 items per loop = 1.2 clocks per item
- FLDZ
- SUB EDX, 1 // now EDX contains Count - 1
- MOV ECX, EDX
- FLD ST(0)
- AND EDX, not 3
- FLD ST(0)
- AND ECX, 3
- FLD ST(0)
- SHL EDX, 3 // count * sizeof(Double) = count * 8
- JMP @Vector.Pointer[ECX*4]
- @Vector:
- DD @@1
- DD @@2
- DD @@3
- DD @@4
- @@4: FADD qword ptr [EAX+EDX+24] // 1
- FXCH ST(3) // 0
- @@3: FADD qword ptr [EAX+EDX+16] // 1
- FXCH ST(2) // 0
- @@2: FADD qword ptr [EAX+EDX+8] // 1
- FXCH ST(1) // 0
- @@1: FADD qword ptr [EAX+EDX] // 1
- FXCH ST(2) // 0
- SUB EDX, 32
- JNS @@4
- FADDP ST(3),ST // ST(3) := ST + ST(3); Pop ST
- FADD // ST(1) := ST + ST(1); Pop ST
- FADD // ST(1) := ST + ST(1); Pop ST
- FWAIT
end;
-{$ENDIF}
procedure RCDMeanAndStdDev(pData: Pointer; Ndata: Integer; var Mean, StdDev: Double);
type
pDoubleArray = ^DoubleArray;
DoubleArray = array[1..100] of Double;
-
var
Data: pDoubleArray;
S: Double;
i: Integer;
-
begin
-
Data := pData; // make a double pointer
if Ndata = 1 then
begin
@@ -401,14 +256,10 @@ procedure RCDMeanAndStdDev(pData: Pointer; Ndata: Integer; var Mean, StdDev: Dou
StdDev := Data^[1];
Exit;
end;
-{$IF (Defined(CPUX64) or Defined(Darwin) or Defined(Unix))}
Mean := 0.0;
for i := 1 to NData do
Mean := Mean + Data^[i];
Mean := Mean / Ndata;
-{$ELSE ! CPUX86}
- Mean := RCDSum(Data, (Ndata)) / Ndata;
-{$ENDIF}
S := 0; // sum differences from the mean, for greater accuracy
for i := 1 to Ndata do
S := S + Sqr(Mean - Data^[i]);
@@ -498,7 +349,6 @@ procedure CurveMeanAndStdDevSingle(pY: pSingleArray; pX: pSingleArray; N: Intege
end;
function GetXR(const A: Complex): Double;
-
begin
if A.re <> 0.0 then
begin
@@ -509,19 +359,17 @@ function GetXR(const A: Complex): Double;
else
Result := 9999.0;
;
-
-
end;
function ParallelZ(const Z1, Z2: Complex): Complex;
var
Denom: Complex;
begin
- {Parallel two complex impedances}
- Denom := Cadd(Z1, Z2);
+ // Parallel two complex impedances
+ Denom := Z1 + Z2;
if (Abs(Denom.Re) > 0.0) or (abs(Denom.im) > 0.0) then
- Result := CDiv(Cmul(Z1, Z2), Denom)
- else {Error}
+ Result := (Z1 * Z2) / Denom
+ else // Error
Result := CZERO;
end;
@@ -530,7 +378,6 @@ function Bessel_I0(const a: Complex): Complex;
const
MaxTerm: Integer = 1000;
EpsilonSqr: Double = 1.0E-20;
-
var
i: Integer;
SizeSqr: Double;
@@ -539,54 +386,49 @@ function Bessel_I0(const a: Complex): Complex;
begin
RESULT := COne; // term 0
- zSQR25 := CmulReal(Cmul(a, a), 0.25);
+ zSQR25 := (a * a) * 0.25;
term := zSQR25;
- CAccum(RESULT, zSQR25); // term 1
+ RESULT += zSQR25; // term 1
i := 1;
repeat
- term := CMul(zSQR25, term);
+ term := zSQR25 * term;
INC(i);
- Term := CDivReal(term, SQR(i));
- CAccum(RESULT, term); // sum := sum + term
+ Term := term / SQR(i);
+ RESULT += term; // sum := sum + term
SizeSqr := SQR(term.re) + SQR(term.im)
until (i > MaxTerm) or (SizeSqr < EpsilonSqr)
-end {Bessel_I0};
+end;
function Bessel_I1(const x: Complex): Complex;
const
MaxTerm: Integer = 1000;
EpsilonSqr: Double = 1.0E-20;
-
var
i: Integer;
term, incterm, newterm: Complex;
SizeSqr: Double;
-
begin
- term := CdivReal(x, 2);
+ term := x / 2;
Result := Term;
incTerm := Term;
i := 4;
repeat
- newterm := CdivReal(x, i);
- Term := Cmul(term, cmul(incterm, newterm));
- Caccum(Result, Term);
+ newterm := x / i;
+ Term := Term * (incterm * newterm);
+ Result += Term;
incterm := newterm;
inc(i, 2);
SizeSqr := SQR(term.re) + SQR(term.im)
until (i > MaxTerm) or (SizeSqr < EpsilonSqr)
-
end;
function PctNemaUnbalance(Vph: PComplex3): Double;
-
-{Return Nema unbalance }
+// Return Nema unbalance
var
i: Integer;
Vavg: Double;
MaxDiff: Double;
VMag: array[1..3] of Double;
-
begin
for i := 1 to 3 do
VMag[i] := cabs(Vph^[i]);
@@ -604,11 +446,9 @@ function PctNemaUnbalance(Vph: PComplex3): Double;
Result := MaxDiff / Vavg * 100.0 // pct difference
else
Result := 0.0;
-
end;
procedure DblInc(var x: Double; const y: Double); inline;
-
begin
x := x + y;
end;
@@ -621,7 +461,7 @@ initialization
ClarkeR := TcMatrix.CreateMatrix(3);
SetAMatrix(As2p);
SetAMatrix_inv(Ap2s);
- SetClarkeMatrices;
+ // SetClarkeMatrices;
// Sqrt23 := Sqrt(2.0/3.0); // for park
finalization
As2p.Free;
diff --git a/src/classic_to_ctx.py b/src/classic_to_ctx.py
index c8a9249b8..24c7d9c20 100644
--- a/src/classic_to_ctx.py
+++ b/src/classic_to_ctx.py
@@ -57,9 +57,17 @@
"CAPI_Context in 'src/CAPI/CAPI_Context.pas'"
]
+def skip(fun):
+ prefixes = [' DSS_Dispose_', ' DSS_Get_PAnsiChar(', ' Obj_', 'Batch_', ' DSS_ExtractSchema']
+ for p in prefixes:
+ if p in fun:
+ return True
+
+ return False
+
for fn in glob('src/CAPI/*.pas'):
bn = os.path.basename(fn)
- if bn in ['CAPI_Utils.pas', 'CAPI_Types.pas', 'CAPI_Metadata.pas', 'CAPI_Context.pas']:
+ if bn in ['CAPI_Utils.pas', 'CAPI_Types.pas', 'CAPI_Metadata.pas', 'CAPI_Context.pas', 'CAPI_Obj.pas']:
continue
bnctx = bn.replace('CAPI_', 'CAPICtx_')
@@ -76,12 +84,14 @@
usefns.append(f"{ctxunitname} in 'build/generated/{bnctx}'")
- iface = src.split('implementation')[0]
+ iface, _ = src.split('implementation', 1)
funcs = [x.split('//')[0].strip() for x in iface.split('\n') if x.lower().startswith('function') or x.lower().startswith('procedure')]
-
+ file_funcnames = []
+ file_ftypes = []
for func in funcs:
assert '(' in func, (bn, func)
-
+ file_ftypes.append(func.split(' ', 1)[0])
+
try:
funcname = func.split()[1].split('(')[0]
except:
@@ -89,15 +99,46 @@
funcname = func.split()[1].split(':')[0]
except:
funcname = func.split()[1].split(';')[0]
-
+
src = src.replace(func, func.replace('()', '(DSS: TDSSContext)'))
src = src.replace(func, func.replace('(', '(DSS: TDSSContext; '))
src = src.replace(funcname + '(', funcname + '(DSS, ')
src = src.replace(funcname + '(DSS, )', funcname + '(DSS)')
src = src.replace(funcname + '(DSS, DSS:', funcname + '(DSS:')
src = src.replace(' ' + funcname + '(', ' ctx_' + funcname + '(')
- funcnames.append('ctx_' + funcname)
-
+ file_funcnames.append('ctx_' + funcname)
+
+ funcnames += file_funcnames
+
+ # This block is required to use the correct "actor" for the PM implementation
+ impls = src.split('implementation', 1)[1].split('\nend;\n')
+ assert '\nend; ' not in src, fn
+ fstarts = [ftype + ' ' + funcname + '(' for ftype, funcname in zip(file_ftypes, file_funcnames)]
+ n = '\n'
+ file_used_funcs = set()
+ if 'CAPI_Parallel' not in fn:
+ for impl in impls:
+ for fstart, ftype, funcname in zip(fstarts, file_ftypes, file_funcnames):
+ if fstart not in impl:
+ continue
+
+ if not '_GR(' in fstart:
+ new_impl, sub_count = re.subn(
+ rf'{re.escape(fstart)}(.*?){n}begin{n}(.*?)',
+ rf'{fstart}\1{n}begin{n} DSSPrime := DSSPrime.ActiveChild;{n}\2',
+ impl,
+ 1,
+ flags=re.DOTALL|re.IGNORECASE
+ )
+ assert sub_count == 1
+ assert fstart not in file_used_funcs, fstart
+ src = src.replace(impl, new_impl)
+ file_used_funcs.add(fstart)
+ break
+
+ missing = (set(fstart for fstart in fstarts if not '_GR(' in fstart) - file_used_funcs)
+ assert not missing, missing
+
src = src.replace(f'unit {unitname};', f'unit {ctxunitname};')
# src = src.replace('ctx_ctx_', 'ctx_')
# src = src.replace('ctx_ctx_', 'ctx_')
@@ -111,9 +152,10 @@
fo.write('// \n')
fo.write(src)
+
with open('build/generated/ctx_functions.inc', 'w') as fo:
- fo.write(' ' + ',\n '.join(sorted(funcnames)))
+ fo.write(' ' + ',\n '.join(sorted(set(funcnames))))
with open('build/generated/ctx_files.inc', 'w') as fo:
fo.write(' ' + ',\n '.join(sorted(usefns)))
@@ -137,7 +179,7 @@
functions = [
re.sub(r'( DSS_CAPI_DLL [^ ]+[ \*]*)([a-zA-Z0-9_]+)\(', r'\1ctx_\2(void* ctx, ', function)
for function in functions
- if (' DSS_Dispose_' not in function and ' DSS_Get_PAnsiChar(' not in function)
+ if not skip(function)
]
functions = '\n'.join(functions).replace('(void* ctx, void)', '(void* ctx)')
@@ -180,7 +222,7 @@
previous prime instance returned by ctx_Set_Prime, if required.
*/
DSS_CAPI_DLL void *ctx_Set_Prime(void *ctx);
-
+
''')
fo.write(functions)
fo.write('''
diff --git a/src/common-debug.cfg b/src/common-debug.cfg
index 2b7f0c869..b0193d0fb 100644
--- a/src/common-debug.cfg
+++ b/src/common-debug.cfg
@@ -1,12 +1,13 @@
#DEFINE DSS_CAPI
#DEFINE DSS_CAPI_MVMULT
#DEFINE DSS_CAPI_HASHLIST
-#DEFINE DSS_CAPI_CONTEXT
#DEFINE DSS_CAPI_INCREMENTAL_Y
+#DEFINE DSS_CAPI_CONTEXT
+#DEFINE DSS_CAPI_PM
+#DEFINE DSS_CAPI_ADIAKOPTICS_DISABLED
#DEFINE DSS_CAPI_DEBUG_BUILD
-Mdelphi
-Fd
--Fi./src/CMD
-Fu./src/Controls
-Fu./src/Executive
-Fu./src/General
@@ -16,8 +17,8 @@
-Fu./src/PDElements
-Fu./src/Shared
-Fu./src/Common
--Fu./src/CMD
-Fu./src/CAPI
+-Fu./src/lazutf8
-CF64
-Si-
-O-
@@ -27,4 +28,6 @@
-godwarfmethodclassprefix
-vew
-Sy
--vm6058
\ No newline at end of file
+-vm6058
+-Sm
+-d_:=DSSTranslate
diff --git a/src/common-release.cfg b/src/common-release.cfg
index 2130abaf3..88f5f5897 100644
--- a/src/common-release.cfg
+++ b/src/common-release.cfg
@@ -3,9 +3,10 @@
#DEFINE DSS_CAPI_HASHLIST
#DEFINE DSS_CAPI_INCREMENTAL_Y
#DEFINE DSS_CAPI_CONTEXT
+#DEFINE DSS_CAPI_PM
+#DEFINE DSS_CAPI_ADIAKOPTICS_DISABLED
-Mdelphi
-Fd
--Fi./src/CMD
-Fu./src/Controls
-Fu./src/Executive
-Fu./src/General
@@ -15,11 +16,20 @@
-Fu./src/PDElements
-Fu./src/Shared
-Fu./src/Common
--Fu./src/CMD
-Fu./src/CAPI
+-Fu./src/lazutf8
-Si
+-Sv
-CF64
-O3
-vew
-Sy
--vm6058
\ No newline at end of file
+-g
+-gw
+-gl
+-vm6058
+-Sm
+-XX
+-CX
+-d_:=DSSTranslate
+
diff --git a/src/darwin-arm64-dbg.cfg b/src/darwin-arm64-dbg.cfg
new file mode 100644
index 000000000..943391083
--- /dev/null
+++ b/src/darwin-arm64-dbg.cfg
@@ -0,0 +1,12 @@
+#include src/common-debug.cfg
+-Paarch64
+-FUbuild/units_arm64
+-Fllib/darwin_arm64
+-k-Llib/darwin_arm64
+-FElib/darwin_arm64
+-Cg
+-vm5025,5027,5029,9001
+-Tdarwin
+-k-lc
+-k-lm
+-k-lklusolvex
diff --git a/src/darwin-arm64.cfg b/src/darwin-arm64.cfg
new file mode 100644
index 000000000..44020c031
--- /dev/null
+++ b/src/darwin-arm64.cfg
@@ -0,0 +1,12 @@
+#include src/common-release.cfg
+-Paarch64
+-FUbuild/units_arm64
+-Fllib/darwin_arm64
+-k-Llib/darwin_arm64
+-FElib/darwin_arm64
+-Cg
+-vm5025,5027,5029,9001
+-Tdarwin
+-k-lc
+-k-lm
+-k-lklusolvex
diff --git a/src/dss_capi.lpr b/src/dss_capi.lpr
index f8f76a9ad..9689220ef 100644
--- a/src/dss_capi.lpr
+++ b/src/dss_capi.lpr
@@ -3,8 +3,9 @@
{$ENDIF}
{$MODE Delphi}
+{$IFDEF WINDOWS}
{$APPTYPE CONSOLE}
-
+{$ENDIF}
{ ----------------------------------------------------------
Copyright (c) 2008-2014, Electric Power Research Institute, Inc.
All rights reserved.
@@ -34,48 +35,51 @@
POSSIBILITY OF SUCH DAMAGE.
}
-{
-Copyright (c) 2017-2021, Paulo Meira
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-* Neither the name of the copyright holder nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-}
+// Although barely anything remains, the initial version of this file based on work
+// Copyright (c) 2016 Battelle Memorial Institute
-{
- 08/05/2008 Created from ESI DSS
-}
-{
- 08/17/2016 Created from OpenDSS
- ----------------------------------------------------------
- Copyright (c) 2016 Battelle Memorial Institute
- ----------------------------------------------------------
-}
+// Copyright (c) 2017-2022, Paulo Meira
+// Copyright (c) 2017-2022, DSS C-API contributors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// * Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
uses
+{$IFDEF DSS_CAPI_CONTEXT}
+{$IFDEF UNIX}
+ {$IF NOT DECLARED(UseHeapTrace)}
+ cmem,
+ {$ENDIF}
+ cthreads,
+{$ENDIF}
+{$ENDIF}
+{$IFNDEF DARWIN}
+ LazUTF8,
+{$ENDIF}
SysUtils,
Classes,
CustApp,
@@ -87,7 +91,6 @@
CableData in 'src/General/CableData.pas',
Capacitor in 'src/PDElements/Capacitor.pas',
CapControl in 'src/Controls/CapControl.pas',
- CapControlVars in 'src/Controls/CapControlVars.pas',
CapUserControl in 'src/Controls/CapUserControl.pas',
Circuit in 'src/Common/Circuit.pas',
CktElement in 'src/Common/CktElement.pas',
@@ -104,11 +107,9 @@
DSSCallBackRoutines in 'src/Common/DSSCallBackRoutines.pas',
DSSClass in 'src/Common/DSSClass.pas',
DSSClassDefs in 'src/Common/DSSClassDefs.pas',
- DSSGlobals in 'src/Common/DSSGlobals.pas',
DSSObject in 'src/General/DSSObject.pas',
Dynamics in 'src/Shared/Dynamics.pas',
EnergyMeter in 'src/Meters/EnergyMeter.pas',
- EventQueue in 'src/Common/EventQueue.pas',
ExecCommands in 'src/Executive/ExecCommands.pas',
ExecHelper in 'src/Executive/ExecHelper.pas',
ExecOptions in 'src/Executive/ExecOptions.pas',
@@ -122,7 +123,6 @@
UPFCControl in 'src/Controls/UPFCControl.pas',
GenDispatcher in 'src/Controls/GenDispatcher.pas',
generator in 'src/PCElements/generator.pas',
- GeneratorVars in 'src/PCElements/GeneratorVars.pas',
GenUserModel in 'src/PCElements/GenUserModel.pas',
GICLine in 'src/PCElements/GICLine.pas',
GICTransformer in 'src/PDElements/GICTransformer.pas',
@@ -172,7 +172,6 @@
StackDef in 'src/Shared/StackDef.pas',
Storage in 'src/PCElements/Storage.pas',
StorageController in 'src/Controls/StorageController.pas',
- StorageVars in 'src/PCElements/StorageVars.pas',
StoreUserModel in 'src/PCElements/StoreUserModel.pas',
SwtControl in 'src/Controls/SwtControl.pas',
TCC_Curve in 'src/General/TCC_Curve.pas',
@@ -230,6 +229,7 @@
CAPI_Meters in 'CAPI_Meters.pas',
CAPI_Monitors in 'CAPI_Monitors.pas',
CAPI_Parallel in 'CAPI_Parallel.pas',
+ CAPI_NoParallel in 'CAPI_NoParallel.pas',
CAPI_Parser in 'CAPI_Parser.pas',
CAPI_PDElements in 'CAPI_PDElements.pas',
CAPI_PVSystems in 'CAPI_PVSystems.pas',
@@ -250,7 +250,12 @@
CAPI_Vsources in 'CAPI_Vsources.pas',
CAPI_WireData in 'CAPI_WireData.pas', // API extension
CAPI_XYCurves in 'CAPI_XYCurves.pas',
- CAPI_YMatrix in 'CAPI_YMatrix.pas'
+ CAPI_YMatrix in 'CAPI_YMatrix.pas',
+ CAPI_Obj in 'CAPI_Obj.pas', // new experimental API
+ CAPI_ZIP in 'CAPI_ZIP.pas', // new experimental API
+
+ DSSGlobals in 'src/Common/DSSGlobals.pas'
+
{$IFDEF DSS_CAPI_CONTEXT},
{$I '../build/generated/ctx_files.inc'}
{$ENDIF}
@@ -465,6 +470,8 @@
CktElement_Get_AllVariableValues,
CktElement_Get_Variable,
CktElement_Get_Variablei,
+ CktElement_Set_Variable,
+ CktElement_Set_Variablei,
CktElement_Get_NodeOrder,
CktElement_Get_HasOCPDevice,
CktElement_Get_NumControls,
@@ -518,6 +525,8 @@
DSS_Set_AllowChangeDir, // API extension
DSS_Get_COMErrorResults, // API extension
DSS_Set_COMErrorResults, // API extension
+ DSS_Get_AllowDOScmd, // API extension
+ DSS_Set_AllowDOScmd, // API extension
DSSElement_Get_AllPropertyNames,
DSSElement_Get_Name,
DSSElement_Get_NumProperties,
@@ -1945,41 +1954,131 @@
YMatrix_Set_SolverOptions,
YMatrix_Get_SolverOptions,
+ Text_CommandBlock,
+ Text_CommandArray,
+ ZIP_Open,
+ ZIP_Close,
+ ZIP_Redirect,
+ ZIP_Extract,
+ ZIP_Extract_GR,
+ ZIP_Contains,
+ ZIP_List,
+
DSS_RegisterPlotCallback,
- DSS_RegisterMessageCallback
+ DSS_RegisterMessageCallback,
+
+ DSS_ExtractSchema,
+ DSS_Dispose_String,
+ DSS_Dispose_PPointer,
+ Obj_New,
+ Obj_GetHandleByName,
+ Obj_GetHandleByIdx,
+ Obj_GetName,
+ Obj_PropertySideEffects,
+ Obj_BeginEdit,
+ Obj_EndEdit,
+ Obj_Activate,
+ Obj_GetPropSeqPtr,
+ Obj_GetFloat64,
+ Obj_GetInt32,
+ Obj_GetString,
+ Obj_GetObject,
+ Obj_GetAsString,
+ Obj_GetFloat64Array,
+ Obj_GetInt32Array,
+ Obj_GetStringArray,
+ Obj_GetObjectArray,
+ Obj_SetAsString,
+ Obj_SetFloat64,
+ Obj_SetInt32,
+ Obj_SetString,
+ Obj_SetObject,
+ Obj_SetFloat64Array,
+ Obj_SetInt32Array,
+ Obj_SetStringArray,
+ Obj_SetObjectArray,
+ Obj_GetIdx,
+ Obj_GetClassName,
+ Obj_GetClassIdx,
+
+ Batch_CreateFromNew,
+ Batch_CreateByRegExp,
+ Batch_CreateByIndex,
+ Batch_CreateByInt32Property,
+ Batch_CreateByClass,
+ Batch_Dispose,
+ Batch_BeginEdit,
+ Batch_EndEdit,
+ Batch_GetPropSeq,
+ Batch_GetFloat64,
+ Batch_GetInt32,
+ Batch_GetString,
+ Batch_GetAsString,
+ Batch_GetObject,
+ // Batch_SetAsString,
+ Batch_Float64,
+ Batch_Int32,
+ Batch_SetString,
+ Batch_SetObject,
+ Batch_SetFloat64Array,
+ Batch_SetInt32Array,
+ Batch_SetStringArray,
+ Batch_SetObjectArray,
+
+ Batch_CreateFromNewS,
+ Batch_CreateByRegExpS,
+ Batch_CreateByIndexS,
+ Batch_CreateByInt32PropertyS,
+ Batch_CreateByClassS,
+ Batch_GetFloat64S,
+ Batch_GetInt32S,
+ Batch_GetStringS,
+ Batch_GetAsStringS,
+ Batch_GetObjectS,
+ // Batch_SetAsStringS,
+ Batch_Float64S,
+ Batch_Int32S,
+ Batch_SetStringS,
+ Batch_SetObjectS,
+ Batch_SetFloat64ArrayS,
+ Batch_SetInt32ArrayS,
+ Batch_SetStringArrayS,
+ Batch_SetObjectArrayS
+
+
{$IFDEF DSS_CAPI_CONTEXT},
{$I '../build/generated/ctx_functions.inc'}
{$ENDIF}
-// // Re-export most KLUSolve functions
-// ,
-// NewSparseSet,
-// GetFlops,
-// ZeroSparseSet,
-// FactorSparseMatrix,
-// SolveSparseSet,
-// DeleteSparseSet,
-// AddMatrixElement,
-// GetSize,
-// GetNNZ,
-// GetSparseNNZ,
-// GetSingularCol,
-// GetRCond,
-// GetRGrowth,
-// GetCondEst,
-// GetMatrixElement,
-// AddPrimitiveMatrix,
-// GetCompressedMatrix,
-// GetTripletMatrix,
-// FindIslands
-//{$IFDEF DSS_CAPI_MVMULT}
-// , mvmult
-//{$ENDIF}
-//{$IFDEF DSS_CAPI_INCREMENTAL_Y}
-// , IncrementMatrixElement
-// , ZeroiseMatrixElement
-//{$ENDIF}
+ // Re-export most KLUSolve functions
+ ,
+ NewSparseSet,
+ GetFlops,
+ ZeroSparseSet,
+ FactorSparseMatrix,
+ SolveSparseSet,
+ DeleteSparseSet,
+ AddMatrixElement,
+ GetSize,
+ GetNNZ,
+ GetSparseNNZ,
+ GetSingularCol,
+ GetRCond,
+ GetRGrowth,
+ GetCondEst,
+ GetMatrixElement,
+ AddPrimitiveMatrix,
+ GetCompressedMatrix,
+ GetTripletMatrix,
+ FindIslands
+{$IFDEF DSS_CAPI_MVMULT}
+ , mvmult
+{$ENDIF}
+{$IFDEF DSS_CAPI_INCREMENTAL_Y}
+ , IncrementMatrixElement
+ , ZeroiseMatrixElement
+{$ENDIF}
;
begin
end.
diff --git a/src/lazutf8/COPYING.LGPL.txt b/src/lazutf8/COPYING.LGPL.txt
new file mode 100644
index 000000000..92b8903ff
--- /dev/null
+++ b/src/lazutf8/COPYING.LGPL.txt
@@ -0,0 +1,481 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ , 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/src/lazutf8/README.md b/src/lazutf8/README.md
new file mode 100644
index 000000000..6b4e496d0
--- /dev/null
+++ b/src/lazutf8/README.md
@@ -0,0 +1,17 @@
+# Notes from DSS C-API
+
+This is a filtered copy of LazUtils from Lazarus components, specifically
+to provide LazUTF8.
+Please see the source files for details about licensing.
+
+You can get a full copy of the Lazarus source code on SourceForge at
+https://sourceforge.net/p/lazarus/laz.git/ci/main/tree/
+
+The specific version used here is from the `lazarus_2_2_0` tag, you can
+grab it with the following git command, on subfolder `components/lazutils`:
+
+ git clone --depth 1 --branch lazarus_2_2_0 https://git.code.sf.net/p/lazarus/laz.git lazarus
+
+Since the SourceForge Git can be slow, the repository is heavy, and Lazarus
+itself is too big to justify installing just for LazUTF8, we decided to
+distribute a copy here.
diff --git a/src/lazutf8/fpcadds.pas b/src/lazutf8/fpcadds.pas
new file mode 100644
index 000000000..aa0fd34dd
--- /dev/null
+++ b/src/lazutf8/fpcadds.pas
@@ -0,0 +1,67 @@
+{
+ *****************************************************************************
+ This file is part of LazUtils.
+
+ See the file COPYING.modifiedLGPL.txt, included in this distribution,
+ for details about the license.
+ *****************************************************************************
+}
+unit FPCAdds;
+
+{$mode objfpc}{$H+}{$inline on}
+
+{$i lazutils_defines.inc}
+
+interface
+
+uses
+ Classes, SysUtils;
+
+type
+ TStreamSeekType = int64;
+ TMemStreamSeekType = PtrInt;
+ TCompareMemSize = PtrUInt;
+ PHandle = ^THandle;
+
+function StrToWord(const s: string): word;
+
+function AlignToPtr(const p: Pointer): Pointer; inline;
+function AlignToInt(const p: Pointer): Pointer; inline;
+
+implementation
+
+function StrToWord(const s: string): word;
+var
+ i: Integer;
+begin
+ Result:=0;
+ for i:=1 to Length(s) do
+ Result:=Result*10+ord(s[i])-ord('0');
+end;
+
+function AlignToPtr(const p: Pointer): Pointer; inline;
+begin
+{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
+ Result := Align(p, SizeOf(Pointer));
+{$ELSE}
+ Result := p;
+{$ENDIF}
+end;
+
+function AlignToInt(const p: Pointer): Pointer; inline;
+begin
+{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
+ Result := Align(p, SizeOf(integer));
+{$ELSE}
+ Result := p;
+{$ENDIF}
+end;
+
+{$ifdef UTF8_RTL}
+initialization
+ SetMultiByteConversionCodePage(CP_UTF8);
+ // SetMultiByteFileSystemCodePage(CP_UTF8); not needed, this is the default under Windows
+ SetMultiByteRTLFileSystemCodePage(CP_UTF8);
+{$IFEND}
+
+end.
diff --git a/src/lazutf8/lazutf8.pas b/src/lazutf8/lazutf8.pas
new file mode 100644
index 000000000..07e413e89
--- /dev/null
+++ b/src/lazutf8/lazutf8.pas
@@ -0,0 +1,4070 @@
+{
+ /***************************************************************************
+ lazutf8.pas
+ ***************************************************************************/
+
+ *****************************************************************************
+ This file is part of LazUtils
+
+ See the file COPYING.modifiedLGPL.txt, included in this distribution,
+ for details about the license.
+ *****************************************************************************
+
+ Useful routines for managing UTF-8 strings
+
+ - all functions are thread safe unless explicitely stated
+}
+unit LazUTF8;
+
+{$mode objfpc}{$H+}{$inline on}
+
+{$i lazutils_defines.inc}
+
+interface
+
+uses
+ {$ifdef unix}
+ // WideCompare* functions on Unix requires this. Must be used although it pulls in clib.
+ cwstring,
+ {$endif}
+ {$IFDEF UTF8_RTL}
+ FPCAdds,
+ {$ENDIF}
+ {$ifdef windows}
+ Windows,
+ {$endif}
+ Classes, SysUtils, StrUtils;
+
+// AnsiToUTF8 and UTF8ToAnsi need a widestring manager under Linux, BSD, MacOSX
+// but normally these OS use UTF-8 as system encoding so the widestringmanager
+// is not needed.
+function NeedRTLAnsi: boolean;// true if system encoding is not UTF-8
+procedure SetNeedRTLAnsi(NewValue: boolean);
+
+// UTF8ToSys works like UTF8ToAnsi but more independent of widestringmanager
+function UTF8ToSys(const s: string): string; overload; {$IFDEF UTF8_RTL}inline;{$ENDIF}
+function UTF8ToSys(const AFormatSettings: TFormatSettings): TFormatSettings; overload; {$IFDEF UTF8_RTL}inline;{$ENDIF}
+
+// SysToUTF8 works like AnsiToUTF8 but more independent of widestringmanager
+function SysToUTF8(const s: string): string; overload; {$IFDEF UTF8_RTL}inline;{$ENDIF}
+function SysToUTF8(const AFormatSettings: TFormatSettings): TFormatSettings; overload;
+
+// converts OEM encoded string to UTF8 (used with some Windows specific functions)
+function ConsoleToUTF8(const s: string): string; {$IFDEF UTF8_RTL}inline;{$ENDIF}
+// converts UTF8 string to console encoding (used by Write, WriteLn)
+function UTF8ToConsole(const s: string): string; {$IFDEF UTF8_RTL}inline;{$ENDIF}
+
+// for all Windows supporting 8bit codepages (e.g. not WinCE)
+// converts string in Windows code page to UTF8 (used with some Windows specific functions)
+function WinCPToUTF8(const s: string): string; {$ifdef WinCe}inline;{$endif}
+// converts UTF8 string to Windows code page encoding (used by Write, WriteLn)
+function UTF8ToWinCP(const s: string): string; {$ifdef WinCe}inline;{$endif}
+
+function ParamStrUTF8(Param: Integer): string;
+
+{$ifdef windows}
+procedure GetFormatSettingsUTF8;
+procedure GetLocaleFormatSettingsUTF8(LCID: Integer; var aFormatSettings: TFormatSettings);
+{$endif}
+
+Function GetEnvironmentVariableCountUTF8: Integer; inline;
+function GetEnvironmentStringUTF8(Index: Integer): string; inline;
+function GetEnvironmentVariableUTF8(const EnvVar: string): String;
+function SysErrorMessageUTF8(ErrorCode: Integer): String;
+
+// Returns the size of one codepoint in bytes.
+function UTF8CodepointSize(p: PChar): integer; inline;
+function UTF8CharacterLength(p: PChar): integer; deprecated 'Use UTF8CodepointSize instead.';
+// Fast version of UTF8CodepointSize. Assumes the UTF-8 codepoint is valid.
+function UTF8CodepointSizeFast(p: PChar): integer; inline;
+
+function UTF8Length(const s: string): PtrInt; inline;
+function UTF8Length(p: PChar; ByteCount: PtrInt): PtrInt;
+// Fast versions of UTF8Length. They assume the UTF-8 data is valid.
+function UTF8LengthFast(const s: string): PtrInt; inline;
+function UTF8LengthFast(p: PChar; ByteCount: PtrInt): PtrInt;
+
+// Functions dealing with unicode number U+xxx.
+function UTF8CodepointToUnicode(p: PChar; out CodepointLen: integer): Cardinal;
+function UTF8CharacterToUnicode(p: PChar; out CharLen: integer): Cardinal; deprecated 'Use UTF8CodepointToUnicode instead.';
+function UnicodeToUTF8(CodePoint: cardinal): string; // UTF32 to UTF8
+function UnicodeToUTF8(CodePoint: cardinal; Buf: PChar): integer; // UTF32 to UTF8
+function UnicodeToUTF8SkipErrors(CodePoint: cardinal; Buf: PChar): integer; inline; // UTF32 to UTF8
+function UnicodeToUTF8Inline(CodePoint: cardinal; Buf: PChar): integer; inline; // UTF32 to UTF8
+function UTF8ToDoubleByteString(const s: string): string;
+function UTF8ToDoubleByte(UTF8Str: PChar; Len: PtrInt; DBStr: PByte): PtrInt;
+function UTF8FindNearestCharStart(UTF8Str: PChar; Len: SizeInt;
+ BytePos: SizeInt): SizeInt;
+function Utf8TryFindCodepointStart(AString: PChar; var CurPos: PChar; out CodepointLen: Integer): Boolean;
+function Utf8TryFindCodepointStart(const AString: String; var Index: Integer; out CharLen: Integer): Boolean;
+// find the n-th UTF8 codepoint, ignoring BIDI
+function UTF8CodepointStart(UTF8Str: PChar; Len, CodepointIndex: PtrInt): PChar;
+function UTF8CharStart(UTF8Str: PChar; Len, CharIndex: PtrInt): PChar; deprecated 'Use UTF8CodepointStart instead.';
+// find the byte index of the n-th UTF8 codepoint, ignoring BIDI (byte len of substr)
+function UTF8CodepointToByteIndex(UTF8Str: PChar; Len, CodepointIndex: PtrInt): PtrInt;
+function UTF8CharToByteIndex(UTF8Str: PChar; Len, CharIndex: PtrInt): PtrInt; deprecated 'Use UTF8CodepointToByteIndex instead.';
+procedure UTF8FixBroken(P: PChar); overload;
+procedure UTF8FixBroken(var S: string); overload;
+function UTF8CodepointStrictSize(P: PChar): integer;
+function UTF8CharacterStrictLength(P: PChar): integer; deprecated 'Use UTF8CodepointStrictSize instead.';
+function UTF8CStringToUTF8String(SourceStart: PChar; SourceLen: PtrInt) : string;
+
+function UTF8Pos(const SearchForText, SearchInText: string; StartPos: SizeInt = 1): PtrInt;
+function UTF8PosP(SearchForText: PChar; SearchForTextLen: SizeInt;
+ SearchInText: PChar; SearchInTextLen: SizeInt): PChar;
+function UTF8Copy(const s: string; StartCharIndex, CharCount: PtrInt): string;
+procedure UTF8Delete(var s: Utf8String; StartCharIndex, CharCount: PtrInt);
+procedure UTF8Delete(var s: String; StartCharIndex, CharCount: PtrInt);
+procedure UTF8Insert(const source: Utf8String; var s: Utf8String; StartCharIndex: PtrInt);
+procedure UTF8Insert(const source: String; var s: String; StartCharIndex: PtrInt);
+function UTF8StringReplace(const S, OldPattern, NewPattern: String;
+ Flags: TReplaceFlags; ALanguage: string=''): String; inline;
+function UTF8StringReplace(const S, OldPattern, NewPattern: String;
+ Flags: TReplaceFlags; out Count: Integer; ALanguage: string=''): String;
+
+function UTF8LowerCase(const AInStr: string; ALanguage: string=''): string;
+function UTF8LowerString(const s: string): string; inline;
+function UTF8UpperCase(const AInStr: string; ALanguage: string=''): string;
+function UTF8UpperString(const s: string): string; inline;
+function UTF8SwapCase(const AInStr: string; ALanguage: string=''): string;
+// Capitalize the first letters of every word
+function UTF8ProperCase(const AInStr: string; const WordDelims: TSysCharSet): string;
+function FindInvalidUTF8Codepoint(p: PChar; Count: PtrInt; StopOnNonUTF8: Boolean = true): PtrInt;
+function FindInvalidUTF8Character(p: PChar; Count: PtrInt; StopOnNonUTF8: Boolean = true): PtrInt; deprecated 'Use FindInvalidUTF8Codepoint instead.';
+function UTF8StringOfChar(AUtf8Char: String; N: Integer): String;
+function UTF8AddChar(AUtf8Char: String; const S: String; N: Integer): String;
+function UTF8AddCharR(AUtf8Char: String; const S: String; N: Integer): String;
+function UTF8PadLeft(const S: String; const N: Integer; const AUtf8Char: String = #32): String; inline;
+function UTF8PadRight(const S: String; const N: Integer; const AUtf8Char: String = #32): String; inline;
+function UTF8PadCenter(const S: String; const N: Integer; const AUtf8Char: String = #32): String;
+function UTF8LeftStr(const AText: String; const ACount: Integer): String; inline;
+function UTF8RightStr(const AText: String; const ACount: Integer): String;
+function UTF8QuotedStr(const S, Quote: string): string;
+//Utf8 version of MidStr is just Utf8Copy with same parameters, so it is not implemented here
+function UTF8StartsText(const ASubText, AText: string): Boolean;
+function UTF8EndsText(const ASubText, AText: string): Boolean;
+function UTF8ReverseString(p: PChar; const ByteCount: LongInt): string;
+function UTF8ReverseString(const AText: string): string; inline;
+function UTF8RPos(const Substr, Source: string): PtrInt;
+
+function UTF8WrapText(S, BreakStr: string; BreakChars: TSysCharSet; MaxCol: integer): string; overload;
+function UTF8WrapText(S: string; MaxCol: integer): string; overload;
+
+type
+ TEscapeMode = (emPascal, emHexPascal, emHexC, emC, emAsciiControlNames);
+
+function Utf8EscapeControlChars(S: String; EscapeMode: TEscapeMode = emPascal): String;
+
+type
+ TUTF8TrimFlag = (
+ u8tKeepStart,
+ u8tKeepEnd,
+ u8tKeepTabs,
+ u8tKeepLineBreaks,
+ u8tKeepNoBreakSpaces,
+ u8tKeepControlCodes // excluding tabs and line breaks
+ );
+ TUTF8TrimFlags = set of TUTF8TrimFlag;
+function UTF8Trim(const s: string; Flags: TUTF8TrimFlags = []): string;
+
+//compare functions
+
+function UTF8CompareStr(const S1, S2: string): PtrInt; inline;
+function UTF8CompareStrP(S1, S2: PChar): PtrInt;
+function UTF8CompareStr(S1: PChar; Count1: SizeInt; S2: PChar; Count2: SizeInt): PtrInt;
+function UTF8CompareText(const S1, S2: string): PtrInt;
+function UTF8CompareTextP(S1, S2: PChar): PtrInt;
+function UTF8CompareLatinTextFast(const S1, S2: String): PtrInt;
+function UTF8CompareStrCollated(const S1, S2: string): PtrInt; {$IFnDEF ACP_RTL}inline;{$endif}
+function CompareStrListUTF8LowerCase(List: TStringList; Index1, Index2: Integer): Integer;
+
+type
+
+ { TStringListUTF8Fast }
+
+ TStringListUTF8Fast = class(TStringList)
+ protected // Uses UTF8CompareLatinTextFast for comparison.
+ function DoCompareText(const s1,s2 : string): PtrInt; override;
+ end;
+
+ TConvertResult = (trNoError, trNullSrc, trNullDest, trDestExhausted,
+ trInvalidChar, trUnfinishedChar);
+
+ TConvertOption = (toInvalidCharError, toInvalidCharToSymbol,
+ toUnfinishedCharError, toUnfinishedCharToSymbol);
+ TConvertOptions = set of TConvertOption;
+
+function ConvertUTF8ToUTF16(Dest: PWideChar; DestWideCharCount: SizeUInt;
+ Src: PChar; SrcCharCount: SizeUInt; Options: TConvertOptions;
+ out ActualWideCharCount: SizeUInt): TConvertResult;
+
+function ConvertUTF16ToUTF8(Dest: PChar; DestCharCount: SizeUInt;
+ Src: PWideChar; SrcWideCharCount: SizeUInt; Options: TConvertOptions;
+ out ActualCharCount: SizeUInt): TConvertResult;
+
+function UTF8ToUTF16(const S: AnsiString): UnicodeString; overload; inline;
+function UTF8ToUTF16(const P: PChar; ByteCnt: SizeUInt): UnicodeString; overload;
+function UTF16ToUTF8(const S: UnicodeString): AnsiString; overload; inline;
+function UTF16ToUTF8(const P: PWideChar; WideCnt: SizeUInt): AnsiString; overload;
+
+// locale
+procedure LazGetLanguageIDs(var Lang, FallbackLang: String);
+procedure LazGetShortLanguageID(var Lang: String);
+
+var
+ FPUpChars: array[char] of char;
+
+procedure ReplaceSubstring(var s: string; StartPos, Count: SizeInt;
+ const Insertion: string); deprecated 'Use it from unit LazStringUtils'; // Deprecated in 2.1 / 29.10.2020 / Remove in 2.3
+
+implementation
+
+uses
+ gettext
+{$IFDEF Darwin}, MacOSAll{$ENDIF}
+ ;
+
+{$IFDEF WinCE}
+// CP_UTF8 is missing in the windows unit of the Windows CE RTL
+const
+ CP_UTF8 = 65001;
+{$ENDIF}
+
+function IsASCII(const s: string): boolean; inline;
+var
+ i: Integer;
+begin
+ for i:=1 to length(s) do if ord(s[i])>127 then exit(false);
+ Result:=true;
+end;
+
+{$IFDEF windows}
+ {$i winlazutf8.inc}
+{$ELSE}
+ {$IFDEF HASAMIGA}
+ {$i unixlazutf8.inc} // Reuse UNIX code for Amiga
+ {$ELSE}
+ {$i unixlazutf8.inc}
+ {$ENDIF}
+{$ENDIF}
+
+var
+ FNeedRTLAnsi: boolean = false;
+ FNeedRTLAnsiValid: boolean = false;
+
+function NeedRTLAnsi: boolean;
+{$IFNDEF Windows}
+var
+ Lang: String;
+ i: LongInt;
+ Encoding: String;
+{$ENDIF}
+begin
+ if FNeedRTLAnsiValid then
+ exit(FNeedRTLAnsi);
+ {$IFDEF Windows}
+ {$IF FPC_FULLVERSION>=20701}
+ FNeedRTLAnsi:=DefaultSystemCodePage<>CP_UTF8;
+ {$ELSE}
+ FNeedRTLAnsi:=GetACP<>CP_UTF8;
+ {$ENDIF}
+ {$ELSE}
+ FNeedRTLAnsi:=false;
+ Lang := SysUtils.GetEnvironmentVariable('LC_ALL');
+ if lang = '' then
+ begin
+ Lang := SysUtils.GetEnvironmentVariable('LC_MESSAGES');
+ if Lang = '' then
+ begin
+ Lang := SysUtils.GetEnvironmentVariable('LANG');
+ end;
+ end;
+ i:=System.Pos('.',Lang);
+ if (i>0) then begin
+ Encoding:=copy(Lang,i+1,length(Lang)-i);
+ FNeedRTLAnsi:=(SysUtils.CompareText(Encoding,'UTF-8')<>0)
+ and (SysUtils.CompareText(Encoding,'UTF8')<>0);
+ end;
+ {$ENDIF}
+ FNeedRTLAnsiValid:=true;
+ Result:=FNeedRTLAnsi;
+end;
+
+procedure SetNeedRTLAnsi(NewValue: boolean);
+begin
+ FNeedRTLAnsi:=NewValue;
+ FNeedRTLAnsiValid:=true;
+end;
+
+function UTF8ToSys(const s: string): string;
+begin
+ {$IFDEF UTF8_RTL}
+ Result:=s;
+ {$ELSE}
+ if NeedRTLAnsi and (not IsASCII(s)) then
+ Result:=UTF8ToAnsi(s)
+ else
+ Result:=s;
+ {$ENDIF}
+end;
+
+function SysToUTF8(const s: string): string;
+begin
+ {$IFDEF UTF8_RTL}
+ Result:=s;
+ {$ELSE}
+ if NeedRTLAnsi and (not IsASCII(s)) then
+ begin
+ Result:=AnsiToUTF8(s);
+ {$ifdef FPC_HAS_CPSTRING}
+ // prevent UTF8 codepage appear in the strings - we don't need codepage
+ // conversion magic in LCL code
+ SetCodePage(RawByteString(Result), StringCodePage(s), False);
+ {$endif}
+ end
+ else
+ Result:=s;
+ {$ENDIF}
+end;
+
+function SysToUTF8(const AFormatSettings: TFormatSettings): TFormatSettings;
+{$IFNDEF UTF8_RTL}
+var
+ i: Integer;
+{$ENDIF}
+begin
+ Result := AFormatSettings;
+ {$IFNDEF UTF8_RTL}
+ Result.CurrencyString := SysToUTF8(AFormatSettings.CurrencyString);
+ for i:=1 to 12 do begin
+ Result.LongMonthNames[i] := SysToUTF8(AFormatSettings.LongMonthNames[i]);
+ Result.ShortMonthNames[i] := SysToUTF8(AFormatSettings.ShortMonthNames[i]);
+ end;
+ for i:=1 to 7 do begin
+ Result.LongDayNames[i] := SysToUTF8(AFormatSettings.LongDayNames[i]);
+ Result.ShortDayNames[i] := SysToUTF8(AFormatSettings.ShortDayNames[i]);
+ end;
+ {$ENDIF}
+end;
+
+function UTF8ToSys(const AFormatSettings: TFormatSettings): TFormatSettings;
+{$IFnDEF UTF8_RTL}
+var
+ i: Integer;
+{$ENDIF}
+begin
+ Result := AFormatSettings;
+ {$IFnDEF UTF8_RTL}
+ Result.CurrencyString := UTF8ToSys(AFormatSettings.CurrencyString);
+ for i:=1 to 12 do begin
+ Result.LongMonthNames[i] := UTF8ToSys(AFormatSettings.LongMonthNames[i]);
+ Result.ShortMonthNames[i] := UTF8ToSys(AFormatSettings.ShortMonthNames[i]);
+ end;
+ for i:=1 to 7 do begin
+ Result.LongDayNames[i] := UTF8ToSys(AFormatSettings.LongDayNames[i]);
+ Result.ShortDayNames[i] := UTF8ToSys(AFormatSettings.ShortDayNames[i]);
+ end;
+ {$ENDIF}
+end;
+
+function GetEnvironmentVariableCountUTF8: Integer; inline;
+begin
+ {$IF defined(FPC_RTL_UNICODE) or not defined(MSWindows)} //also WinCE, issue #0031788
+ Result:=SysUtils.GetEnvironmentVariableCount;
+ {$ELSE}
+ Result:=GetGetEnvironmentVariableCountWide;
+ {$ENDIF}
+end;
+
+function GetEnvironmentStringUTF8(Index: Integer): string; inline;
+begin
+ {$IFDEF FPC_RTL_UNICODE}
+ Result:=UTF16ToUTF8(SysUtils.GetEnvironmentString(Index));
+ {$ELSE}
+ {$IFDEF MSWindows} //not for WinCE, issue #0031788
+ Result:=UTF16ToUTF8(GetEnvironmentStringWide(Index));
+ {$ELSE}
+ // by default the environment is in console encoding
+ // see also RTL issue: http://bugs.freepascal.org/view.php?id=15233
+ Result:=ConsoleToUTF8(SysUtils.GetEnvironmentString(Index));
+ {$ENDIF}
+ {$ENDIF}
+end;
+
+function GetEnvironmentVariableUTF8(const EnvVar: string): String;
+begin
+ {$IFDEF FPC_RTL_UNICODE}
+ Result:=UTF16ToUTF8(SysUtils.GetEnvironmentVariable(UTF8ToUTF16(EnvVar)));
+ {$ELSE}
+ {$IFDEF Windows}
+ Result:=UTF16ToUTF8(GetEnvironmentVariableWide(EnvVar));
+ {$ELSE}
+ // by default the environment is in console encoding
+ // RTL issue: http://bugs.freepascal.org/view.php?id=15233
+ Result:=ConsoleToUTF8(SysUtils.GetEnvironmentVariable(UTF8ToSys(EnvVar)));
+ {$ENDIF}
+ {$ENDIF}
+end;
+
+function SysErrorMessageUTF8(ErrorCode: Integer): String;
+begin
+ Result := SysToUTF8(SysUtils.SysErrorMessage(ErrorCode));
+end;
+
+function UTF8CodepointSizeFull(p: PChar): integer;
+begin
+ case p^ of
+ #0..#191: // %11000000
+ // regular single byte character (#0 is a character, this is Pascal ;)
+ Result:=1;
+ #192..#223: // p^ and %11100000 = %11000000
+ begin
+ // could be 2 byte character
+ if (ord(p[1]) and %11000000) = %10000000 then
+ Result:=2
+ else
+ Result:=1;
+ end;
+ #224..#239: // p^ and %11110000 = %11100000
+ begin
+ // could be 3 byte character
+ if ((ord(p[1]) and %11000000) = %10000000)
+ and ((ord(p[2]) and %11000000) = %10000000) then
+ Result:=3
+ else
+ Result:=1;
+ end;
+ #240..#247: // p^ and %11111000 = %11110000
+ begin
+ // could be 4 byte character
+ if ((ord(p[1]) and %11000000) = %10000000)
+ and ((ord(p[2]) and %11000000) = %10000000)
+ and ((ord(p[3]) and %11000000) = %10000000) then
+ Result:=4
+ else
+ Result:=1;
+ end;
+ else
+ Result:=1;
+ end;
+end;
+
+function UTF8CodepointSize(p: PChar): integer; inline;
+begin
+ if p=nil then exit(0);
+ if p^<#192 then exit(1);
+ Result:=UTF8CodepointSizeFull(p);
+end;
+
+function UTF8CharacterLength(p: PChar): integer;
+begin
+ Result := UTF8CodepointSize(p);
+end;
+
+function UTF8CodepointSizeFast(p: PChar): integer;
+begin
+ case p^ of
+ #0..#191 : Result := 1;
+ #192..#223 : Result := 2;
+ #224..#239 : Result := 3;
+ #240..#247 : Result := 4;
+ //#248..#255 : Result := 1;
+ // Theoretically UTF-8 supports length 1-7, but since 2003, RFC 3629 limits
+ // it to 1-4 bytes.
+ // This is an inline function, so keep the function short.
+ //#248..#251 : Result := 5;
+ //#252, #253 : Result := 6;
+ //#254 : Result := 7;
+
+ else Result := 1; // An optimization + prevents compiler warning about uninitialized Result.
+ end;
+end;
+
+function UTF8Length(const s: string): PtrInt;
+begin
+ Result:=UTF8Length(PChar(s),length(s));
+end;
+
+function UTF8Length(p: PChar; ByteCount: PtrInt): PtrInt;
+var
+ CharLen: LongInt;
+begin
+ Result:=0;
+ while (ByteCount>0) do begin
+ inc(Result);
+ CharLen:=UTF8CodepointSize(p);
+ inc(p,CharLen);
+ dec(ByteCount,CharLen);
+ end;
+end;
+
+function UTF8LengthFast(const s: string): PtrInt;
+begin
+ Result := UTF8LengthFast(PChar(s), Length(s));
+end;
+
+// Ported from:
+// http://www.daemonology.net/blog/2008-06-05-faster-utf8-strlen.html
+// The code uses CPU's native data size. In a 64-bit CPU it means 8 bytes at once.
+// The UTF-8 data is assumed to be valid.
+function UTF8LengthFast(p: PChar; ByteCount: PtrInt): PtrInt;
+const
+{$ifdef CPU32}
+ ONEMASK =$01010101;
+ EIGHTYMASK=$80808080;
+{$endif}
+{$ifdef CPU64}
+ ONEMASK =$0101010101010101;
+ EIGHTYMASK=$8080808080808080;
+{$endif}
+var
+ pnx: PPtrInt absolute p; // To get contents of text in PtrInt blocks. x refers to 32 or 64 bits
+ pn8: pint8 absolute pnx; // To read text as Int8 in the initial and final loops
+ ix: PtrInt absolute pnx; // To read text as PtrInt in the block loop
+ nx: PtrInt; // values processed in block loop
+ i,cnt,e: PtrInt;
+begin
+ Result := 0;
+ e := ix+ByteCount; // End marker
+ // Handle any initial misaligned bytes.
+ cnt := (not (ix-1)) and (sizeof(PtrInt)-1);
+ if cnt>ByteCount then
+ cnt := ByteCount;
+ for i := 1 to cnt do
+ begin
+ // Is this byte NOT the first byte of a character?
+ Result += (pn8^ shr 7) and ((not pn8^) shr 6);
+ inc(pn8);
+ end;
+ // Handle complete blocks
+ for i := 1 to (ByteCount-cnt) div sizeof(PtrInt) do
+ begin
+ // Count bytes which are NOT the first byte of a character.
+ nx := ((pnx^ and EIGHTYMASK) shr 7) and ((not pnx^) shr 6);
+ {$push}{$overflowchecks off} // "nx * ONEMASK" causes an arithmetic overflow.
+ Result += (nx * ONEMASK) >> ((sizeof(PtrInt) - 1) * 8);
+ {$pop}
+ inc(pnx);
+ end;
+ // Take care of any left-over bytes.
+ while ix0
+ If there is an encoding error the Result is 0 and CodepointLen=1.
+ Use UTF8FixBroken to fix UTF-8 encoding.
+ It does not check if the codepoint is defined in the Unicode tables.
+}
+begin
+ if p<>nil then begin
+ if ord(p^)<%11000000 then begin
+ // regular single byte character (#0 is a normal char, this is pascal ;)
+ Result:=ord(p^);
+ CodepointLen:=1;
+ end
+ else if ((ord(p^) and %11100000) = %11000000) then begin
+ // starts with %110 => could be double byte character
+ if (ord(p[1]) and %11000000) = %10000000 then begin
+ CodepointLen:=2;
+ Result:=((ord(p^) and %00011111) shl 6) or (ord(p[1]) and %00111111);
+ if Result<(1 shl 7) then begin
+ // wrong encoded, could be an XSS attack
+ Result:=0;
+ end;
+ end else begin
+ Result:=ord(p^);
+ CodepointLen:=1;
+ end;
+ end
+ else if ((ord(p^) and %11110000) = %11100000) then begin
+ // starts with %1110 => could be triple byte character
+ if ((ord(p[1]) and %11000000) = %10000000)
+ and ((ord(p[2]) and %11000000) = %10000000) then begin
+ CodepointLen:=3;
+ Result:=((ord(p^) and %00011111) shl 12)
+ or ((ord(p[1]) and %00111111) shl 6)
+ or (ord(p[2]) and %00111111);
+ if Result<(1 shl 11) then begin
+ // wrong encoded, could be an XSS attack
+ Result:=0;
+ end;
+ end else begin
+ Result:=ord(p^);
+ CodepointLen:=1;
+ end;
+ end
+ else if ((ord(p^) and %11111000) = %11110000) then begin
+ // starts with %11110 => could be 4 byte character
+ if ((ord(p[1]) and %11000000) = %10000000)
+ and ((ord(p[2]) and %11000000) = %10000000)
+ and ((ord(p[3]) and %11000000) = %10000000) then begin
+ CodepointLen:=4;
+ Result:=((ord(p^) and %00001111) shl 18)
+ or ((ord(p[1]) and %00111111) shl 12)
+ or ((ord(p[2]) and %00111111) shl 6)
+ or (ord(p[3]) and %00111111);
+ if Result<(1 shl 16) then begin
+ // wrong encoded, could be an XSS attack
+ Result:=0;
+ end else if Result>$10FFFF then begin
+ // out of range
+ Result:=0;
+ end;
+ end else begin
+ Result:=ord(p^);
+ CodepointLen:=1;
+ end;
+ end
+ else begin
+ // invalid character
+ Result:=ord(p^);
+ CodepointLen:=1;
+ end;
+ end else begin
+ Result:=0;
+ CodepointLen:=0;
+ end;
+end;
+
+function UTF8CharacterToUnicode(p: PChar; out CharLen: integer): Cardinal;
+begin
+ Result := UTF8CodepointToUnicode(p, CharLen);
+end;
+
+function UnicodeToUTF8(CodePoint: cardinal; Buf: PChar): integer;
+
+ procedure RaiseInvalidUnicode;
+ begin
+ raise Exception.Create('UnicodeToUTF8: invalid unicode: '+IntToStr(CodePoint));
+ end;
+
+begin
+ Result:=UnicodeToUTF8Inline(CodePoint,Buf);
+ if Result=0 then
+ RaiseInvalidUnicode;
+end;
+
+function UnicodeToUTF8SkipErrors(CodePoint: cardinal; Buf: PChar): integer; inline;
+begin
+ Result:=UnicodeToUTF8Inline(CodePoint,Buf);
+end;
+
+function UnicodeToUTF8(CodePoint: cardinal): string;
+var
+ Buf: array[0..6] of Char;
+ Len: Integer;
+begin
+ if (CodePoint = 0) then
+ Result := #0 //StrPas does not like #0
+ else
+ begin
+ Len:=UnicodeToUTF8Inline(CodePoint, @Buf[0]);
+ Buf[Len]:=#0;
+ Result := StrPas(@Buf[0]);
+ end;
+end;
+
+function UnicodeToUTF8Inline(CodePoint: cardinal; Buf: PChar): integer;
+begin
+ case CodePoint of
+ 0..$7f:
+ begin
+ Result:=1;
+ Buf[0]:=char(byte(CodePoint));
+ end;
+ $80..$7ff:
+ begin
+ Result:=2;
+ Buf[0]:=char(byte($c0 or (CodePoint shr 6)));
+ Buf[1]:=char(byte($80 or (CodePoint and $3f)));
+ end;
+ $800..$ffff:
+ begin
+ Result:=3;
+ Buf[0]:=char(byte($e0 or (CodePoint shr 12)));
+ Buf[1]:=char(byte((CodePoint shr 6) and $3f) or $80);
+ Buf[2]:=char(byte(CodePoint and $3f) or $80);
+ end;
+ $10000..$10ffff:
+ begin
+ Result:=4;
+ Buf[0]:=char(byte($f0 or (CodePoint shr 18)));
+ Buf[1]:=char(byte((CodePoint shr 12) and $3f) or $80);
+ Buf[2]:=char(byte((CodePoint shr 6) and $3f) or $80);
+ Buf[3]:=char(byte(CodePoint and $3f) or $80);
+ end;
+ else
+ Result:=0;
+ end;
+end;
+
+function UTF8ToDoubleByteString(const s: string): string;
+var
+ Len: Integer;
+begin
+ Len:=UTF8Length(s);
+ SetLength(Result{%H-},Len*2);
+ if Len=0 then exit;
+ UTF8ToDoubleByte(PChar(s),length(s),PByte(Result));
+end;
+
+{ returns number of double bytes }
+function UTF8ToDoubleByte(UTF8Str: PChar; Len: PtrInt; DBStr: PByte): PtrInt;
+var
+ SrcPos: PChar;
+ CharLen: LongInt;
+ DestPos: PByte;
+ u: Cardinal;
+begin
+ SrcPos:=UTF8Str;
+ DestPos:=DBStr;
+ Result:=0;
+ while Len>0 do begin
+ u:=UTF8CodepointToUnicode(SrcPos,CharLen);
+ DestPos^:=byte((u shr 8) and $ff);
+ inc(DestPos);
+ DestPos^:=byte(u and $ff);
+ inc(DestPos);
+ inc(SrcPos,CharLen);
+ dec(Len,CharLen);
+ inc(Result);
+ end;
+end;
+
+
+{ Tries to find the start of a valid UTF8 codepoint that contains the character pointed to by CurPos
+ - AString: pointer to the (start of the) string
+ - CurPos: pointer to the character inside AString that we want to get the information off
+ * if the function succeeds, CurPos wil point to the start of the valid UTF8 codepoint
+ * if the function fails, CurPos will not be changed
+ Note: if CurPos points beyond the end of AString you will get a crash!
+ - CharLen: the length of the UTF8 codepoint in bytes, if the function succeeds
+ - Returns:
+ True if the character pointed to by Curpos is part of a valid UTF8 codepoint (1 to 4 bytes),
+ otherwise it returns False. }
+function Utf8TryFindCodepointStart(AString: PChar; var CurPos: PChar; out CodepointLen: Integer): Boolean;
+var
+ SavedPos: PChar;
+begin
+ Result := False;
+ CodepointLen := 0;
+ if (not (Assigned(AString) and Assigned(CurPos)))
+ or (CurPos < AString) then Exit;
+ SavedPos := CurPos;
+ //Note: UTF8CodepointStrictSize will NOT "look" beyond the terminating #0 of a PChar, so this is safe with AnsiStrings
+ CodepointLen := UTF8CodepointStrictSize(CurPos);
+ if (CodepointLen > 0) then Exit(True);
+ if (CurPos > AString) then
+ begin
+ Dec(CurPos); //-1
+ //is it second byte of 2..4 byte codepoint?
+ CodepointLen := UTF8CodepointStrictSize(CurPos);
+ if (CodepointLen > 1) then Exit(True);
+ if (CurPos > AString) then
+ begin
+ Dec(CurPos); //-2
+ //is it third byte of 3..4 byte codepoint?
+ CodepointLen := UTF8CodepointStrictSize(CurPos);
+ if (CodepointLen > 2) then Exit(True);
+ if (CurPos > AString) then
+ begin
+ Dec(CurPos); //-3
+ //is it fouth byte of 4 byte codepoint?
+ CodepointLen := UTF8CodepointStrictSize(CurPos);
+ if (CodepointLen = 4) then Exit(True);
+ end;
+ end;
+ end;
+ //At this point we failed: we are NOT inside a valid UTF8 codepoint!
+ CurPos := SavedPos;
+end;
+
+function Utf8TryFindCodepointStart(const AString: String; var Index: Integer; out CharLen: Integer): Boolean;
+var
+ CurPos, SavedCurPos: PChar;
+begin
+ CurPos := @AString[Index];
+ SavedCurPos := CurPos;
+ Result := Utf8TryFindCodepointStart(PChar(AString), CurPos, CharLen);
+ Index := Index - (SavedCurPos - CurPos);
+end;
+
+{ Find the start of the UTF8 character which contains BytePos,
+ if BytePos is not part of a valid Utf8 Codepoint the function returns BytePos
+ Len is length in byte, BytePos starts at 0 }
+function UTF8FindNearestCharStart(UTF8Str: PChar; Len: SizeInt; BytePos: SizeInt): SizeInt;
+var
+ CurPos: PChar;
+ CharLen: Integer;
+begin
+ if (BytePos > Len-1) then BytePos := Len - 1;
+ CurPos := Utf8Str + BytePos;
+ //No need to check the result value, since when it retuns False CurPos will be reset
+ //to it's original value, and that's what we want to return in that case
+ Utf8TryFindCodepointStart(Utf8Str, CurPos, CharLen);
+ Result := CurPos - Utf8Str;
+end;
+
+
+{ Len is the length in bytes of UTF8Str
+ CodepointIndex is the position of the desired codepoint (starting at 0), in chars
+}
+function UTF8CodepointStart(UTF8Str: PChar; Len, CodepointIndex: PtrInt): PChar;
+var
+ CharLen: LongInt;
+begin
+ Result:=UTF8Str;
+ if Result<>nil then begin
+ while (CodepointIndex>0) and (Len>0) do begin
+ CharLen:=UTF8CodepointSize(Result);
+ dec(Len,CharLen);
+ dec(CodepointIndex);
+ inc(Result,CharLen);
+ end;
+ if (CodepointIndex<>0) or (Len<0) then
+ Result:=nil;
+ end;
+end;
+
+function UTF8CharStart(UTF8Str: PChar; Len, CharIndex: PtrInt): PChar;
+begin
+ Result := UTF8CodepointStart(UTF8Str, Len, CharIndex);
+end;
+
+function UTF8CodepointToByteIndex(UTF8Str: PChar; Len, CodepointIndex: PtrInt): PtrInt;
+var
+ p: PChar;
+begin
+ p := UTF8CodepointStart(UTF8Str, Len, CodepointIndex);
+ if p = nil
+ then Result := -1
+ else Result := p - UTF8Str;
+end;
+
+function UTF8CharToByteIndex(UTF8Str: PChar; Len, CharIndex: PtrInt): PtrInt;
+begin
+ Result := UTF8CodepointToByteIndex(UTF8Str, Len, CharIndex);
+end;
+
+{ fix any broken UTF8 sequences with spaces }
+procedure UTF8FixBroken(P: PChar);
+var
+ b: byte;
+ c: cardinal;
+begin
+ if p=nil then exit;
+ while p^<>#0 do begin
+ b:=ord(p^);
+ if b<%10000000 then begin
+ // regular single byte character
+ inc(p);
+ end
+ else if b<%11000000 then begin
+ // invalid
+ p^:=' ';
+ inc(p);
+ end
+ else if (b and %11100000) = %11000000 then begin
+ // starts with %110 => should be 2 byte character
+ if ((ord(p[1]) and %11000000) = %10000000) then begin
+ if b<%11000010 then
+ p^:=' ' // fix XSS attack
+ else
+ inc(p,2)
+ end
+ else
+ p^:=' ';
+ end
+ else if (b and %11110000) = %11100000 then begin
+ // starts with %1110 => should be 3 byte character
+ if ((ord(p[1]) and %11000000) = %10000000)
+ and ((ord(p[2]) and %11000000) = %10000000) then begin
+ c:=((ord(p^) and %00011111) shl 12)
+ or ((ord(p[1]) and %00111111) shl 6);
+ //or (ord(p[2]) and %00111111);
+ if c<(1 shl 11) then
+ p^:=' ' // fix XSS attack
+ else
+ inc(p,3);
+ end else
+ p^:=' ';
+ end
+ else if (b and %11111000) = %11110000 then begin
+ // starts with %11110 => should be 4 byte character
+ if ((ord(p[1]) and %11000000) = %10000000)
+ and ((ord(p[2]) and %11000000) = %10000000)
+ and ((ord(p[3]) and %11000000) = %10000000) then begin
+ c:=((ord(p^) and %00001111) shl 18)
+ or ((ord(p[1]) and %00111111) shl 12)
+ or ((ord(p[2]) and %00111111) shl 6);
+ //or (ord(p[3]) and %00111111);
+ if c<(1 shl 16) then
+ p^:=' ' // fix XSS attack
+ else if (c>$10FFFF) then
+ p^:=' ' // out of range U+10FFFF
+ else
+ inc(p,4)
+ end else
+ p^:=' ';
+ end
+ else begin
+ p^:=' ';
+ inc(p);
+ end;
+ end;
+end;
+
+procedure UTF8FixBroken(var S: string);
+begin
+ if S='' then exit;
+ if FindInvalidUTF8Codepoint(PChar(S),length(S))<0 then exit;
+ UniqueString(S);
+ UTF8FixBroken(PChar(S));
+end;
+
+function UTF8CodepointStrictSize(P: PChar): integer;
+var
+ c: Char;
+begin
+ if p=nil then exit(0);
+ c:=p^;
+ if ord(c)<%10000000 then begin
+ // regular single byte character
+ exit(1);
+ end
+ else if ord(c)<%11000000 then begin
+ // invalid single byte character
+ exit(0);
+ end
+ else if ((ord(c) and %11100000) = %11000000) then begin
+ // should be 2 byte character
+ if (ord(p[1]) and %11000000) = %10000000 then
+ exit(2)
+ else
+ exit(0);
+ end
+ else if ((ord(c) and %11110000) = %11100000) then begin
+ // should be 3 byte character
+ if ((ord(p[1]) and %11000000) = %10000000)
+ and ((ord(p[2]) and %11000000) = %10000000) then
+ exit(3)
+ else
+ exit(0);
+ end
+ else if ((ord(c) and %11111000) = %11110000) then begin
+ // should be 4 byte character
+ if ((ord(p[1]) and %11000000) = %10000000)
+ and ((ord(p[2]) and %11000000) = %10000000)
+ and ((ord(p[3]) and %11000000) = %10000000) then
+ exit(4)
+ else
+ exit(0);
+ end else
+ exit(0);
+end;
+
+function UTF8CharacterStrictLength(P: PChar): integer;
+begin
+ Result := UTF8CodepointStrictSize(P);
+end;
+
+function UTF8CStringToUTF8String(SourceStart: PChar; SourceLen: PtrInt) : string;
+var
+ Source: PChar;
+ Dest: PChar;
+ SourceEnd: PChar;
+ SourceCopied: PChar;
+
+ // Copies from SourceStart till Source to Dest and updates Dest
+ procedure CopyPart; inline;
+ var
+ CopyLength: SizeInt;
+ begin
+ CopyLength := Source - SourceCopied;
+ if CopyLength=0 then exit;
+ System.move(SourceCopied^ , Dest^, CopyLength);
+ SourceCopied:=Source;
+ inc(Dest, CopyLength);
+ end;
+
+begin
+ SetLength(Result{%H-}, SourceLen);
+ if SourceLen=0 then exit;
+ SourceCopied:=SourceStart;
+ Source:=SourceStart;
+ Dest:=PChar(Result);
+ SourceEnd := Source + SourceLen;
+ while Source0 then
+ Result:=UTF8Length(PChar(SearchInText),i-1)+1;
+ end
+ else if StartPos>1 then
+ begin
+ // skip
+ StartPosP:=UTF8CodepointStart(PChar(SearchInText),Length(SearchInText),StartPos-1);
+ if StartPosP=nil then exit;
+ // search
+ p:=UTF8PosP(PChar(SearchForText),length(SearchForText),
+ StartPosP,length(SearchInText)+PChar(SearchInText)-StartPosP);
+ // get UTF-8 position
+ if p=nil then exit;
+ Result:=StartPos+UTF8Length(StartPosP,p-StartPosP);
+ end;
+end;
+
+function UTF8PosP(SearchForText: PChar; SearchForTextLen: SizeInt;
+ SearchInText: PChar; SearchInTextLen: SizeInt): PChar;
+// returns the position where SearchInText starts in SearchForText
+// returns nil if not found
+var
+ p: SizeInt;
+begin
+ Result:=nil;
+ if (SearchForText=nil) or (SearchForTextLen=0) or (SearchInText=nil) then
+ exit;
+ while SearchInTextLen>0 do begin
+ p:=IndexByte(SearchInText^,SearchInTextLen,PByte(SearchForText)^);
+ if p<0 then exit;
+ inc(SearchInText,p);
+ dec(SearchInTextLen,p);
+ if SearchInTextLennil then
+ MaxBytes:=EndBytePos-StartBytePos;
+ Result:=copy(s,StartBytePos-PChar(s)+1,MaxBytes);
+ end;
+end;
+
+procedure UTF8Delete(var s: Utf8String; StartCharIndex, CharCount: PtrInt);
+var
+ tmp: String;
+begin
+ tmp := RawByteString(s);
+ {.$IFDEF ACP_RTL}
+ { change code page without converting the data }
+ SetCodePage(RawByteString(tmp), CP_UTF8, False);
+ {.$ENDIF}
+ { keep refcount to 1 if it was 1, to avoid unnecessary copies }
+ s := '';
+ UTF8Delete(tmp,StartCharIndex,CharCount);
+ { same as above }
+ s := RawByteString(tmp);
+ tmp := '';
+ SetCodePage(RawByteString(s), CP_UTF8, False);
+end;
+
+procedure UTF8Delete(var s: String; StartCharIndex, CharCount: PtrInt);
+var
+ StartBytePos: PChar;
+ EndBytePos: PChar;
+ MaxBytes: PtrInt;
+begin
+ StartBytePos:=UTF8CodepointStart(PChar(s),length(s),StartCharIndex-1);
+ if StartBytePos <> nil then
+ begin
+ MaxBytes:=PtrInt(PChar(s)+length(s)-StartBytePos);
+ EndBytePos:=UTF8CodepointStart(StartBytePos,MaxBytes,CharCount);
+ if EndBytePos=nil then
+ Delete(s,StartBytePos-PChar(s)+1,MaxBytes)
+ else
+ Delete(s,StartBytePos-PChar(s)+1,EndBytePos-StartBytePos);
+ end;
+end;
+
+{It's simper to copy the code from the variant with String parameters than writing a wrapper}
+procedure UTF8Insert(const source: UTF8String; var s: UTF8string;
+ StartCharIndex: PtrInt);
+var
+ StartBytePos: PChar;
+begin
+ StartBytePos:=UTF8CodepointStart(PChar(s),length(s),StartCharIndex-1);
+ if StartBytePos <> nil then
+ Insert(source, s, StartBytePos-PChar(s)+1);
+end;
+
+procedure UTF8Insert(const source: String; var s: String; StartCharIndex: PtrInt);
+var
+ StartBytePos: PChar;
+begin
+ StartBytePos:=UTF8CodepointStart(PChar(s),length(s),StartCharIndex-1);
+ if StartBytePos <> nil then
+ Insert(source, s, StartBytePos-PChar(s)+1);
+end;
+
+function UTF8StringReplace(const S, OldPattern, NewPattern: String;
+ Flags: TReplaceFlags; ALanguage: string): String; inline;
+var
+ DummyCount: Integer;
+begin
+ Result := Utf8StringReplace(S, OldPattern, NewPattern, Flags, DummyCount, ALanguage);
+end;
+
+function UTF8StringReplace(const S, OldPattern, NewPattern: String;
+ Flags: TReplaceFlags; out Count: Integer; ALanguage: string=''): String;
+// same algorithm as StringReplace, but using UTF8LowerCase
+// for case insensitive search
+var
+ Srch, OldP, RemS: string;
+ P: Integer;
+begin
+ Srch := S;
+ OldP := OldPattern;
+ Count := 0;
+ if rfIgnoreCase in Flags then
+ begin
+ Srch := UTF8LowerCase(Srch,ALanguage);
+ OldP := UTF8LowerCase(OldP,ALanguage);
+ end;
+ RemS := S;
+ Result := '';
+ while Length(Srch) <> 0 do
+ begin
+ P := Pos(OldP, Srch);
+ if P = 0 then
+ begin
+ Result := Result + RemS;
+ Srch := '';
+ end
+ else
+ begin
+ Inc(Count);
+ Result := Result + Copy(RemS,1,P-1) + NewPattern;
+ P := P + Length(OldP);
+ RemS := Copy(RemS, P, Length(RemS)-P+1);
+ if not (rfReplaceAll in Flags) then
+ begin
+ Result := Result + RemS;
+ Srch := '';
+ end
+ else
+ Srch := Copy(Srch, P, Length(Srch)-P+1);
+ end;
+ end;
+end;
+
+{
+ UTF8SwapCase - a "naive" implementation that uses UTF8UpperCase and UTF8LowerCase.
+ It serves its purpose and performs OK for short and resonably long strings
+ but it should be rewritten in the future if better performance and lower
+ memory consumption is needed.
+
+ AInStr - The input string.
+ ALanguage - The language. Use '' for maximum speed if one desires to ignore the language
+ (See UTF8LowerCase comment for more details on ALanguage parameter.)
+}
+function UTF8SwapCase(const AInStr: string; ALanguage: string=''): string;
+var
+ xUpperCase: string;
+ xLowerCase: string;
+ I: Integer;
+begin
+ if AInStr = '' then
+ Exit('');
+
+ xUpperCase := UTF8UpperCase(AInStr, ALanguage);
+ xLowerCase := UTF8LowerCase(AInStr, ALanguage);
+ if (Length(xUpperCase) <> Length(AInStr)) or (Length(xLowerCase) <> Length(AInStr)) then
+ Exit(AInStr);//something went wrong -> the lengths of utf8 strings changed
+
+ SetLength(Result, Length(AInStr));
+ for I := 1 to Length(AInStr) do
+ if AInStr[I] <> xUpperCase[I] then
+ Result[I] := xUpperCase[I]
+ else
+ Result[I] := xLowerCase[I];
+end;
+
+function UTF8ProperCase(const AInStr: string; const WordDelims: TSysCharSet): string;
+var
+ P, PE : PChar;
+ CharLen: Integer;
+ Capital: string;
+begin
+ Result := UTF8LowerCase(AInStr);
+ UniqueString(Result);
+ P := PChar(Result);
+ PE := P+Length(Result);
+ while (P= InStrEnd then Exit;
+
+ // Language identification
+ IsTurkish := (ALanguage = 'tr') or (ALanguage = 'az'); // Turkish and Azeri have a special handling
+
+ UniqueString(Result);
+ OutStr := PChar(Result) + (InStr - PChar(AInStr));
+ CounterDiff := 0;
+
+ while InStr < InStrEnd do
+ begin
+ c1 := InStr^;
+ case c1 of
+ // codepoints UTF-8 range Description Case change
+ // $0041..$005A $41..$5A Capital ASCII X+$20
+ 'A'..'Z':
+ begin
+ { First ASCII chars }
+ // Special turkish handling
+ // capital undotted I to small undotted i
+ if IsTurkish and (c1 = 'I') then
+ begin
+ p:=OutStr - PChar(Result);
+ SetLength(Result,Length(Result)+1);// Increase the buffer
+ OutStr := PChar(Result)+p;
+ OutStr^ := #$C4;
+ inc(OutStr);
+ OutStr^ := #$B1;
+ dec(CounterDiff);
+ end
+ else
+ begin
+ OutStr^ := chr(ord(c1)+32);
+ end;
+ inc(InStr);
+ inc(OutStr);
+ end;
+
+ // Chars with 2-bytes which might be modified
+ #$C3..#$D5:
+ begin
+ c2 := InStr[1];
+ new_c1 := c1;
+ new_c2 := c2;
+ case c1 of
+ // Latin Characters 0000–0FFF http://en.wikibooks.org/wiki/Unicode/Character_reference/0000-0FFF
+ // codepoints UTF-8 range Description Case change
+ // $00C0..$00D6 C3 80..C3 96 Capital Latin with accents X+$20
+ // $D7 C3 97 Multiplication Sign N/A
+ // $00D8..$00DE C3 98..C3 9E Capital Latin with accents X+$20
+ // $DF C3 9F German beta ß already lowercase
+ #$C3:
+ begin
+ case c2 of
+ #$80..#$96, #$98..#$9E: new_c2 := chr(ord(c2) + $20)
+ end;
+ end;
+ // $0100..$012F C4 80..C4 AF Capital/Small Latin accents if mod 2 = 0 then X+1
+ // $0130..$0131 C4 B0..C4 B1 Turkish
+ // C4 B0 turkish uppercase dotted i -> 'i'
+ // C4 B1 turkish lowercase undotted ı
+ // $0132..$0137 C4 B2..C4 B7 Capital/Small Latin accents if mod 2 = 0 then X+1
+ // $0138 C4 B8 ĸ N/A
+ // $0139..$024F C4 B9..C5 88 Capital/Small Latin accents if mod 2 = 1 then X+1
+ #$C4:
+ begin
+ case c2 of
+ #$80..#$AF, #$B2..#$B7: if ord(c2) mod 2 = 0 then new_c2 := chr(ord(c2) + 1);
+ #$B0: // Turkish
+ begin
+ OutStr^ := 'i';
+ inc(InStr, 2);
+ inc(OutStr);
+ inc(CounterDiff, 1);
+ Continue;
+ end;
+ #$B9..#$BE: if ord(c2) mod 2 = 1 then new_c2 := chr(ord(c2) + 1);
+ #$BF: // This crosses the borders between the first byte of the UTF-8 char
+ begin
+ new_c1 := #$C5;
+ new_c2 := #$80;
+ end;
+ end;
+ end;
+ // $C589 ʼn
+ // $C58A..$C5B7: if OldChar mod 2 = 0 then NewChar := OldChar + 1;
+ // $C5B8: NewChar := $C3BF; // Ÿ
+ // $C5B9..$C8B3: if OldChar mod 2 = 1 then NewChar := OldChar + 1;
+ #$C5:
+ begin
+ case c2 of
+ #$8A..#$B7: //0
+ begin
+ if ord(c2) mod 2 = 0 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ #$00..#$88, #$B9..#$BE: //1
+ begin
+ if ord(c2) mod 2 = 1 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ #$B8: // Ÿ
+ begin
+ new_c1 := #$C3;
+ new_c2 := #$BF;
+ end;
+ end;
+ end;
+ {A convoluted part: C6 80..C6 8F
+
+ 0180;LATIN SMALL LETTER B WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER B BAR;;0243;;0243
+ 0181;LATIN CAPITAL LETTER B WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B HOOK;;;0253; => C6 81=>C9 93
+ 0182;LATIN CAPITAL LETTER B WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B TOPBAR;;;0183;
+ 0183;LATIN SMALL LETTER B WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER B TOPBAR;;0182;;0182
+ 0184;LATIN CAPITAL LETTER TONE SIX;Lu;0;L;;;;;N;;;;0185;
+ 0185;LATIN SMALL LETTER TONE SIX;Ll;0;L;;;;;N;;;0184;;0184
+ 0186;LATIN CAPITAL LETTER OPEN O;Lu;0;L;;;;;N;;;;0254; ==> C9 94
+ 0187;LATIN CAPITAL LETTER C WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER C HOOK;;;0188;
+ 0188;LATIN SMALL LETTER C WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER C HOOK;;0187;;0187
+ 0189;LATIN CAPITAL LETTER AFRICAN D;Lu;0;L;;;;;N;;;;0256; => C9 96
+ 018A;LATIN CAPITAL LETTER D WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D HOOK;;;0257; => C9 97
+ 018B;LATIN CAPITAL LETTER D WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D TOPBAR;;;018C;
+ 018C;LATIN SMALL LETTER D WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER D TOPBAR;;018B;;018B
+ 018D;LATIN SMALL LETTER TURNED DELTA;Ll;0;L;;;;;N;;;;;
+ 018E;LATIN CAPITAL LETTER REVERSED E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER TURNED E;;;01DD; => C7 9D
+ 018F;LATIN CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;0259; => C9 99
+ }
+ #$C6:
+ begin
+ case c2 of
+ #$81:
+ begin
+ new_c1 := #$C9;
+ new_c2 := #$93;
+ end;
+ #$82..#$85:
+ begin
+ if ord(c2) mod 2 = 0 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ #$87..#$88,#$8B..#$8C:
+ begin
+ if ord(c2) mod 2 = 1 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ #$86:
+ begin
+ new_c1 := #$C9;
+ new_c2 := #$94;
+ end;
+ #$89:
+ begin
+ new_c1 := #$C9;
+ new_c2 := #$96;
+ end;
+ #$8A:
+ begin
+ new_c1 := #$C9;
+ new_c2 := #$97;
+ end;
+ #$8E:
+ begin
+ new_c1 := #$C7;
+ new_c2 := #$9D;
+ end;
+ #$8F:
+ begin
+ new_c1 := #$C9;
+ new_c2 := #$99;
+ end;
+ {
+ And also C6 90..C6 9F
+
+ 0190;LATIN CAPITAL LETTER OPEN E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER EPSILON;;;025B; => C9 9B
+ 0191;LATIN CAPITAL LETTER F WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER F HOOK;;;0192; => +1
+ 0192;LATIN SMALL LETTER F WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT F;;0191;;0191 <=
+ 0193;LATIN CAPITAL LETTER G WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G HOOK;;;0260; => C9 A0
+ 0194;LATIN CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;0263; => C9 A3
+ 0195;LATIN SMALL LETTER HV;Ll;0;L;;;;;N;LATIN SMALL LETTER H V;;01F6;;01F6 <=
+ 0196;LATIN CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;0269; => C9 A9
+ 0197;LATIN CAPITAL LETTER I WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED I;;;0268; => C9 A8
+ 0198;LATIN CAPITAL LETTER K WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER K HOOK;;;0199; => +1
+ 0199;LATIN SMALL LETTER K WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER K HOOK;;0198;;0198 <=
+ 019A;LATIN SMALL LETTER L WITH BAR;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED L;;023D;;023D <=
+ 019B;LATIN SMALL LETTER LAMBDA WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED LAMBDA;;;; <=
+ 019C;LATIN CAPITAL LETTER TURNED M;Lu;0;L;;;;;N;;;;026F; => C9 AF
+ 019D;LATIN CAPITAL LETTER N WITH LEFT HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER N HOOK;;;0272; => C9 B2
+ 019E;LATIN SMALL LETTER N WITH LONG RIGHT LEG;Ll;0;L;;;;;N;;;0220;;0220 <=
+ 019F;LATIN CAPITAL LETTER O WITH MIDDLE TILDE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED O;;;0275; => C9 B5
+ }
+ #$90:
+ begin
+ new_c1 := #$C9;
+ new_c2 := #$9B;
+ end;
+ #$91, #$98: new_c2 := chr(ord(c2)+1);
+ #$93:
+ begin
+ new_c1 := #$C9;
+ new_c2 := #$A0;
+ end;
+ #$94:
+ begin
+ new_c1 := #$C9;
+ new_c2 := #$A3;
+ end;
+ #$96:
+ begin
+ new_c1 := #$C9;
+ new_c2 := #$A9;
+ end;
+ #$97:
+ begin
+ new_c1 := #$C9;
+ new_c2 := #$A8;
+ end;
+ #$9C:
+ begin
+ new_c1 := #$C9;
+ new_c2 := #$AF;
+ end;
+ #$9D:
+ begin
+ new_c1 := #$C9;
+ new_c2 := #$B2;
+ end;
+ #$9F:
+ begin
+ new_c1 := #$C9;
+ new_c2 := #$B5;
+ end;
+ {
+ And also C6 A0..C6 AF
+
+ 01A0;LATIN CAPITAL LETTER O WITH HORN;Lu;0;L;004F 031B;;;;N;LATIN CAPITAL LETTER O HORN;;;01A1; => +1
+ 01A1;LATIN SMALL LETTER O WITH HORN;Ll;0;L;006F 031B;;;;N;LATIN SMALL LETTER O HORN;;01A0;;01A0 <=
+ 01A2;LATIN CAPITAL LETTER OI;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O I;;;01A3; => +1
+ 01A3;LATIN SMALL LETTER OI;Ll;0;L;;;;;N;LATIN SMALL LETTER O I;;01A2;;01A2 <=
+ 01A4;LATIN CAPITAL LETTER P WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER P HOOK;;;01A5; => +1
+ 01A5;LATIN SMALL LETTER P WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER P HOOK;;01A4;;01A4 <=
+ 01A6;LATIN LETTER YR;Lu;0;L;;;;;N;LATIN LETTER Y R;;;0280; => CA 80
+ 01A7;LATIN CAPITAL LETTER TONE TWO;Lu;0;L;;;;;N;;;;01A8; => +1
+ 01A8;LATIN SMALL LETTER TONE TWO;Ll;0;L;;;;;N;;;01A7;;01A7 <=
+ 01A9;LATIN CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;0283; => CA 83
+ 01AA;LATIN LETTER REVERSED ESH LOOP;Ll;0;L;;;;;N;;;;;
+ 01AB;LATIN SMALL LETTER T WITH PALATAL HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T PALATAL HOOK;;;; <=
+ 01AC;LATIN CAPITAL LETTER T WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T HOOK;;;01AD; => +1
+ 01AD;LATIN SMALL LETTER T WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T HOOK;;01AC;;01AC <=
+ 01AE;LATIN CAPITAL LETTER T WITH RETROFLEX HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T RETROFLEX HOOK;;;0288; => CA 88
+ 01AF;LATIN CAPITAL LETTER U WITH HORN;Lu;0;L;0055 031B;;;;N;LATIN CAPITAL LETTER U HORN;;;01B0; => +1
+ }
+ #$A0..#$A5,#$AC:
+ begin
+ if ord(c2) mod 2 = 0 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ #$A7,#$AF:
+ begin
+ if ord(c2) mod 2 = 1 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ #$A6:
+ begin
+ new_c1 := #$CA;
+ new_c2 := #$80;
+ end;
+ #$A9:
+ begin
+ new_c1 := #$CA;
+ new_c2 := #$83;
+ end;
+ #$AE:
+ begin
+ new_c1 := #$CA;
+ new_c2 := #$88;
+ end;
+ {
+ And also C6 B0..C6 BF
+
+ 01B0;LATIN SMALL LETTER U WITH HORN;Ll;0;L;0075 031B;;;;N;LATIN SMALL LETTER U HORN;;01AF;;01AF <= -1
+ 01B1;LATIN CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;028A; => CA 8A
+ 01B2;LATIN CAPITAL LETTER V WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER SCRIPT V;;;028B; => CA 8B
+ 01B3;LATIN CAPITAL LETTER Y WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Y HOOK;;;01B4; => +1
+ 01B4;LATIN SMALL LETTER Y WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Y HOOK;;01B3;;01B3 <=
+ 01B5;LATIN CAPITAL LETTER Z WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Z BAR;;;01B6; => +1
+ 01B6;LATIN SMALL LETTER Z WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER Z BAR;;01B5;;01B5 <=
+ 01B7;LATIN CAPITAL LETTER EZH;Lu;0;L;;;;;N;LATIN CAPITAL LETTER YOGH;;;0292; => CA 92
+ 01B8;LATIN CAPITAL LETTER EZH REVERSED;Lu;0;L;;;;;N;LATIN CAPITAL LETTER REVERSED YOGH;;;01B9; => +1
+ 01B9;LATIN SMALL LETTER EZH REVERSED;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED YOGH;;01B8;;01B8 <=
+ 01BA;LATIN SMALL LETTER EZH WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH WITH TAIL;;;; <=
+ 01BB;LATIN LETTER TWO WITH STROKE;Lo;0;L;;;;;N;LATIN LETTER TWO BAR;;;; X
+ 01BC;LATIN CAPITAL LETTER TONE FIVE;Lu;0;L;;;;;N;;;;01BD; => +1
+ 01BD;LATIN SMALL LETTER TONE FIVE;Ll;0;L;;;;;N;;;01BC;;01BC <=
+ 01BE;LATIN LETTER INVERTED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER INVERTED GLOTTAL STOP BAR;;;; X
+ 01BF;LATIN LETTER WYNN;Ll;0;L;;;;;N;;;01F7;;01F7 <=
+ }
+ #$B8,#$BC:
+ begin
+ if ord(c2) mod 2 = 0 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ #$B3..#$B6:
+ begin
+ if ord(c2) mod 2 = 1 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ #$B1:
+ begin
+ new_c1 := #$CA;
+ new_c2 := #$8A;
+ end;
+ #$B2:
+ begin
+ new_c1 := #$CA;
+ new_c2 := #$8B;
+ end;
+ #$B7:
+ begin
+ new_c1 := #$CA;
+ new_c2 := #$92;
+ end;
+ end;
+ end;
+ #$C7:
+ begin
+ case c2 of
+ #$84..#$8C,#$B1..#$B3:
+ begin
+ if (ord(c2) and $F) mod 3 = 1 then new_c2 := chr(ord(c2) + 2)
+ else if (ord(c2) and $F) mod 3 = 2 then new_c2 := chr(ord(c2) + 1);
+ end;
+ #$8D..#$9C:
+ begin
+ if ord(c2) mod 2 = 1 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ #$9E..#$AF,#$B4..#$B5,#$B8..#$BF:
+ begin
+ if ord(c2) mod 2 = 0 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ {
+ 01F6;LATIN CAPITAL LETTER HWAIR;Lu;0;L;;;;;N;;;;0195;
+ 01F7;LATIN CAPITAL LETTER WYNN;Lu;0;L;;;;;N;;;;01BF;
+ }
+ #$B6:
+ begin
+ new_c1 := #$C6;
+ new_c2 := #$95;
+ end;
+ #$B7:
+ begin
+ new_c1 := #$C6;
+ new_c2 := #$BF;
+ end;
+ end;
+ end;
+ {
+ Codepoints 0200 to 023F
+ }
+ #$C8:
+ begin
+ // For this one we can simply start with a default and override for some specifics
+ if (c2 in [#$80..#$B3]) and (ord(c2) mod 2 = 0) then new_c2 := chr(ord(c2) + 1);
+
+ case c2 of
+ #$A0:
+ begin
+ new_c1 := #$C6;
+ new_c2 := #$9E;
+ end;
+ #$A1: new_c2 := c2;
+ {
+ 023A;LATIN CAPITAL LETTER A WITH STROKE;Lu;0;L;;;;;N;;;;2C65; => E2 B1 A5
+ 023B;LATIN CAPITAL LETTER C WITH STROKE;Lu;0;L;;;;;N;;;;023C; => +1
+ 023C;LATIN SMALL LETTER C WITH STROKE;Ll;0;L;;;;;N;;;023B;;023B <=
+ 023D;LATIN CAPITAL LETTER L WITH BAR;Lu;0;L;;;;;N;;;;019A; => C6 9A
+ 023E;LATIN CAPITAL LETTER T WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;2C66; => E2 B1 A6
+ 023F;LATIN SMALL LETTER S WITH SWASH TAIL;Ll;0;L;;;;;N;;;2C7E;;2C7E <=
+ 0240;LATIN SMALL LETTER Z WITH SWASH TAIL;Ll;0;L;;;;;N;;;2C7F;;2C7F <=
+ }
+ #$BA,#$BE:
+ begin
+ p:= OutStr - PChar(Result);
+ SetLength(Result,Length(Result)+1);// Increase the buffer
+ OutStr := PChar(Result)+p;
+ OutStr^ := #$E2;
+ inc(OutStr);
+ OutStr^ := #$B1;
+ inc(OutStr);
+ if c2 = #$BA then OutStr^ := #$A5
+ else OutStr^ := #$A6;
+ dec(CounterDiff);
+ inc(OutStr);
+ inc(InStr, 2);
+ Continue;
+ end;
+ #$BD:
+ begin
+ new_c1 := #$C6;
+ new_c2 := #$9A;
+ end;
+ #$BB: new_c2 := chr(ord(c2) + 1);
+ end;
+ end;
+ {
+ Codepoints 0240 to 027F
+
+ Here only 0240..024F needs lowercase
+ }
+ #$C9:
+ begin
+ case c2 of
+ #$81..#$82:
+ begin
+ if ord(c2) mod 2 = 1 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ #$86..#$8F:
+ begin
+ if ord(c2) mod 2 = 0 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ #$83:
+ begin
+ new_c1 := #$C6;
+ new_c2 := #$80;
+ end;
+ #$84:
+ begin
+ new_c1 := #$CA;
+ new_c2 := #$89;
+ end;
+ #$85:
+ begin
+ new_c1 := #$CA;
+ new_c2 := #$8C;
+ end;
+ end;
+ end;
+ // $CE91..$CE9F: NewChar := OldChar + $20; // Greek Characters
+ // $CEA0..$CEA9: NewChar := OldChar + $E0; // Greek Characters
+ #$CE:
+ begin
+ case c2 of
+ // 0380 = CE 80
+ #$86: new_c2 := #$AC;
+ #$88: new_c2 := #$AD;
+ #$89: new_c2 := #$AE;
+ #$8A: new_c2 := #$AF;
+ #$8C: new_c1 := #$CF; // By coincidence new_c2 remains the same
+ #$8E:
+ begin
+ new_c1 := #$CF;
+ new_c2 := #$8D;
+ end;
+ #$8F:
+ begin
+ new_c1 := #$CF;
+ new_c2 := #$8E;
+ end;
+ // 0390 = CE 90
+ #$91..#$9F:
+ begin
+ new_c2 := chr(ord(c2) + $20);
+ end;
+ // 03A0 = CE A0
+ #$A0..#$AB:
+ begin
+ new_c1 := #$CF;
+ new_c2 := chr(ord(c2) - $20);
+ end;
+ end;
+ end;
+ // 03C0 = CF 80
+ // 03D0 = CF 90
+ // 03E0 = CF A0
+ // 03F0 = CF B0
+ #$CF:
+ begin
+ case c2 of
+ // 03CF;GREEK CAPITAL KAI SYMBOL;Lu;0;L;;;;;N;;;;03D7; CF 8F => CF 97
+ #$8F: new_c2 := #$97;
+ // 03D8;GREEK LETTER ARCHAIC KOPPA;Lu;0;L;;;;;N;;;;03D9;
+ #$98: new_c2 := #$99;
+ // 03DA;GREEK LETTER STIGMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER STIGMA;;;03DB;
+ #$9A: new_c2 := #$9B;
+ // 03DC;GREEK LETTER DIGAMMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DIGAMMA;;;03DD;
+ #$9C: new_c2 := #$9D;
+ // 03DE;GREEK LETTER KOPPA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KOPPA;;;03DF;
+ #$9E: new_c2 := #$9F;
+ {
+ 03E0;GREEK LETTER SAMPI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SAMPI;;;03E1;
+ 03E1;GREEK SMALL LETTER SAMPI;Ll;0;L;;;;;N;;;03E0;;03E0
+ 03E2;COPTIC CAPITAL LETTER SHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHEI;;;03E3;
+ 03E3;COPTIC SMALL LETTER SHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER SHEI;;03E2;;03E2
+ ...
+ 03EE;COPTIC CAPITAL LETTER DEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DEI;;;03EF;
+ 03EF;COPTIC SMALL LETTER DEI;Ll;0;L;;;;;N;GREEK SMALL LETTER DEI;;03EE;;03EE
+ }
+ #$A0..#$AF: if ord(c2) mod 2 = 0 then
+ new_c2 := chr(ord(c2) + 1);
+ // 03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L; 0398;;;;N;;;;03B8;
+ #$B4:
+ begin
+ new_c1 := #$CE;
+ new_c2 := #$B8;
+ end;
+ // 03F7;GREEK CAPITAL LETTER SHO;Lu;0;L;;;;;N;;;;03F8;
+ #$B7: new_c2 := #$B8;
+ // 03F9;GREEK CAPITAL LUNATE SIGMA SYMBOL;Lu;0;L; 03A3;;;;N;;;;03F2;
+ #$B9: new_c2 := #$B2;
+ // 03FA;GREEK CAPITAL LETTER SAN;Lu;0;L;;;;;N;;;;03FB;
+ #$BA: new_c2 := #$BB;
+ // 03FD;GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL;Lu;0;L;;;;;N;;;;037B;
+ #$BD:
+ begin
+ new_c1 := #$CD;
+ new_c2 := #$BB;
+ end;
+ // 03FE;GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL;Lu;0;L;;;;;N;;;;037C;
+ #$BE:
+ begin
+ new_c1 := #$CD;
+ new_c2 := #$BC;
+ end;
+ // 03FF;GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL;Lu;0;L;;;;;N;;;;037D;
+ #$BF:
+ begin
+ new_c1 := #$CD;
+ new_c2 := #$BD;
+ end;
+ end;
+ end;
+ // $D080..$D08F: NewChar := OldChar + $110; // Cyrillic alphabet
+ // $D090..$D09F: NewChar := OldChar + $20; // Cyrillic alphabet
+ // $D0A0..$D0AF: NewChar := OldChar + $E0; // Cyrillic alphabet
+ #$D0:
+ begin
+ c2 := InStr[1];
+ case c2 of
+ #$80..#$8F:
+ begin
+ new_c1 := chr(ord(c1)+1);
+ new_c2 := chr(ord(c2) + $10);
+ end;
+ #$90..#$9F:
+ begin
+ new_c2 := chr(ord(c2) + $20);
+ end;
+ #$A0..#$AF:
+ begin
+ new_c1 := chr(ord(c1)+1);
+ new_c2 := chr(ord(c2) - $20);
+ end;
+ end;
+ end;
+ // Archaic and non-slavic cyrillic 460-47F = D1A0-D1BF
+ // These require just adding 1 to get the lowercase
+ #$D1:
+ begin
+ if (c2 in [#$A0..#$BF]) and (ord(c2) mod 2 = 0) then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ // Archaic and non-slavic cyrillic 480-4BF = D280-D2BF
+ // These mostly require just adding 1 to get the lowercase
+ #$D2:
+ begin
+ case c2 of
+ #$80:
+ begin
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ // #$81 is already lowercase
+ // #$82-#$89 ???
+ #$8A..#$BF:
+ begin
+ if ord(c2) mod 2 = 0 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ end;
+ end;
+ {
+ Codepoints 04C0..04FF
+ }
+ #$D3:
+ begin
+ case c2 of
+ #$80: new_c2 := #$8F;
+ #$81..#$8E:
+ begin
+ if ord(c2) mod 2 = 1 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ #$90..#$BF:
+ begin
+ if ord(c2) mod 2 = 0 then
+ new_c2 := chr(ord(c2) + 1);
+ end;
+ end;
+ end;
+ {
+ Codepoints 0500..053F
+
+ Armenian starts in 0531
+ }
+ #$D4:
+ begin
+ if ord(c2) mod 2 = 0 then
+ new_c2 := chr(ord(c2) + 1);
+
+ // Armenian
+ if c2 in [#$B1..#$BF] then
+ begin
+ new_c1 := #$D5;
+ new_c2 := chr(ord(c2) - $10);
+ end;
+ end;
+ {
+ Codepoints 0540..057F
+
+ Armenian
+ }
+ #$D5:
+ begin
+ case c2 of
+ #$80..#$8F:
+ begin
+ new_c2 := chr(ord(c2) + $30);
+ end;
+ #$90..#$96:
+ begin
+ new_c1 := #$D6;
+ new_c2 := chr(ord(c2) - $10);
+ end;
+ end;
+ end;
+ end;
+ // Common code 2-byte modifiable chars
+ if (CounterDiff <> 0) then
+ begin
+ OutStr^ := new_c1;
+ OutStr[1] := new_c2;
+ end
+ else
+ begin
+ if (new_c1 <> c1) then OutStr^ := new_c1;
+ if (new_c2 <> c2) then OutStr[1] := new_c2;
+ end;
+ inc(InStr, 2);
+ inc(OutStr, 2);
+ end;
+ {
+ Characters with 3 bytes
+ }
+ #$E1:
+ begin
+ new_c1 := c1;
+ c2 := InStr[1];
+ c3 := InStr[2];
+ new_c2 := c2;
+ new_c3 := c3;
+ {
+ Georgian codepoints 10A0-10C5 => 2D00-2D25
+
+ In UTF-8 this is:
+ E1 82 A0 - E1 82 BF => E2 B4 80 - E2 B4 9F
+ E1 83 80 - E1 83 85 => E2 B4 A0 - E2 B4 A5
+ }
+ case c2 of
+ #$82:
+ if (c3 in [#$A0..#$BF]) then
+ begin
+ new_c1 := #$E2;
+ new_c2 := #$B4;
+ new_c3 := chr(ord(c3) - $20);
+ end;
+ #$83:
+ if (c3 in [#$80..#$85]) then
+ begin
+ new_c1 := #$E2;
+ new_c2 := #$B4;
+ new_c3 := chr(ord(c3) + $20);
+ end;
+ {
+ Extra chars between 1E00..1EFF
+
+ Blocks of chars:
+ 1E00..1E3F E1 B8 80..E1 B8 BF
+ 1E40..1E7F E1 B9 80..E1 B9 BF
+ 1E80..1EBF E1 BA 80..E1 BA BF
+ 1EC0..1EFF E1 BB 80..E1 BB BF
+ }
+ #$B8..#$BB:
+ begin
+ // Start with a default and change for some particular chars
+ if ord(c3) mod 2 = 0 then
+ new_c3 := chr(ord(c3) + 1);
+
+ { Only 1E96..1E9F are different E1 BA 96..E1 BA 9F
+
+ 1E96;LATIN SMALL LETTER H WITH LINE BELOW;Ll;0;L;0068 0331;;;;N;;;;;
+ 1E97;LATIN SMALL LETTER T WITH DIAERESIS;Ll;0;L;0074 0308;;;;N;;;;;
+ 1E98;LATIN SMALL LETTER W WITH RING ABOVE;Ll;0;L;0077 030A;;;;N;;;;;
+ 1E99;LATIN SMALL LETTER Y WITH RING ABOVE;Ll;0;L;0079 030A;;;;N;;;;;
+ 1E9A;LATIN SMALL LETTER A WITH RIGHT HALF RING;Ll;0;L; 0061 02BE;;;;N;;;;;
+ 1E9B;LATIN SMALL LETTER LONG S WITH DOT ABOVE;Ll;0;L;017F 0307;;;;N;;;1E60;;1E60
+ 1E9C;LATIN SMALL LETTER LONG S WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;;;
+ 1E9D;LATIN SMALL LETTER LONG S WITH HIGH STROKE;Ll;0;L;;;;;N;;;;;
+ 1E9E;LATIN CAPITAL LETTER SHARP S;Lu;0;L;;;;;N;;;;00DF; => C3 9F
+ 1E9F;LATIN SMALL LETTER DELTA;Ll;0;L;;;;;N;;;;;
+ }
+ if (c2 = #$BA) and (c3 in [#$96..#$9F]) then new_c3 := c3;
+ // LATIN CAPITAL LETTER SHARP S => to german Beta
+ if (c2 = #$BA) and (c3 = #$9E) then
+ begin
+ inc(InStr, 3);
+ OutStr^ := #$C3;
+ inc(OutStr);
+ OutStr^ := #$9F;
+ inc(OutStr);
+ inc(CounterDiff, 1);
+ Continue;
+ end;
+ end;
+ {
+ Extra chars between 1F00..1FFF
+
+ Blocks of chars:
+ 1E00..1E3F E1 BC 80..E1 BC BF
+ 1E40..1E7F E1 BD 80..E1 BD BF
+ 1E80..1EBF E1 BE 80..E1 BE BF
+ 1EC0..1EFF E1 BF 80..E1 BF BF
+ }
+ #$BC:
+ begin
+ // Start with a default and change for some particular chars
+ if (ord(c3) mod $10) div 8 = 1 then
+ new_c3 := chr(ord(c3) - 8);
+ end;
+ #$BD:
+ begin
+ // Start with a default and change for some particular chars
+ case c3 of
+ #$80..#$8F, #$A0..#$AF: if (ord(c3) mod $10) div 8 = 1 then
+ new_c3 := chr(ord(c3) - 8);
+ {
+ 1F50;GREEK SMALL LETTER UPSILON WITH PSILI;Ll;0;L;03C5 0313;;;;N;;;;;
+ 1F51;GREEK SMALL LETTER UPSILON WITH DASIA;Ll;0;L;03C5 0314;;;;N;;;1F59;;1F59
+ 1F52;GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA;Ll;0;L;1F50 0300;;;;N;;;;;
+ 1F53;GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA;Ll;0;L;1F51 0300;;;;N;;;1F5B;;1F5B
+ 1F54;GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA;Ll;0;L;1F50 0301;;;;N;;;;;
+ 1F55;GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA;Ll;0;L;1F51 0301;;;;N;;;1F5D;;1F5D
+ 1F56;GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI;Ll;0;L;1F50 0342;;;;N;;;;;
+ 1F57;GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI;Ll;0;L;1F51 0342;;;;N;;;1F5F;;1F5F
+ 1F59;GREEK CAPITAL LETTER UPSILON WITH DASIA;Lu;0;L;03A5 0314;;;;N;;;;1F51;
+ 1F5B;GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA;Lu;0;L;1F59 0300;;;;N;;;;1F53;
+ 1F5D;GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA;Lu;0;L;1F59 0301;;;;N;;;;1F55;
+ 1F5F;GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI;Lu;0;L;1F59 0342;;;;N;;;;1F57;
+ }
+ #$99,#$9B,#$9D,#$9F: new_c3 := chr(ord(c3) - 8);
+ end;
+ end;
+ #$BE:
+ begin
+ // Start with a default and change for some particular chars
+ case c3 of
+ #$80..#$B9: if (ord(c3) mod $10) div 8 = 1 then
+ new_c3 := chr(ord(c3) - 8);
+ {
+ 1FB0;GREEK SMALL LETTER ALPHA WITH VRACHY;Ll;0;L;03B1 0306;;;;N;;;1FB8;;1FB8
+ 1FB1;GREEK SMALL LETTER ALPHA WITH MACRON;Ll;0;L;03B1 0304;;;;N;;;1FB9;;1FB9
+ 1FB2;GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F70 0345;;;;N;;;;;
+ 1FB3;GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI;Ll;0;L;03B1 0345;;;;N;;;1FBC;;1FBC
+ 1FB4;GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AC 0345;;;;N;;;;;
+ 1FB6;GREEK SMALL LETTER ALPHA WITH PERISPOMENI;Ll;0;L;03B1 0342;;;;N;;;;;
+ 1FB7;GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FB6 0345;;;;N;;;;;
+ 1FB8;GREEK CAPITAL LETTER ALPHA WITH VRACHY;Lu;0;L;0391 0306;;;;N;;;;1FB0;
+ 1FB9;GREEK CAPITAL LETTER ALPHA WITH MACRON;Lu;0;L;0391 0304;;;;N;;;;1FB1;
+ 1FBA;GREEK CAPITAL LETTER ALPHA WITH VARIA;Lu;0;L;0391 0300;;;;N;;;;1F70;
+ 1FBB;GREEK CAPITAL LETTER ALPHA WITH OXIA;Lu;0;L;0386;;;;N;;;;1F71;
+ 1FBC;GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI;Lt;0;L;0391 0345;;;;N;;;;1FB3;
+ 1FBD;GREEK KORONIS;Sk;0;ON; 0020 0313;;;;N;;;;;
+ 1FBE;GREEK PROSGEGRAMMENI;Ll;0;L;03B9;;;;N;;;0399;;0399
+ 1FBF;GREEK PSILI;Sk;0;ON; 0020 0313;;;;N;;;;;
+ }
+ #$BA:
+ begin
+ new_c2 := #$BD;
+ new_c3 := #$B0;
+ end;
+ #$BB:
+ begin
+ new_c2 := #$BD;
+ new_c3 := #$B1;
+ end;
+ #$BC: new_c3 := #$B3;
+ end;
+ end;
+ end;
+
+ if (CounterDiff <> 0) then
+ begin
+ OutStr^ := new_c1;
+ OutStr[1] := new_c2;
+ OutStr[2] := new_c3;
+ end
+ else
+ begin
+ if c1 <> new_c1 then OutStr^ := new_c1;
+ if c2 <> new_c2 then OutStr[1] := new_c2;
+ if c3 <> new_c3 then OutStr[2] := new_c3;
+ end;
+
+ inc(InStr, 3);
+ inc(OutStr, 3);
+ end;
+ {
+ More Characters with 3 bytes, so exotic stuff between:
+ $2126..$2183 E2 84 A6..E2 86 83
+ $24B6..$24CF Result:=u+26; E2 92 B6..E2 93 8F
+ $2C00..$2C2E Result:=u+48; E2 B0 80..E2 B0 AE
+ $2C60..$2CE2 E2 B1 A0..E2 B3 A2
+ }
+ #$E2:
+ begin
+ new_c1 := c1;
+ c2 := InStr[1];
+ c3 := InStr[2];
+ new_c2 := c2;
+ new_c3 := c3;
+ // 2126;OHM SIGN;Lu;0;L;03A9;;;;N;OHM;;;03C9; E2 84 A6 => CF 89
+ if (c2 = #$84) and (c3 = #$A6) then
+ begin
+ inc(InStr, 3);
+ OutStr^ := #$CF;
+ inc(OutStr);
+ OutStr^ := #$89;
+ inc(OutStr);
+ inc(CounterDiff, 1);
+ Continue;
+ end
+ {
+ 212A;KELVIN SIGN;Lu;0;L;004B;;;;N;DEGREES KELVIN;;;006B; E2 84 AA => 6B
+ }
+ else if (c2 = #$84) and (c3 = #$AA) then
+ begin
+ inc(InStr, 3);
+ OutStr^ := #$6B;
+ inc(OutStr);
+ inc(CounterDiff, 2);
+ Continue;
+ end
+ {
+ 212B;ANGSTROM SIGN;Lu;0;L;00C5;;;;N;ANGSTROM UNIT;;;00E5; E2 84 AB => C3 A5
+ }
+ else if (c2 = #$84) and (c3 = #$AB) then
+ begin
+ inc(InStr, 3);
+ OutStr^ := #$C3;
+ inc(OutStr);
+ OutStr^ := #$A5;
+ inc(OutStr);
+ inc(CounterDiff, 1);
+ Continue;
+ end
+ {
+ 2160;ROMAN NUMERAL ONE;Nl;0;L; 0049;;;1;N;;;;2170; E2 85 A0 => E2 85 B0
+ 2161;ROMAN NUMERAL TWO;Nl;0;L; 0049 0049;;;2;N;;;;2171;
+ 2162;ROMAN NUMERAL THREE;Nl;0;L; 0049 0049 0049;;;3;N;;;;2172;
+ 2163;ROMAN NUMERAL FOUR;Nl;0;L; 0049 0056;;;4;N;;;;2173;
+ 2164;ROMAN NUMERAL FIVE;Nl;0;L; 0056;;;5;N;;;;2174;
+ 2165;ROMAN NUMERAL SIX;Nl;0;L; 0056 0049;;;6;N;;;;2175;
+ 2166;ROMAN NUMERAL SEVEN;Nl;0;L; 0056 0049 0049;;;7;N;;;;2176;
+ 2167;ROMAN NUMERAL EIGHT;Nl;0;L; 0056 0049 0049 0049;;;8;N;;;;2177;
+ 2168;ROMAN NUMERAL NINE;Nl;0;L; 0049 0058;;;9;N;;;;2178;
+ 2169;ROMAN NUMERAL TEN;Nl;0;L; 0058;;;10;N;;;;2179;
+ 216A;ROMAN NUMERAL ELEVEN;Nl;0;L; 0058 0049;;;11;N;;;;217A;
+ 216B;ROMAN NUMERAL TWELVE;Nl;0;L; 0058 0049 0049;;;12;N;;;;217B;
+ 216C;ROMAN NUMERAL FIFTY;Nl;0;L; 004C;;;50;N;;;;217C;
+ 216D;ROMAN NUMERAL ONE HUNDRED;Nl;0;L; 0043;;;100;N;;;;217D;
+ 216E;ROMAN NUMERAL FIVE HUNDRED;Nl;0;L; 0044;;;500;N;;;;217E;
+ 216F;ROMAN NUMERAL ONE THOUSAND;Nl;0;L; 004D;;;1000;N;;;;217F;
+ }
+ else if (c2 = #$85) and (c3 in [#$A0..#$AF]) then new_c3 := chr(ord(c3) + $10)
+ {
+ 2183;ROMAN NUMERAL REVERSED ONE HUNDRED;Lu;0;L;;;;;N;;;;2184; E2 86 83 => E2 86 84
+ }
+ else if (c2 = #$86) and (c3 = #$83) then new_c3 := chr(ord(c3) + 1)
+ {
+ $24B6..$24CF Result:=u+26; E2 92 B6..E2 93 8F
+
+ Ex: 24B6;CIRCLED LATIN CAPITAL LETTER A;So;0;L; 0041;;;;N;;;;24D0; E2 92 B6 => E2 93 90
+ }
+ else if (c2 = #$92) and (c3 in [#$B6..#$BF]) then
+ begin
+ new_c2 := #$93;
+ new_c3 := chr(ord(c3) - $26);
+ end
+ // CIRCLED LATIN CAPITAL LETTER K $24C0 -> $24DA
+ else if (c2 = #$93) and (c3 in [#$80..#$8F]) then new_c3 := chr(ord(c3) + $1A)
+ {
+ $2C00..$2C2E Result:=u+48; E2 B0 80..E2 B0 AE
+
+ 2C00;GLAGOLITIC CAPITAL LETTER AZU;Lu;0;L;;;;;N;;;;2C30; E2 B0 80 => E2 B0 B0
+
+ 2C10;GLAGOLITIC CAPITAL LETTER NASHI;Lu;0;L;;;;;N;;;;2C40; E2 B0 90 => E2 B1 80
+ }
+ else if (c2 = #$B0) and (c3 in [#$80..#$8F]) then new_c3 := chr(ord(c3) + $30)
+ else if (c2 = #$B0) and (c3 in [#$90..#$AE]) then
+ begin
+ new_c2 := #$B1;
+ new_c3 := chr(ord(c3) - $10);
+ end
+ {
+ $2C60..$2CE2 E2 B1 A0..E2 B3 A2
+
+ 2C60;LATIN CAPITAL LETTER L WITH DOUBLE BAR;Lu;0;L;;;;;N;;;;2C61; E2 B1 A0 => +1
+ 2C61;LATIN SMALL LETTER L WITH DOUBLE BAR;Ll;0;L;;;;;N;;;2C60;;2C60
+ 2C62;LATIN CAPITAL LETTER L WITH MIDDLE TILDE;Lu;0;L;;;;;N;;;;026B; => C9 AB
+ 2C63;LATIN CAPITAL LETTER P WITH STROKE;Lu;0;L;;;;;N;;;;1D7D; => E1 B5 BD
+ 2C64;LATIN CAPITAL LETTER R WITH TAIL;Lu;0;L;;;;;N;;;;027D; => C9 BD
+ 2C65;LATIN SMALL LETTER A WITH STROKE;Ll;0;L;;;;;N;;;023A;;023A
+ 2C66;LATIN SMALL LETTER T WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;023E;;023E
+ 2C67;LATIN CAPITAL LETTER H WITH DESCENDER;Lu;0;L;;;;;N;;;;2C68; => E2 B1 A8
+ 2C68;LATIN SMALL LETTER H WITH DESCENDER;Ll;0;L;;;;;N;;;2C67;;2C67
+ 2C69;LATIN CAPITAL LETTER K WITH DESCENDER;Lu;0;L;;;;;N;;;;2C6A; => E2 B1 AA
+ 2C6A;LATIN SMALL LETTER K WITH DESCENDER;Ll;0;L;;;;;N;;;2C69;;2C69
+ 2C6B;LATIN CAPITAL LETTER Z WITH DESCENDER;Lu;0;L;;;;;N;;;;2C6C; => E2 B1 AC
+ 2C6C;LATIN SMALL LETTER Z WITH DESCENDER;Ll;0;L;;;;;N;;;2C6B;;2C6B
+ 2C6D;LATIN CAPITAL LETTER ALPHA;Lu;0;L;;;;;N;;;;0251; => C9 91
+ 2C6E;LATIN CAPITAL LETTER M WITH HOOK;Lu;0;L;;;;;N;;;;0271; => C9 B1
+ 2C6F;LATIN CAPITAL LETTER TURNED A;Lu;0;L;;;;;N;;;;0250; => C9 90
+
+ 2C70;LATIN CAPITAL LETTER TURNED ALPHA;Lu;0;L;;;;;N;;;;0252; => C9 92
+ }
+ else if (c2 = #$B1) then
+ begin
+ case c3 of
+ #$A0: new_c3 := chr(ord(c3)+1);
+ #$A2,#$A4,#$AD..#$AF,#$B0:
+ begin
+ inc(InStr, 3);
+ OutStr^ := #$C9;
+ inc(OutStr);
+ case c3 of
+ #$A2: OutStr^ := #$AB;
+ #$A4: OutStr^ := #$BD;
+ #$AD: OutStr^ := #$91;
+ #$AE: OutStr^ := #$B1;
+ #$AF: OutStr^ := #$90;
+ #$B0: OutStr^ := #$92;
+ end;
+ inc(OutStr);
+ inc(CounterDiff, 1);
+ Continue;
+ end;
+ #$A3:
+ begin
+ new_c2 := #$B5;
+ new_c3 := #$BD;
+ end;
+ #$A7,#$A9,#$AB: new_c3 := chr(ord(c3)+1);
+ {
+ 2C71;LATIN SMALL LETTER V WITH RIGHT HOOK;Ll;0;L;;;;;N;;;;;
+ 2C72;LATIN CAPITAL LETTER W WITH HOOK;Lu;0;L;;;;;N;;;;2C73;
+ 2C73;LATIN SMALL LETTER W WITH HOOK;Ll;0;L;;;;;N;;;2C72;;2C72
+ 2C74;LATIN SMALL LETTER V WITH CURL;Ll;0;L;;;;;N;;;;;
+ 2C75;LATIN CAPITAL LETTER HALF H;Lu;0;L;;;;;N;;;;2C76;
+ 2C76;LATIN SMALL LETTER HALF H;Ll;0;L;;;;;N;;;2C75;;2C75
+ 2C77;LATIN SMALL LETTER TAILLESS PHI;Ll;0;L;;;;;N;;;;;
+ 2C78;LATIN SMALL LETTER E WITH NOTCH;Ll;0;L;;;;;N;;;;;
+ 2C79;LATIN SMALL LETTER TURNED R WITH TAIL;Ll;0;L;;;;;N;;;;;
+ 2C7A;LATIN SMALL LETTER O WITH LOW RING INSIDE;Ll;0;L;;;;;N;;;;;
+ 2C7B;LATIN LETTER SMALL CAPITAL TURNED E;Ll;0;L;;;;;N;;;;;
+ 2C7C;LATIN SUBSCRIPT SMALL LETTER J;Ll;0;L; 006A;;;;N;;;;;
+ 2C7D;MODIFIER LETTER CAPITAL V;Lm;0;L; 0056;;;;N;;;;;
+ 2C7E;LATIN CAPITAL LETTER S WITH SWASH TAIL;Lu;0;L;;;;;N;;;;023F; => C8 BF
+ 2C7F;LATIN CAPITAL LETTER Z WITH SWASH TAIL;Lu;0;L;;;;;N;;;;0240; => C9 80
+ }
+ #$B2,#$B5: new_c3 := chr(ord(c3)+1);
+ #$BE,#$BF:
+ begin
+ inc(InStr, 3);
+ case c3 of
+ #$BE: OutStr^ := #$C8;
+ #$BF: OutStr^ := #$C9;
+ end;
+ OutStr^ := #$C8;
+ inc(OutStr);
+ case c3 of
+ #$BE: OutStr^ := #$BF;
+ #$BF: OutStr^ := #$80;
+ end;
+ inc(OutStr);
+ inc(CounterDiff, 1);
+ Continue;
+ end;
+ end;
+ end
+ {
+ 2C80;COPTIC CAPITAL LETTER ALFA;Lu;0;L;;;;;N;;;;2C81; E2 B2 80 => E2 B2 81
+ ...
+ 2CBE;COPTIC CAPITAL LETTER OLD COPTIC OOU;Lu;0;L;;;;;N;;;;2CBF; E2 B2 BE => E2 B2 BF
+ 2CBF;COPTIC SMALL LETTER OLD COPTIC OOU;Ll;0;L;;;;;N;;;2CBE;;2CBE
+ ...
+ 2CC0;COPTIC CAPITAL LETTER SAMPI;Lu;0;L;;;;;N;;;;2CC1; E2 B3 80 => E2 B2 81
+ 2CC1;COPTIC SMALL LETTER SAMPI;Ll;0;L;;;;;N;;;2CC0;;2CC0
+ ...
+ 2CE2;COPTIC CAPITAL LETTER OLD NUBIAN WAU;Lu;0;L;;;;;N;;;;2CE3; E2 B3 A2 => E2 B3 A3
+ 2CE3;COPTIC SMALL LETTER OLD NUBIAN WAU;Ll;0;L;;;;;N;;;2CE2;;2CE2 <=
+ }
+ else if (c2 = #$B2) then
+ begin
+ if ord(c3) mod 2 = 0 then new_c3 := chr(ord(c3) + 1);
+ end
+ else if (c2 = #$B3) and (c3 in [#$80..#$A3]) then
+ begin
+ if ord(c3) mod 2 = 0 then new_c3 := chr(ord(c3) + 1);
+ end;
+
+ if (CounterDiff <> 0) then
+ begin
+ OutStr^ := new_c1;
+ OutStr[1] := new_c2;
+ OutStr[2] := new_c3;
+ end
+ else
+ begin
+ if c1 <> new_c1 then OutStr^ := new_c1;
+ if c2 <> new_c2 then OutStr[1] := new_c2;
+ if c3 <> new_c3 then OutStr[2] := new_c3;
+ end;
+
+ inc(InStr, 3);
+ inc(OutStr, 3);
+ end;
+ {
+ FF21;FULLWIDTH LATIN CAPITAL LETTER A;Lu;0;L; 0041;;;;N;;;;FF41; EF BC A1 => EF BD 81
+ ...
+ FF3A;FULLWIDTH LATIN CAPITAL LETTER Z;Lu;0;L; 005A;;;;N;;;;FF5A; EF BC BA => EF BD 9A
+ }
+ #$EF:
+ begin
+ c2 := InStr[1];
+ c3 := InStr[2];
+
+ if (c2 = #$BC) and (c3 in [#$A1..#$BA]) then
+ begin
+ OutStr^ := c1;
+ OutStr[1] := #$BD;
+ OutStr[2] := chr(ord(c3) - $20);
+ end;
+
+ if (CounterDiff <> 0) then
+ begin
+ OutStr^ := c1;
+ OutStr[1] := c2;
+ OutStr[2] := c3;
+ end;
+
+ inc(InStr, 3);
+ inc(OutStr, 3);
+ end;
+ else
+ // Copy the character if the string was disaligned by previous changes
+ if (CounterDiff <> 0) then OutStr^:= c1;
+ inc(InStr);
+ inc(OutStr);
+ end; // Case InStr^
+ end; // while
+
+ // Final correction of the buffer size
+ SetLength(Result,OutStr - PChar(Result));
+end;
+
+function UTF8LowerString(const s: string): string; inline;
+begin
+ Result:=UTF8LowerCase(s);
+end;
+
+
+{
+ AInStr - The input string
+ ALanguage - The language. Use '' for maximum speed if one desires to ignore the language
+ The language should be specified in the format from ISO 639-1,
+ which uses 2 characters to represent each language.
+ If the language has no code in ISO 639-1, then the 3-chars code
+ from ISO 639-2 should be used.
+ Example: "tr" - Turkish language locale
+
+ Data from here: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
+
+ The columns in the file UnicodeData.txt are explained here:
+ http://www.ksu.ru/eng/departments/ktk/test/perl/lib/unicode/UCDFF301.html#Case Mappings
+}
+function UTF8UpperCase(const AInStr: string; ALanguage: string=''): string;
+var
+ i, InCounter, OutCounter: PtrInt;
+ OutStr: PChar;
+ CharLen: integer;
+ CharProcessed: Boolean;
+ NewCharLen: integer;
+ NewChar, OldChar: Word;
+ // Language identification
+ IsTurkish: Boolean;
+
+ procedure CorrectOutStrSize(AOldCharSize, ANewCharSize: Integer);
+ begin
+ if not (ANewCharSize > AOldCharSize) then Exit; // no correction needed
+ if (ANewCharSize > 20) or (AOldCharSize > 20) then Exit; // sanity check
+ // Fix for bug 23428
+ // If the string wasn't decreased by previous char changes,
+ // and our current operation will make it bigger, then for safety
+ // increase the buffer
+ if (ANewCharSize > AOldCharSize) and (OutCounter >= InCounter-1) then
+ begin
+ SetLength(Result, Length(Result)+ANewCharSize-AOldCharSize);
+ OutStr := PChar(Result);
+ end;
+ end;
+
+begin
+ // Start with the same string, and progressively modify
+ Result:=AInStr;
+ UniqueString(Result);
+ OutStr := PChar(Result);
+
+ // Language identification
+ IsTurkish := (ALanguage = 'tr') or (ALanguage = 'az'); // Turkish and Azeri have a special handling
+
+ InCounter:=1; // for AInStr
+ OutCounter := 0; // for Result
+ while InCounter<=length(AInStr) do
+ begin
+ { First ASCII chars }
+ if (AInStr[InCounter] <= 'z') and (AInStr[InCounter] >= 'a') then
+ begin
+ // Special turkish handling
+ // small dotted i to capital dotted i
+ if IsTurkish and (AInStr[InCounter] = 'i') then
+ begin
+ SetLength(Result,Length(Result)+1);// Increase the buffer
+ OutStr := PChar(Result);
+ OutStr[OutCounter]:=#$C4;
+ OutStr[OutCounter+1]:=#$B0;
+ inc(InCounter);
+ inc(OutCounter,2);
+ end
+ else
+ begin
+ OutStr[OutCounter]:=chr(ord(AInStr[InCounter])-32);
+ inc(InCounter);
+ inc(OutCounter);
+ end;
+ end
+ else { Now everything else }
+ begin
+ CharLen := UTF8CodepointSize(@AInStr[InCounter]);
+ CharProcessed := False;
+ NewCharLen := CharLen;
+
+ if CharLen = 2 then
+ begin
+ OldChar := (Ord(AInStr[InCounter]) shl 8) or Ord(AInStr[InCounter+1]);
+ NewChar := 0;
+
+ // Major processing
+ case OldChar of
+ // Latin Characters 0000–0FFF http://en.wikibooks.org/wiki/Unicode/Character_reference/0000-0FFF
+ $C39F: NewChar := $5353; // ß => SS
+ $C3A0..$C3B6,$C3B8..$C3BE: NewChar := OldChar - $20;
+ $C3BF: NewChar := $C5B8; // ÿ
+ $C481..$C4B0: if OldChar mod 2 = 1 then NewChar := OldChar - 1;
+ // 0130 = C4 B0
+ // turkish small undotted i to capital undotted i
+ $C4B1:
+ begin
+ OutStr[OutCounter]:='I';
+ NewCharLen := 1;
+ CharProcessed := True;
+ end;
+ $C4B2..$C4B7: if OldChar mod 2 = 1 then NewChar := OldChar - 1;
+ // $C4B8: ĸ without upper/lower
+ $C4B9..$C4BF: if OldChar mod 2 = 0 then NewChar := OldChar - 1;
+ $C580: NewChar := $C4BF; // border between bytes
+ $C581..$C588: if OldChar mod 2 = 0 then NewChar := OldChar - 1;
+ // $C589 ʼn => ?
+ $C58A..$C5B7: if OldChar mod 2 = 1 then NewChar := OldChar - 1;
+ // $C5B8: // Ÿ already uppercase
+ $C5B9..$C5BE: if OldChar mod 2 = 0 then NewChar := OldChar - 1;
+ $C5BF: // 017F
+ begin
+ OutStr[OutCounter]:='S';
+ NewCharLen := 1;
+ CharProcessed := True;
+ end;
+ // 0180 = C6 80 -> A convoluted part
+ $C680: NewChar := $C983;
+ $C682..$C685: if OldChar mod 2 = 1 then NewChar := OldChar - 1;
+ $C688: NewChar := $C687;
+ $C68C: NewChar := $C68B;
+ // 0190 = C6 90 -> A convoluted part
+ $C692: NewChar := $C691;
+ $C695: NewChar := $C7B6;
+ $C699: NewChar := $C698;
+ $C69A: NewChar := $C8BD;
+ $C69E: NewChar := $C8A0;
+ // 01A0 = C6 A0 -> A convoluted part
+ $C6A0..$C6A5: if OldChar mod 2 = 1 then NewChar := OldChar - 1;
+ $C6A8: NewChar := $C6A7;
+ $C6AD: NewChar := $C6AC;
+ // 01B0 = C6 B0
+ $C6B0: NewChar := $C6AF;
+ $C6B3..$C6B6: if OldChar mod 2 = 0 then NewChar := OldChar - 1;
+ $C6B9: NewChar := $C6B8;
+ $C6BD: NewChar := $C6BC;
+ $C6BF: NewChar := $C7B7;
+ // 01C0 = C7 80
+ $C784..$C786: NewChar := $C784;
+ $C787..$C789: NewChar := $C787;
+ $C78A..$C78C: NewChar := $C78A;
+ $C78E: NewChar := $C78D;
+ // 01D0 = C7 90
+ $C790: NewChar := $C78F;
+ $C791..$C79C: if OldChar mod 2 = 0 then NewChar := OldChar - 1;
+ $C79D: NewChar := $C68E;
+ $C79F: NewChar := $C79E;
+ // 01E0 = C7 A0
+ $C7A0..$C7AF: if OldChar mod 2 = 1 then NewChar := OldChar - 1;
+ // 01F0 = C7 B0
+ $C7B2..$C7B3: NewChar := $C7B1;
+ $C7B5: NewChar := $C7B4;
+ $C7B8..$C7BF: if OldChar mod 2 = 1 then NewChar := OldChar - 1;
+ // 0200 = C8 80
+ // 0210 = C8 90
+ $C880..$C89F: if OldChar mod 2 = 1 then NewChar := OldChar - 1;
+ // 0220 = C8 A0
+ // 0230 = C8 B0
+ $C8A2..$C8B3: if OldChar mod 2 = 1 then NewChar := OldChar - 1;
+ $C8BC: NewChar := $C8BB;
+ $C8BF:
+ begin
+ CorrectOutStrSize(2, 3);
+ OutStr[OutCounter] := #$E2;
+ OutStr[OutCounter+1]:= #$B1;
+ OutStr[OutCounter+2]:= #$BE;
+ NewCharLen := 3;
+ CharProcessed := True;
+ end;
+ // 0240 = C9 80
+ $C980:
+ begin
+ CorrectOutStrSize(2, 3);
+ OutStr[OutCounter] := #$E2;
+ OutStr[OutCounter+1]:= #$B1;
+ OutStr[OutCounter+2]:= #$BF;
+ NewCharLen := 3;
+ CharProcessed := True;
+ end;
+ $C982: NewChar := $C981;
+ $C986..$C98F: if OldChar mod 2 = 1 then NewChar := OldChar - 1;
+ // 0250 = C9 90
+ $C990:
+ begin
+ CorrectOutStrSize(2, 3);
+ OutStr[OutCounter] := #$E2;
+ OutStr[OutCounter+1]:= #$B1;
+ OutStr[OutCounter+2]:= #$AF;
+ NewCharLen := 3;
+ CharProcessed := True;
+ end;
+ $C991:
+ begin
+ CorrectOutStrSize(2, 3);
+ OutStr[OutCounter] := #$E2;
+ OutStr[OutCounter+1]:= #$B1;
+ OutStr[OutCounter+2]:= #$AD;
+ NewCharLen := 3;
+ CharProcessed := True;
+ end;
+ $C992:
+ begin
+ CorrectOutStrSize(2, 3);
+ OutStr[OutCounter] := #$E2;
+ OutStr[OutCounter+1]:= #$B1;
+ OutStr[OutCounter+2]:= #$B0;
+ NewCharLen := 3;
+ CharProcessed := True;
+ end;
+ $C993: NewChar := $C681;
+ $C994: NewChar := $C686;
+ $C996: NewChar := $C689;
+ $C997: NewChar := $C68A;
+ $C999: NewChar := $C68F;
+ $C99B: NewChar := $C690;
+ // 0260 = C9 A0
+ $C9A0: NewChar := $C693;
+ $C9A3: NewChar := $C694;
+ $C9A5:
+ begin
+ CorrectOutStrSize(2, 3);
+ OutStr[OutCounter] := #$EA;
+ OutStr[OutCounter+1]:= #$9E;
+ OutStr[OutCounter+2]:= #$8D;
+ NewCharLen := 3;
+ CharProcessed := True;
+ end;
+ $C9A8: NewChar := $C697;
+ $C9A9: NewChar := $C696;
+ $C9AB:
+ begin
+ CorrectOutStrSize(2, 3);
+ OutStr[OutCounter] := #$E2;
+ OutStr[OutCounter+1]:= #$B1;
+ OutStr[OutCounter+2]:= #$A2;
+ NewCharLen := 3;
+ CharProcessed := True;
+ end;
+ $C9AF: NewChar := $C69C;
+ // 0270 = C9 B0
+ $C9B1:
+ begin
+ CorrectOutStrSize(2, 3);
+ OutStr[OutCounter] := #$E2;
+ OutStr[OutCounter+1]:= #$B1;
+ OutStr[OutCounter+2]:= #$AE;
+ NewCharLen := 3;
+ CharProcessed := True;
+ end;
+ $C9B2: NewChar := $C69D;
+ $C9B5: NewChar := $C69F;
+ $C9BD:
+ begin
+ CorrectOutStrSize(2, 3);
+ OutStr[OutCounter] := #$E2;
+ OutStr[OutCounter+1]:= #$B1;
+ OutStr[OutCounter+2]:= #$A4;
+ NewCharLen := 3;
+ CharProcessed := True;
+ end;
+ // 0280 = CA 80
+ $CA80: NewChar := $C6A6;
+ $CA83: NewChar := $C6A9;
+ $CA88: NewChar := $C6AE;
+ $CA89: NewChar := $C984;
+ $CA8A: NewChar := $C6B1;
+ $CA8B: NewChar := $C6B2;
+ $CA8C: NewChar := $C985;
+ // 0290 = CA 90
+ $CA92: NewChar := $C6B7;
+ {
+ 03A0 = CE A0
+
+ 03AC;GREEK SMALL LETTER ALPHA WITH TONOS;Ll;0;L;03B1 0301;;;;N;GREEK SMALL LETTER ALPHA TONOS;;0386;;0386
+ 03AD;GREEK SMALL LETTER EPSILON WITH TONOS;Ll;0;L;03B5 0301;;;;N;GREEK SMALL LETTER EPSILON TONOS;;0388;;0388
+ 03AE;GREEK SMALL LETTER ETA WITH TONOS;Ll;0;L;03B7 0301;;;;N;GREEK SMALL LETTER ETA TONOS;;0389;;0389
+ 03AF;GREEK SMALL LETTER IOTA WITH TONOS;Ll;0;L;03B9 0301;;;;N;GREEK SMALL LETTER IOTA TONOS;;038A;;038A
+ }
+ $CEAC: NewChar := $CE86;
+ $CEAD: NewChar := $CE88;
+ $CEAE: NewChar := $CE89;
+ $CEAF: NewChar := $CE8A;
+ {
+ 03B0 = CE B0
+
+ 03B0;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS;Ll;0;L;03CB 0301;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS TONOS;;;;
+ 03B1;GREEK SMALL LETTER ALPHA;Ll;0;L;;;;;N;;;0391;;0391
+ ...
+ 03BF;GREEK SMALL LETTER OMICRON;Ll;0;L;;;;;N;;;039F;;039F
+ }
+ $CEB1..$CEBF: NewChar := OldChar - $20; // Greek Characters
+ {
+ 03C0 = CF 80
+
+ 03C0;GREEK SMALL LETTER PI;Ll;0;L;;;;;N;;;03A0;;03A0 CF 80 => CE A0
+ 03C1;GREEK SMALL LETTER RHO;Ll;0;L;;;;;N;;;03A1;;03A1
+ 03C2;GREEK SMALL LETTER FINAL SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3
+ 03C3;GREEK SMALL LETTER SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3
+ 03C4;GREEK SMALL LETTER TAU;Ll;0;L;;;;;N;;;03A4;;03A4
+ ....
+ 03CB;GREEK SMALL LETTER UPSILON WITH DIALYTIKA;Ll;0;L;03C5 0308;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS;;03AB;;03AB
+ 03CC;GREEK SMALL LETTER OMICRON WITH TONOS;Ll;0;L;03BF 0301;;;;N;GREEK SMALL LETTER OMICRON TONOS;;038C;;038C
+ 03CD;GREEK SMALL LETTER UPSILON WITH TONOS;Ll;0;L;03C5 0301;;;;N;GREEK SMALL LETTER UPSILON TONOS;;038E;;038E
+ 03CE;GREEK SMALL LETTER OMEGA WITH TONOS;Ll;0;L;03C9 0301;;;;N;GREEK SMALL LETTER OMEGA TONOS;;038F;;038F
+ 03CF;GREEK CAPITAL KAI SYMBOL;Lu;0;L;;;;;N;;;;03D7;
+ }
+ $CF80,$CF81,$CF83..$CF8B: NewChar := OldChar - $E0; // Greek Characters
+ $CF82: NewChar := $CEA3;
+ $CF8C: NewChar := $CE8C;
+ $CF8D: NewChar := $CE8E;
+ $CF8E: NewChar := $CE8F;
+ {
+ 03D0 = CF 90
+
+ 03D0;GREEK BETA SYMBOL;Ll;0;L; 03B2;;;;N;GREEK SMALL LETTER CURLED BETA;;0392;;0392 CF 90 => CE 92
+ 03D1;GREEK THETA SYMBOL;Ll;0;L; 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398 => CE 98
+ 03D5;GREEK PHI SYMBOL;Ll;0;L; 03C6;;;;N;GREEK SMALL LETTER SCRIPT PHI;;03A6;;03A6 => CE A6
+ 03D6;GREEK PI SYMBOL;Ll;0;L; 03C0;;;;N;GREEK SMALL LETTER OMEGA PI;;03A0;;03A0 => CE A0
+ 03D7;GREEK KAI SYMBOL;Ll;0;L;;;;;N;;;03CF;;03CF => CF 8F
+ 03D9;GREEK SMALL LETTER ARCHAIC KOPPA;Ll;0;L;;;;;N;;;03D8;;03D8
+ 03DB;GREEK SMALL LETTER STIGMA;Ll;0;L;;;;;N;;;03DA;;03DA
+ 03DD;GREEK SMALL LETTER DIGAMMA;Ll;0;L;;;;;N;;;03DC;;03DC
+ 03DF;GREEK SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;03DE;;03DE
+ }
+ $CF90: NewChar := $CE92;
+ $CF91: NewChar := $CE98;
+ $CF95: NewChar := $CEA6;
+ $CF96: NewChar := $CEA0;
+ $CF97: NewChar := $CF8F;
+ $CF99..$CF9F: if OldChar mod 2 = 1 then NewChar := OldChar - 1;
+ // 03E0 = CF A0
+ $CFA0..$CFAF: if OldChar mod 2 = 1 then NewChar := OldChar - 1;
+ {
+ 03F0 = CF B0
+
+ 03F0;GREEK KAPPA SYMBOL;Ll;0;L; 03BA;;;;N;GREEK SMALL LETTER SCRIPT KAPPA;;039A;;039A => CE 9A
+ 03F1;GREEK RHO SYMBOL;Ll;0;L; 03C1;;;;N;GREEK SMALL LETTER TAILED RHO;;03A1;;03A1 => CE A1
+ 03F2;GREEK LUNATE SIGMA SYMBOL;Ll;0;L; 03C2;;;;N;GREEK SMALL LETTER LUNATE SIGMA;;03F9;;03F9
+ 03F5;GREEK LUNATE EPSILON SYMBOL;Ll;0;L; 03B5;;;;N;;;0395;;0395 => CE 95
+ 03F8;GREEK SMALL LETTER SHO;Ll;0;L;;;;;N;;;03F7;;03F7
+ 03FB;GREEK SMALL LETTER SAN;Ll;0;L;;;;;N;;;03FA;;03FA
+ }
+ $CFB0: NewChar := $CE9A;
+ $CFB1: NewChar := $CEA1;
+ $CFB2: NewChar := $CFB9;
+ $CFB5: NewChar := $CE95;
+ $CFB8: NewChar := $CFB7;
+ $CFBB: NewChar := $CFBA;
+ // 0400 = D0 80 ... 042F everything already uppercase
+ // 0430 = D0 B0
+ $D0B0..$D0BF: NewChar := OldChar - $20; // Cyrillic alphabet
+ // 0440 = D1 80
+ $D180..$D18F: NewChar := OldChar - $E0; // Cyrillic alphabet
+ // 0450 = D1 90
+ $D190..$D19F: NewChar := OldChar - $110; // Cyrillic alphabet
+ end;
+
+ if NewChar <> 0 then
+ begin
+ OutStr[OutCounter] := Chr(Hi(NewChar));
+ OutStr[OutCounter+1]:= Chr(Lo(NewChar));
+ CharProcessed := True;
+ end;
+ end;
+
+ // Copy the character if the string was disaligned by previous changed
+ // and no processing was done in this character
+ if (InCounter <> OutCounter+1) and (not CharProcessed) then
+ begin
+ for i := 0 to CharLen-1 do
+ OutStr[OutCounter+i] :=AInStr[InCounter+i];
+ end;
+
+ inc(InCounter, CharLen);
+ inc(OutCounter, NewCharLen);
+ end;
+ end; // while
+
+ // Final correction of the buffer size
+ SetLength(Result,OutCounter);
+end;
+
+function UTF8UpperString(const s: string): string; inline;
+begin
+ Result:=UTF8UpperCase(s);
+end;
+
+
+function FindInvalidUTF8Codepoint(p: PChar; Count: PtrInt; StopOnNonUTF8: Boolean): PtrInt;
+// return -1 if ok
+var
+ CharLen: Integer;
+ c: Byte;
+begin
+ if (p<>nil) then begin
+ Result:=0;
+ while Result=192) then
+ exit;
+ CharLen:=1;
+ end else if c<=%11011111 then begin
+ // could be 2 byte character (%110xxxxx %10xxxxxx)
+ if (Result%11110100) then
+ exit; // out of range U+10FFFF
+ if (c=%11110100) and (ord(p[1])>%10001111) then
+ exit; // out of range U+10FFFF
+ CharLen:=4;
+ end else
+ exit; // missing following bytes
+ end
+ else begin
+ if StopOnNonUTF8 then
+ exit;
+ CharLen:=1;
+ end;
+ inc(Result,CharLen);
+ inc(p,CharLen);
+ if Result>Count then begin
+ dec(Result,CharLen);
+ exit; // missing following bytes
+ end;
+ end;
+ end;
+ // ok
+ Result:=-1;
+end;
+
+function FindInvalidUTF8Character(p: PChar; Count: PtrInt; StopOnNonUTF8: Boolean = true): PtrInt;
+begin
+ Result := FindInvalidUTF8Codepoint(p, Count, StopOnNonUTF8);
+end;
+
+{
+ Translates escape characters inside an UTF8 encoded string into
+ human readable format.
+ Mainly used for logging purposes.
+ Parameters:
+ S : Input string. Must be UTF8 encoded.
+ EscapeMode: controls the human readable format for escape characters.
+}
+function Utf8EscapeControlChars(S: String; EscapeMode: TEscapeMode = emPascal): String;
+const
+ //lookuptables are about 1.8 to 1.3 times faster than a function using IntToStr or IntToHex
+ PascalEscapeStrings: Array[#0..#31] of string = (
+ '#00', '#01', '#02', '#03', '#04', '#05', '#06', '#07',
+ '#08', '#09', '#10', '#11', '#12', '#13', '#14', '#15',
+ '#16', '#17', '#18', '#19', '#20', '#21', '#22', '#23',
+ '#24', '#25', '#26', '#27', '#28', '#29', '#30', '#31');
+ CEscapeStrings: Array[#0..#31] of string = (
+ '\0' , '\0x01', '\0x02', '\0x03', '\0x04', '\0x05', '\0x06', '\a' ,
+ '\b' , '\t' , '\r' , '\v' , '\f' , '\n' , '\0x0E', '\0x0F',
+ '\0x10', '\0x11', '\0x12', '\0x13', '\0x14', '\0x15', '\0x16', '\0x17',
+ '\0x18', '\0x19', '\0x1A', '\e' , '\0x1C', '\0x1D', '\0x1E', '\0x1F');
+ HexEscapeCStrings: Array[#0..#31] of string = (
+ '\0x00', '\0x01', '\0x02', '\0x03', '\0x04', '\0x05', '\0x06', '\0x07',
+ '\0x08', '\0x09', '\0x0A', '\0x0B', '\0x0C', '\0x0D', '\0x0E', '\0x0F',
+ '\0x10', '\0x11', '\0x12', '\0x13', '\0x14', '\0x15', '\0x16', '\0x17',
+ '\0x18', '\0x19', '\0x1A', '\0x1B', '\0x1C', '\0x1D', '\0x1E', '\0x1F');
+ HexEscapePascalStrings: Array[#0..#31] of string = (
+ '#$00', '#$01', '#$02', '#$03', '#$04', '#$05', '#$06', '#$07',
+ '#$08', '#$09', '#$0A', '#$0B', '#$0C', '#$0D', '#$0E', '#$0F',
+ '#$10', '#$11', '#$12', '#$13', '#$14', '#$15', '#$16', '#$17',
+ '#$18', '#$19', '#$1A', '#$1B', '#$1C', '#$1D', '#$1E', '#$1F');
+ AsciiControlStrings: Array[#0..#31] of string = (
+ '[NUL]', '[SOH]', '[STX]', '[ETX]', '[EOT]', '[ENQ]', '[ACK]', '[BEL]',
+ '[BS]' , '[HT]' , '[LF]' , '[VT]' , '[FF]' , '[CR]' , '[SO]' , '[SI]' ,
+ '[DLE]', '[DC1]', '[DC2]', '[DC3]', '[DC4]', '[NAK]', '[SYN]', '[ETB]',
+ '[CAN]', '[EM]' , '[SUB]', '[ESC]', '[FS]' , '[GS]' , '[RS]' , '[US]');
+var
+ Ch: Char;
+ i: Integer;
+begin
+ if FindInvalidUTF8Codepoint(PChar(S), Length(S)) <> -1 then
+ begin
+ UTF8FixBroken(S);
+ end;
+ Result := '';
+ //a byte < 127 cannot be part of a multi-byte codepoint, so this is safe
+ for i := 1 to Length(S) do
+ begin
+ Ch := S[i];
+ if (Ch < #32) then
+ begin
+ case EscapeMode of
+ emPascal: Result := Result + PascalEscapeStrings[Ch];
+ emHexPascal: Result := Result + HexEscapePascalStrings[Ch];
+ emHexC: Result := Result + HexEscapeCStrings[Ch];
+ emC: Result := Result + CEscapeStrings[Ch];
+ emAsciiControlNames: Result := Result + AsciiControlStrings[Ch];
+ end;//case
+ end
+ else
+ Result := Result + Ch;
+ end;
+end;
+
+function UTF8StringOfChar(AUtf8Char: String; N: Integer): String;
+var
+ UCharLen, i: Integer;
+ C1, C2, C3: Char;
+ PC: PChar;
+begin
+ Result := '';
+ if (N <= 0) or (Utf8Length(AUtf8Char) <> 1) then Exit;
+ UCharLen := Length(AUtf8Char);
+ Case UCharLen of
+ 1: Result := StringOfChar(AUtf8Char[1], N);
+ 2:
+ begin
+ SetLength(Result, 2 * N);
+ System.FillWord(Result[1], N, PWord(Pointer(AUtf8Char))^);
+ end;
+ 3:
+ begin
+ SetLength(Result, 3 * N);
+ C1 := AUtf8Char[1];
+ C2 := AUtf8Char[2];
+ C3 := AUtf8Char[3];
+ PC := PChar(Result);
+ for i:=1 to N do
+ begin
+ PC[0] := C1;
+ PC[1] := C2;
+ PC[2] := C3;
+ inc(PC,3);
+ end;
+ end;
+ 4:
+ begin
+ SetLength(Result, 4 * N);
+ System.FillDWord(Result[1], N, PDWord(Pointer(AUtf8Char))^);
+ end;
+ else
+ begin
+ //In November 2003 UTF-8 was restricted by RFC 3629 to four bytes to match
+ //the constraints of the UTF-16 character encoding.
+ //http://en.wikipedia.org/wiki/UTF-8
+ Result := StringOfChar('?', N);
+ end;
+ end;
+end;
+
+function UTF8AddChar(AUtf8Char: String; const S: String; N: Integer): String;
+var
+ L : Integer;
+begin
+ Result := S;
+ if Utf8Length(AUtf8Char) <> 1 then Exit;
+ L := Utf8Length(Result);
+ if L < N then
+ Result := Utf8StringOfChar(AUtf8Char, N-l) + Result;
+end;
+
+function UTF8AddCharR(AUtf8Char: String; const S: String; N: Integer): String;
+var
+ L : Integer;
+begin
+ Result := S;
+ if Utf8Length(AUtf8Char) <> 1 then Exit;
+ L := Utf8Length(Result);
+ if L < N then
+ Result := Result + Utf8StringOfChar(AUtf8Char, N-l);
+end;
+
+function UTF8PadLeft(const S: String; const N: Integer; const AUtf8Char: String = #32): String; inline;
+begin
+ Result := Utf8AddChar(AUtf8Char, S, N);
+end;
+
+function UTF8PadRight(const S: String; const N: Integer; const AUtf8Char: String = #32): String; inline;
+begin
+ Result := Utf8AddCharR(AUtf8Char, S, N);
+end;
+
+function UTF8PadCenter(const S: String; const N: Integer; const AUtf8Char: String = #32): String;
+var
+ ULen: PtrInt;
+begin
+ ULen := Utf8Length(S);
+ if ULen < N then
+ begin
+ Result := Utf8StringOfChar(AUtf8Char,(N div 2) - (ULen div 2)) + S;
+ Result := Result + Utf8StringOfChar(AUtf8Char, N - Utf8Length(Result));
+ end
+ else
+ Result := S;
+end;
+
+function UTF8LeftStr(const AText: String; const ACount: Integer): String; inline;
+begin
+ Result := Utf8Copy(AText,1,ACount);
+end;
+
+function UTF8RightStr(const AText: String; const ACount: Integer): String;
+var
+ j,l:integer;
+begin
+ l := Utf8Length(AText);
+ j := ACount;
+ if (j > l) then j := l;
+ Result := Utf8Copy(AText,l-j+1,j);
+end;
+
+function UTF8QuotedStr(const S, Quote: string): string;
+// replace all Quote in S with double Quote and enclose the result in Quote.
+var
+ QuoteC: Char;
+ p, QuoteP, CopyPos: PChar;
+ QuoteLen: SizeInt;
+begin
+ Result:=Quote;
+ p:=PChar(S);
+ CopyPos:=p;
+ QuoteC:=Quote[1];
+ QuoteP:=PChar(Quote);
+ QuoteLen:=length(Quote);
+ repeat
+ if (p^=#0) and (p-PChar(S)=length(S)) then
+ break;
+ if (p^=QuoteC) and CompareMem(p,QuoteP,QuoteLen) then begin
+ inc(p,QuoteLen);
+ Result+=copy(S,CopyPos-PChar(S)+1,p-CopyPos)+Quote;
+ CopyPos:=p;
+ end else
+ inc(p);
+ until false;
+ Result+=copy(S,CopyPos-PChar(S)+1,p-CopyPos)+Quote;
+end;
+
+function UTF8StartsText(const ASubText, AText: string): Boolean;
+var
+ TextLen, SubTextLen: PtrInt;
+begin
+ Result := False;
+ if (ASubText <> '') then
+ begin
+ TextLen := Utf8Length(AText);
+ SubTextLen := Utf8Length(ASubText);
+ if (TextLen >= SubTextLen) then
+ Result := (UTF8CompareLatinTextFast(Utf8Copy(AText,1,SubTextLen),ASubText) = 0);
+ end;
+end;
+
+function UTF8EndsText(const ASubText, AText: string): Boolean;
+var
+ TextLen, SubTextLen: PtrInt;
+begin
+ Result := False;
+ if (ASubText <> '') then
+ begin
+ TextLen := Utf8Length(AText);
+ SubTextLen := Utf8Length(ASubText);
+ if (TextLen >= SubTextLen) then
+ Result := (UTF8CompareLatinTextFast(Utf8Copy(AText,TextLen-SubTextLen+1,SubTextLen),ASubText) = 0);
+ end;
+end;
+
+function UTF8ReverseString(p: PChar; const ByteCount: LongInt): string;
+var
+ CharLen, rBytePos: LongInt;
+begin
+ SetLength(Result{%H-}, ByteCount);
+ rBytePos := ByteCount + 1;
+ while (rBytePos > 1) do
+ begin
+ CharLen:=UTF8CodepointSize(p);
+ Dec(rBytePos, CharLen);
+ System.Move(p^, Result[rBytePos], CharLen);
+ Inc(p, CharLen);
+ end;
+end;
+
+function UTF8ReverseString(const AText: string): string; inline;
+begin
+ Result := UTF8ReverseString(PChar(AText), length(AText));
+end;
+
+function UTF8RPos(const Substr, Source: string): PtrInt;
+var
+ pRev: PtrInt;
+begin
+ pRev := RPos(Substr, Source); // Scan from the end.
+ Result := UTF8Length(PChar(Source), pRev); // Length of the leading part.
+end;
+
+function UTF8WrapText(S, BreakStr: string; BreakChars: TSysCharSet; MaxCol: integer): string;
+var
+ P : PChar;
+ RightSpace : integer = 0;
+ N : integer = 0;
+ Len : integer = 0;
+ i, j : integer;
+ CharLen, ResultLen, RP : integer;
+begin
+ Result := '';
+ if (S = '') or (MaxCol = 0) or (BreakStr = '') or (BreakChars = []) then Exit;
+ P := PChar(S);
+ while P^ <> #0 do
+ begin
+ CharLen := UTF8CodepointSize(P);
+ i := 1;
+ j := 0;
+ ResultLen := Length(Result);
+ SetLength(Result, ResultLen + CharLen);
+ while i <= CharLen do
+ begin
+ Result[ResultLen + i] := (P + J)^;
+ Inc(i);
+ Inc(j);
+ end;
+ Inc(N);
+ if P^ = BreakStr[Length(BreakStr)] then
+ N := 0;
+ if N > MaxCol then
+ begin
+ Len := Length(Result);
+ RP := Len;
+ while not (Result[RP] in BreakChars) do
+ Dec(RP);
+ RightSpace := Len - RP;
+ if (RightSpace > 0) and (RightSpace < MaxCol) then
+ begin
+ Dec(P, RightSpace);
+ SetLength(Result, Len - RightSpace);
+ end;
+ Result := Result + BreakStr;
+ N := 0;
+ end;
+ Inc(P, CharLen);
+ end;
+end;
+
+function UTF8WrapText(S: string; MaxCol: integer): string;
+begin
+ Result := UTF8WrapText(S, LineEnding, [' ', '-', #9], MaxCol);
+end;
+
+function UTF8Trim(const s: string; Flags: TUTF8TrimFlags): string;
+var
+ p: PChar;
+ u: Cardinal;
+ StartP: PtrUInt;
+ l: Integer;
+ KeepAllNonASCII: boolean;
+begin
+ Result:=s;
+ if Result='' then exit;
+ KeepAllNonASCII:=[u8tKeepControlCodes,u8tKeepNoBreakSpaces]*Flags=[u8tKeepControlCodes,u8tKeepNoBreakSpaces];
+ if not (u8tKeepStart in Flags) then begin
+ // trim start
+ p:=PChar(Result);
+ repeat
+ l:=1;
+ case p^ of
+ #0:
+ if p-PChar(Result)=length(Result) then
+ begin
+ // everything was trimmed
+ exit('')
+ end else if u8tKeepControlCodes in Flags then
+ break;
+ ' ': ;
+ #10,#13:
+ if u8tKeepLineBreaks in Flags then
+ break;
+ #9:
+ if u8tKeepTabs in Flags then
+ break;
+ #1..#8,#11,#12,#14..#31,#127:
+ if u8tKeepControlCodes in Flags then
+ break;
+ #128..#255:
+ begin
+ if KeepAllNonASCII then break;
+ u:=UTF8CodepointToUnicode(p,l);
+ if (l<=1) then break; // invalid character
+ case u of
+ 128..159, // C1 set of control codes
+ 8206, 8207: // left-to-right, right-to-left mark
+ if u8tKeepControlCodes in Flags then break;
+ 160, // no break space
+ $2007, // figure space
+ $2026, // narrow no-break space
+ $FEFF: // zero with no-break space
+ if u8tKeepNoBreakSpaces in Flags then break;
+ else
+ break;
+ end;
+ end;
+ else
+ break;
+ end;
+ inc(p,l);
+ until false;
+ if p>PChar(Result) then begin
+ Result:=copy(Result,p-PChar(Result)+1,length(Result));
+ if Result='' then exit;
+ end;
+ end;
+
+ if not (u8tKeepEnd in Flags) then begin
+ // trim end
+ p:=@Result[length(Result)];
+ repeat
+ case p^ of
+ #0:
+ if u8tKeepControlCodes in Flags then
+ break;
+ ' ': ;
+ #10,#13:
+ if u8tKeepLineBreaks in Flags then
+ break;
+ #9:
+ if u8tKeepTabs in Flags then
+ break;
+ #1..#8,#11,#12,#14..#31,#127:
+ if u8tKeepControlCodes in Flags then
+ break;
+ #128..#255:
+ begin
+ if KeepAllNonASCII then break;
+ StartP:=UTF8FindNearestCharStart(PChar(Result),length(Result),p-PChar(Result));
+ u:=UTF8CodepointToUnicode(PChar(Result)+StartP,l);
+ if (l<=1) then break; // invalid character
+ case u of
+ 128..159, // C1 set of control codes
+ 8206, 8207: // left-to-right, right-to-left mark
+ if u8tKeepControlCodes in Flags then break;
+ 160, // no break space
+ $2007, // figure space
+ $2026, // narrow no-break space
+ $FEFF: // zero with no-break space
+ if u8tKeepNoBreakSpaces in Flags then break;
+ else
+ break;
+ end;
+ p:=PChar(Result)+StartP;
+ end;
+ else
+ break;
+ end;
+ dec(p);
+ until p S2
+ -2: if S1 < S2, comparison ended at a different byte in an invalid UTF8 codepoint in either S1 or S2 (byte at S1 > byte at S2)
+ +2: if S1 > S2, comparison ended at a different byte in an invalid UTF8 codepoint in either S1 or S2
+
+ Compare two UTF8 encoded strings, case sensitive.
+
+ Internally it uses WideCompareStr on the first Utf8 codepoint that differs between S1 and S2
+ and therefore has proper collation on platforms where the WidestringManager supports this
+ (Windows, *nix with cwstring unit)
+------------------------------------------------------------------------------}
+function UTF8CompareStr(const S1, S2: string): PtrInt;
+begin
+ Result := UTF8CompareStr(PChar(Pointer(S1)),length(S1),
+ PChar(Pointer(S2)),length(S2));
+end;
+
+function UTF8CompareStrP(S1, S2: PChar): PtrInt;
+begin
+ Result:=UTF8CompareStr(S1,StrLen(S1),S2,StrLen(S2));
+end;
+
+function UTF8CompareStr(S1: PChar; Count1: SizeInt; S2: PChar; Count2: SizeInt): PtrInt;
+var
+ Count: SizeInt;
+ i, CL1, CL2: Integer;
+ B1, B2: Byte;
+ W1, W2: WideString;
+ Org1, Org2: PChar;
+begin
+ Result := 0;
+ Org1 := S1;
+ Org2 := S2;
+ if (Count1 > Count2) then
+ Count := Count2
+ else
+ Count := Count1;
+
+ i := 0;
+ if (Count > 0) then
+ begin
+ //unfortunately we cannot use CompareByte here, so we have to iterate ourselves
+ while (i < Count) do
+ begin
+ B1 := byte(S1^);
+ B2 := byte(S2^);
+ if (B1 <> B2) then
+ begin
+ //writeln('UCS: B1=',IntToHex(B1,2),', B2=',IntToHex(B2,2));
+ Break;
+ end;
+ Inc(S1); Inc(S2); Inc(i);
+ end;
+ end;
+ if (i < Count) then
+ begin
+ //Fallback result
+ Result := B1 - B2;
+ if (Result < 0) then
+ Result := -2
+ else
+ Result := 2;
+ //writeln('UCS: FallBack Result = ',Result);
+ //Try t find start of valid UTF8 codepoints
+ if (not Utf8TryFindCodepointStart(Org1, S1, CL1)) or
+ not Utf8TryFindCodepointStart(Org2, S2, CL2) then
+ Exit;
+
+ //writeln('UCS: CL1=',CL1,', CL2=',CL2);
+ //writeln('S1 = "',S1,'"');
+ //writeln('S2 = "',S2,'"');
+ W1 := Utf8ToUtf16(S1, CL1);
+ W2 := Utf8ToUtf16(S2, CL2);
+ //writeln('UCS: W1 = ',Word(W1[1]),' W2 = ',Word(W2[1]));
+ Result := WideCompareStr(W1, W2);
+ end
+ else
+ //Strings are the same up and until size of smallest one
+ Result := Count1 - Count2;
+ if (Result > 1) then
+ Result := 1
+ else if (Result < -1) then
+ Result := -1;
+end;
+
+{------------------------------------------------------------------------------
+ Name: UTF8CompareText
+ Params: S1, S2 - UTF8 encoded strings
+ Returns: < 0 if S1 < S2, 0 if S1 = S2, > 0 if S1 > S2.
+ Compare two UTF8 encoded strings, case insensitive.
+ This function guarantees proper collation on all supported platforms.
+ Internally it uses WideCompareText.
+ ------------------------------------------------------------------------------}
+function UTF8CompareText(const S1, S2: String): PtrInt;
+begin
+ Result := WideCompareText(UTF8ToUTF16(S1),UTF8ToUTF16(S2));
+end;
+
+function UTF8CompareTextP(S1, S2: PChar): PtrInt;
+begin
+ Result := WideCompareText(UTF8ToUTF16(S1,StrLen(S1)), UTF8ToUTF16(S2,StrLen(S2)));
+end;
+
+function UTF8CompareLatinTextFast(const S1, S2: String): PtrInt;
+// Like UTF8CompareText but does not return strict alphabetical order.
+// The order is deterministic and good for binary search and such uses.
+// Optimizes comparison of single-byte encoding and also multi-byte portions
+// when they are equal. Otherwise falls back to WideCompareText.
+var
+ Count, Count1, Count2: sizeint;
+ Chr1, Chr2: Char;
+ P1, P2: PChar;
+ P1LastBytePointOffset: PChar;
+begin
+ Count1 := Length(S1);
+ Count2 := Length(S2);
+ if Count1 > Count2 then
+ Count := Count2
+ else
+ Count := Count1;
+ if Count > 0 then
+ begin
+ P1 := @S1[1];
+ P2 := @S2[1];
+ P1LastBytePointOffset := P1;
+ while Count > 0 do
+ begin
+ Chr1 := P1^;
+ Chr2 := P2^;
+
+ if Chr1 <> Chr2 then
+ begin
+ if (ord(Chr1) or ord(Chr2)) < 128 then
+ begin
+ P1LastBytePointOffset := P1;
+ if (Chr1 in ['A'..'Z']) then
+ inc(Chr1, $20);
+ if (Chr2 in ['A'..'Z']) then
+ inc(Chr2, $20);
+ if Chr1 <> Chr2 then
+ break;
+ end
+ else
+ begin
+ p2 := p2 + (P1LastBytePointOffset - P1);
+ p1 := P1LastBytePointOffset;
+ Exit(WideCompareText(
+ UTF8ToUTF16(p1, Length(s1) - (p1 - @S1[1])),
+ UTF8ToUTF16(p2, Length(s2) - (p2 - @S2[1]))
+ ));
+ end;
+ end
+ else
+ if (ord(Chr1) or ord(Chr2)) < 128 then
+ P1LastBytePointOffset := P1;
+
+ Inc(P1); Inc(P2);
+ Dec(Count);
+ end;
+ end;
+ if Count > 0 then
+ Result := Byte(Chr1)-Byte(Chr2)
+ else
+ Result := Count1-Count2;
+end;
+
+function UTF8CompareStrCollated(const S1, S2: string): PtrInt; {$IFnDEF ACP_RTL}inline;{$endif}
+begin
+ {$IFDEF ACP_RTL}
+ //Only with this define AnsiCompareStr does not point to Utf8CompareStr
+ Result := AnsiCompareStr(UTF8ToSys(S1), UTF8ToSys(S2));
+ {$ELSE}
+ Result := Utf8CompareStr(S1,S2);
+ {$ENDIF}
+end;
+
+function CompareStrListUTF8LowerCase(List: TStringList; Index1, Index2: Integer
+ ): Integer;
+begin
+ Result:=UTF8CompareText(List[Index1],List[Index2]);
+end;
+
+{------------------------------------------------------------------------------
+ Name: ConvertUTF8ToUTF16
+ Params: Dest - Pointer to destination string
+ DestWideCharCount - Wide char count allocated in destination string
+ Src - Pointer to source string
+ SrcCharCount - Char count allocated in source string
+ Options - Conversion options, if none is set, both
+ invalid and unfinished source chars are skipped
+
+ toInvalidCharError - Stop on invalid source char and report
+ error
+ toInvalidCharToSymbol - Replace invalid source chars with '?'
+ toUnfinishedCharError - Stop on unfinished source char and
+ report error
+ toUnfinishedCharToSymbol - Replace unfinished source char with '?'
+
+ ActualWideCharCount - Actual wide char count converted from source
+ string to destination string
+ Returns:
+ trNoError - The string was successfully converted without
+ any error
+ trNullSrc - Pointer to source string is nil
+ trNullDest - Pointer to destination string is nil
+ trDestExhausted - Destination buffer size is not big enough to hold
+ converted string
+ trInvalidChar - Invalid source char found
+ trUnfinishedChar - Unfinished source char found
+
+ Converts the specified UTF-8 encoded string to UTF-16 encoded (system endian)
+ ------------------------------------------------------------------------------}
+function ConvertUTF8ToUTF16(Dest: PWideChar; DestWideCharCount: SizeUInt;
+ Src: PChar; SrcCharCount: SizeUInt; Options: TConvertOptions;
+ out ActualWideCharCount: SizeUInt): TConvertResult;
+var
+ DestI, SrcI: SizeUInt;
+ B1, B2, B3, B4: Byte;
+ W: Word;
+ C: Cardinal;
+
+ function UnfinishedCharError: Boolean;
+ begin
+ if toUnfinishedCharToSymbol in Options then
+ begin
+ Dest[DestI] := System.WideChar('?');
+ Inc(DestI);
+ Result := False;
+ end
+ else
+ if toUnfinishedCharError in Options then
+ begin
+ ConvertUTF8ToUTF16 := trUnfinishedChar;
+ Result := True;
+ end
+ else Result := False;
+ end;
+
+ function InvalidCharError(Count: SizeUInt): Boolean; inline;
+ begin
+ if not (toInvalidCharError in Options) then
+ begin
+ if toInvalidCharToSymbol in Options then
+ begin
+ Dest[DestI] := System.WideChar('?');
+ Inc(DestI);
+ end;
+
+ Dec(SrcI, Count);
+
+ // skip trailing UTF-8 char bytes
+ while (Count > 0) do
+ begin
+ if (Byte(Src[SrcI]) and %11000000) <> %10000000 then Break;
+ Inc(SrcI);
+ Dec(Count);
+ end;
+
+ Result := False;
+ end
+ else
+ if toInvalidCharError in Options then
+ begin
+ ConvertUTF8ToUTF16 := trUnfinishedChar;
+ Result := True;
+ end;
+ end;
+
+begin
+ ActualWideCharCount := 0;
+
+ if not Assigned(Src) then
+ begin
+ Result := trNullSrc;
+ Exit;
+ end;
+
+ if not Assigned(Dest) then
+ begin
+ Result := trNullDest;
+ Exit;
+ end;
+ SrcI := 0;
+ DestI := 0;
+
+ while (DestI < DestWideCharCount) and (SrcI < SrcCharCount) do
+ begin
+ B1 := Byte(Src[SrcI]);
+ Inc(SrcI);
+
+ if B1 < 128 then // single byte UTF-8 char
+ begin
+ Dest[DestI] := System.WideChar(B1);
+ Inc(DestI);
+ end
+ else
+ begin
+ if SrcI >= SrcCharCount then
+ if UnfinishedCharError then Exit(trInvalidChar)
+ else Break;
+
+ B2 := Byte(Src[SrcI]);
+ Inc(SrcI);
+
+ if (B1 and %11100000) = %11000000 then // double byte UTF-8 char
+ begin
+ if (B2 and %11000000) = %10000000 then
+ begin
+ Dest[DestI] := System.WideChar(((B1 and %00011111) shl 6) or (B2 and %00111111));
+ Inc(DestI);
+ end
+ else // invalid character, assume single byte UTF-8 char
+ if InvalidCharError(1) then Exit(trInvalidChar);
+ end
+ else
+ begin
+ if SrcI >= SrcCharCount then
+ if UnfinishedCharError then Exit(trInvalidChar)
+ else Break;
+
+ B3 := Byte(Src[SrcI]);
+ Inc(SrcI);
+
+ if (B1 and %11110000) = %11100000 then // triple byte UTF-8 char
+ begin
+ if ((B2 and %11000000) = %10000000) and ((B3 and %11000000) = %10000000) then
+ begin
+ W := ((B1 and %00011111) shl 12) or ((B2 and %00111111) shl 6) or (B3 and %00111111);
+ if (W < $D800) or (W > $DFFF) then // to single wide char UTF-16 char
+ begin
+ Dest[DestI] := System.WideChar(W);
+ Inc(DestI);
+ end
+ else // invalid UTF-16 character, assume double byte UTF-8 char
+ if InvalidCharError(2) then Exit(trInvalidChar);
+ end
+ else // invalid character, assume double byte UTF-8 char
+ if InvalidCharError(2) then Exit(trInvalidChar);
+ end
+ else
+ begin
+ if SrcI >= SrcCharCount then
+ if UnfinishedCharError then Exit(trInvalidChar)
+ else Break;
+
+ B4 := Byte(Src[SrcI]);
+ Inc(SrcI);
+
+ if ((B1 and %11111000) = %11110000) and ((B2 and %11000000) = %10000000)
+ and ((B3 and %11000000) = %10000000) and ((B4 and %11000000) = %10000000) then
+ begin // 4 byte UTF-8 char
+ C := ((B1 and %00000111) shl 18) or ((B2 and %00111111) shl 12)
+ or ((B3 and %00111111) shl 6) or (B4 and %00111111);
+ if (C>$10FFFF) {out of range U+10FFFF} or
+ ((B1=%11110000) and (B2<=%10001111)) //4 bytes are mapped to the 1-3 byte codes
+ then
+ begin
+ if InvalidCharError(3) then Exit(trInvalidChar);
+ end else
+ begin
+ // to double wide char UTF-16 char
+ C:=C-$10000;
+ Dest[DestI] := System.WideChar($D800 or (C shr 10));
+ Inc(DestI);
+ if DestI >= DestWideCharCount then Break;
+ Dest[DestI] := System.WideChar($DC00 or (C and %0000001111111111));
+ Inc(DestI);
+ end;
+ end
+ else // invalid character, assume triple byte UTF-8 char
+ if InvalidCharError(3) then Exit(trInvalidChar);
+ end;
+ end;
+ end;
+ end;
+
+ if DestI >= DestWideCharCount then
+ begin
+ DestI := DestWideCharCount - 1;
+ Result := trDestExhausted;
+ end
+ else
+ Result := trNoError;
+
+ Dest[DestI] := #0;
+ ActualWideCharCount := DestI + 1;
+end;
+
+{------------------------------------------------------------------------------
+ Name: ConvertUTF16ToUTF8
+ Params: Dest - Pointer to destination string
+ DestCharCount - Char count allocated in destination string
+ Src - Pointer to source string
+ SrcWideCharCount - Wide char count allocated in source string
+ Options - Conversion options, if none is set, both
+ invalid and unfinished source chars are skipped.
+ See ConvertUTF8ToUTF16 for details.
+
+ ActualCharCount - Actual char count converted from source
+ string to destination string
+ Returns: See ConvertUTF8ToUTF16
+
+ Converts the specified UTF-16 encoded string (system endian) to UTF-8 encoded
+ ------------------------------------------------------------------------------}
+function ConvertUTF16ToUTF8(Dest: PChar; DestCharCount: SizeUInt;
+ Src: PWideChar; SrcWideCharCount: SizeUInt; Options: TConvertOptions;
+ out ActualCharCount: SizeUInt): TConvertResult;
+var
+ DestI, SrcI: SizeUInt;
+ W1, W2: Word;
+ C: Cardinal;
+
+ function UnfinishedCharError: Boolean;
+ begin
+ if toUnfinishedCharToSymbol in Options then
+ begin
+ Dest[DestI] := Char('?');
+ Inc(DestI);
+ Result := False;
+ end
+ else
+ if toUnfinishedCharError in Options then
+ begin
+ ConvertUTF16ToUTF8 := trUnfinishedChar;
+ Result := True;
+ end
+ else Result := False;
+ end;
+
+ function InvalidCharError(Count: SizeUInt): Boolean; inline;
+ begin
+ if not (toInvalidCharError in Options) then
+ begin
+ if toInvalidCharToSymbol in Options then
+ begin
+ Dest[DestI] := Char('?');
+ Inc(DestI);
+ end;
+
+ Dec(SrcI, Count);
+ // skip trailing UTF-16 wide char
+ if (Word(Src[SrcI]) and $FC00) = $DC00 then Inc(SrcI);
+
+ Result := False;
+ end
+ else
+ if toInvalidCharError in Options then
+ begin
+ ConvertUTF16ToUTF8 := trUnfinishedChar;
+ Result := True;
+ end;
+ end;
+
+begin
+ ActualCharCount := 0;
+
+ if not Assigned(Src) then
+ begin
+ Result := trNullSrc;
+ Exit;
+ end;
+
+ if not Assigned(Dest) then
+ begin
+ Result := trNullDest;
+ Exit;
+ end;
+ SrcI := 0;
+ DestI := 0;
+
+ while (DestI < DestCharCount) and (SrcI < SrcWideCharCount) do
+ begin
+ W1 := Word(Src[SrcI]);
+ Inc(SrcI);
+
+ if (W1 < $D800) or (W1 > $DFFF) then // single wide char UTF-16 char
+ begin
+ if W1 < $0080 then // to single byte UTF-8 char
+ begin
+ Dest[DestI] := Char(W1);
+ Inc(DestI);
+ end
+ else
+ if W1 < $0800 then // to double byte UTF-8 char
+ begin
+ Dest[DestI] := Char(%11000000 or ((W1 and %11111000000) shr 6));
+ Inc(DestI);
+ if DestI >= DestCharCount then Break;
+ Dest[DestI] := Char(%10000000 or (W1 and %111111));
+ Inc(DestI);
+ end
+ else
+ begin // to triple byte UTF-8 char
+ Dest[DestI] := Char(%11100000 or ((W1 and %1111000000000000) shr 12));
+ Inc(DestI);
+ if DestI >= DestCharCount then Break;
+ Dest[DestI] := Char(%10000000 or ((W1 and %111111000000) shr 6));
+ Inc(DestI);
+ if DestI >= DestCharCount then Break;
+ Dest[DestI] := Char(%10000000 or (W1 and %111111));
+ Inc(DestI);
+ end;
+ end
+ else
+ begin
+ if SrcI >= SrcWideCharCount then
+ if UnfinishedCharError then Exit(trInvalidChar)
+ else Break;
+
+ W2 := Word(Src[SrcI]);
+ Inc(SrcI);
+
+ if (W1 and $F800) = $D800 then // double wide char UTF-16 char
+ begin
+ if (W2 and $FC00) = $DC00 then
+ begin
+ C := (W1 - $D800) shl 10 + (W2 - $DC00) + $10000;
+
+ // to 4 byte UTF-8 char
+ Dest[DestI] := Char(%11110000 or (C shr 18));
+ Inc(DestI);
+ if DestI >= DestCharCount then Break;
+ Dest[DestI] := Char(%10000000 or ((C and $3F000) shr 12));
+ Inc(DestI);
+ if DestI >= DestCharCount then Break;
+ Dest[DestI] := Char(%10000000 or ((C and %111111000000) shr 6));
+ Inc(DestI);
+ if DestI >= DestCharCount then Break;
+ Dest[DestI] := Char(%10000000 or (C and %111111));
+ Inc(DestI);
+ end
+ else // invalid character, assume single wide char UTF-16 char
+ if InvalidCharError(1) then Exit(trInvalidChar);
+ end
+ else // invalid character, assume single wide char UTF-16 char
+ if InvalidCharError(1) then Exit(trInvalidChar);
+ end;
+ end;
+
+ if DestI >= DestCharCount then
+ begin
+ DestI := DestCharCount - 1;
+ Result := trDestExhausted;
+ end
+ else
+ Result := trNoError;
+
+ Dest[DestI] := #0;
+ ActualCharCount := DestI + 1;
+end;
+
+{------------------------------------------------------------------------------
+ Name: UTF8ToUTF16
+ Params: S - Source UTF-8 string
+ Returns: UTF-16 encoded string
+
+ Converts the specified UTF-8 encoded string to UTF-16 encoded (system endian)
+ Avoid copying the result string since on windows a widestring requires a full
+ copy
+ ------------------------------------------------------------------------------}
+function UTF8ToUTF16(const S: AnsiString): UnicodeString; inline;
+begin
+ Result:=UTF8ToUTF16(PChar(S),length(S));
+end;
+
+function UTF8ToUTF16(const P: PChar; ByteCnt: SizeUInt): UnicodeString;
+var
+ L: SizeUInt;
+begin
+ if ByteCnt=0 then
+ exit('');
+ SetLength(Result, ByteCnt);
+ // wide chars of UTF-16 <= bytes of UTF-8 string
+ if ConvertUTF8ToUTF16(PWideChar(Result), Length(Result) + 1, P, ByteCnt,
+ [toInvalidCharToSymbol], L) = trNoError
+ then SetLength(Result, L - 1)
+ else Result := '';
+end;
+
+{------------------------------------------------------------------------------
+ Name: UTF16ToUTF8
+ Params: S - Source UTF-16 string (system endian)
+ Returns: UTF-8 encoded string
+
+ Converts the specified UTF-16 encoded string (system endian) to UTF-8 encoded
+ ------------------------------------------------------------------------------}
+function UTF16ToUTF8(const S: UnicodeString): AnsiString; inline;
+begin
+ Result := UTF16ToUTF8(PWideChar(S),length(S));
+end;
+
+function UTF16ToUTF8(const P: PWideChar; WideCnt: SizeUInt): AnsiString;
+var
+ L: SizeUInt;
+begin
+ if WideCnt=0 then
+ exit('');
+
+ SetLength(Result, WideCnt * 3);
+ // bytes of UTF-8 <= 3 * wide chars of UTF-16 string
+ // e.g. %11100000 10100000 10000000 (UTF-8) is $0800 (UTF-16)
+ if ConvertUTF16ToUTF8(PChar(Result), Length(Result) + 1, P, WideCnt,
+ [toInvalidCharToSymbol], L) = trNoError then
+ begin
+ SetLength(Result, L - 1);
+ end else
+ Result := '';
+end;
+
+procedure LazGetLanguageIDs(var Lang, FallbackLang: String);
+
+ {$IFDEF DARWIN}
+ function GetLanguage: boolean;
+ var
+ Ref: CFStringRef;
+ LangArray: CFMutableArrayRef;
+ StrSize: CFIndex;
+ StrRange: CFRange;
+ Locals: CFArrayRef;
+ Bundle: CFBundleRef;
+ begin
+ Result := false;
+ Bundle:=CFBundleGetMainBundle;
+ if Bundle=nil then exit;
+ Locals:=CFBundleCopyBundleLocalizations(Bundle);
+ if Locals=nil then exit;
+ LangArray := CFBundleCopyLocalizationsForPreferences(Locals, nil);
+ try
+ if CFArrayGetCount(LangArray) > 0 then
+ begin
+ Ref := CFArrayGetValueAtIndex(LangArray, 0);
+ StrRange.location := 0;
+ StrRange.length := CFStringGetLength(Ref);
+
+ StrSize:=0;
+ CFStringGetBytes(Ref, StrRange, kCFStringEncodingUTF8,
+ Ord('?'), False, nil, 0, StrSize);
+ SetLength(Lang, StrSize);
+
+ if StrSize > 0 then
+ begin
+ CFStringGetBytes(Ref, StrRange, kCFStringEncodingUTF8,
+ Ord('?'), False, @Lang[1], StrSize, StrSize);
+ Result:=true;
+ FallbackLang := Copy(Lang, 1, 2);
+ end;
+ end;
+ finally
+ CFRelease(LangArray);
+ CFRelease(Locals);
+ end;
+ end;
+ {$ENDIF}
+begin
+{$IFDEF DARWIN}
+ if not GetLanguage then
+ GetLanguageIDs(Lang, FallbackLang);
+{$ELSE}
+ GetLanguageIDs(Lang, FallbackLang);
+{$ENDIF}
+end;
+
+{
+This routine will strip country information from the language ID
+making it more simple
+
+Ideally the resulting ID from here should conform to ISO 639-1
+or ISO 639-2, if the language has no code in ISO 639-1
+}
+procedure LazGetShortLanguageID(var Lang: String);
+var
+ FallbackLang: String;
+begin
+ FallbackLang:='';
+ LazGetLanguageIDs(Lang, FallbackLang);
+
+ // Simply making sure its length is at most 2 should be enough for most languages
+ if Length(Lang) > 2 then Lang := Lang[1] + Lang[2];
+end;
+
+procedure ReplaceSubstring(var s: string; StartPos, Count: SizeInt;
+ const Insertion: string);
+// This was moved to LazStringUtils and a deprecated copy was left here. Will be removed.
+var
+ MaxCount: SizeInt;
+ InsertionLen: SizeInt;
+ SLen: SizeInt;
+ RestLen: SizeInt;
+ p: PByte;
+begin
+ SLen:=length(s);
+ if StartPos>SLen then begin
+ s:=s+Insertion;
+ exit;
+ end;
+ if StartPos<1 then StartPos:=1;
+ if Count<0 then Count:=0;
+ MaxCount:=SLen-StartPos+1;
+ if Count>MaxCount then
+ Count:=MaxCount;
+ InsertionLen:=length(Insertion);
+ if (Count=0) and (InsertionLen=0) then
+ exit; // nothing to do
+ if (Count=InsertionLen) then begin
+ if CompareMem(PByte(s)+StartPos-1,Pointer(Insertion),Count) then
+ // already the same content
+ exit;
+ UniqueString(s);
+ end else begin
+ RestLen:=SLen-StartPos-Count+1;
+ if InsertionLen0 then begin
+ UniqueString(s);
+ p:=PByte(s)+StartPos-1;
+ System.Move((p+Count)^,(p+InsertionLen)^,RestLen);
+ end;
+ Setlength(s,SLen-Count+InsertionLen);
+ end else begin
+ // longen
+ Setlength(s,SLen-Count+InsertionLen);
+ if RestLen>0 then begin
+ p:=PByte(s)+StartPos-1;
+ System.Move((p+Count)^,(p+InsertionLen)^,RestLen);
+ end;
+ end;
+ end;
+ if InsertionLen>0 then
+ System.Move(PByte(Insertion)^,(PByte(s)+StartPos-1)^,InsertionLen);
+end;
+
+procedure InitFPUpchars;
+var
+ c: Char;
+begin
+ for c:=Low(char) to High(char) do begin
+ FPUpChars[c]:=upcase(c);
+ end;
+end;
+
+{ TStringListUTF8Fast }
+
+function TStringListUTF8Fast.DoCompareText(const s1, s2: string): PtrInt;
+begin
+ Result:=UTF8CompareLatinTextFast(s1, s2);
+end;
+
+
+initialization
+ InitFPUpchars;
+ InitLazUtf8;
+finalization
+ FinalizeLazUTF8;
+
+end.
+
diff --git a/src/lazutf8/lazutils_defines.inc b/src/lazutf8/lazutils_defines.inc
new file mode 100644
index 000000000..ddf91420e
--- /dev/null
+++ b/src/lazutf8/lazutils_defines.inc
@@ -0,0 +1,13 @@
+// Add defines here. This file should be included in all LazUtils units headers
+
+
+{$undef UTF8_RTL} // FPC >= 2.7.1 with codepages and default string = CP_UTF8
+{$undef ACP_RTL} // FPC >= 2.7.1 with codepages and default string = CP_ACP
+
+
+{$ifndef DisableUTF8RTL}
+ {$define UTF8_RTL}
+{$else DisableUTF8RTL}
+ {$define ACP_RTL}
+{$endif DisableUTF8RTL}
+
diff --git a/src/lazutf8/unixlazutf8.inc b/src/lazutf8/unixlazutf8.inc
new file mode 100644
index 000000000..6c6a88568
--- /dev/null
+++ b/src/lazutf8/unixlazutf8.inc
@@ -0,0 +1,50 @@
+{%MainUnit lazutf8.pas}
+
+function ConsoleToUTF8(const s: string): string;// converts UTF8 string to console encoding (used by Write, WriteLn)
+begin
+ Result := SysToUTF8(S);
+end;
+
+function UTF8ToConsole(const s: string): string;
+begin
+ Result := UTF8ToSys(s);
+end;
+
+function WinCPToUTF8(const s: string): string;
+begin
+ if NeedRTLAnsi and (not IsASCII(s)) then
+ begin
+ Result:=AnsiToUTF8(s);
+ {$ifdef FPC_HAS_CPSTRING}
+ // prevent UTF8 codepage appear in the strings - we don't need codepage
+ // conversion magic in LCL code
+ SetCodePage(RawByteString(Result), StringCodePage(s), False);
+ {$endif}
+ end
+ else
+ Result:=s;
+end;
+
+function UTF8ToWinCP(const s: string): string;
+begin
+ if NeedRTLAnsi and (not IsASCII(s)) then
+ Result:=UTF8ToAnsi(s)
+ else
+ Result:=s;
+end;
+
+function ParamStrUTF8(Param: Integer): string;
+begin
+ Result:=SysToUTF8(ObjPas.ParamStr(Param));
+end;
+
+procedure InitLazUtf8;
+begin
+ //dummy procedure
+end;
+
+procedure FinalizeLazUTF8;
+begin
+ //dummy procedure
+end;
+
diff --git a/src/lazutf8/winlazutf8.inc b/src/lazutf8/winlazutf8.inc
new file mode 100644
index 000000000..83201b2b7
--- /dev/null
+++ b/src/lazutf8/winlazutf8.inc
@@ -0,0 +1,639 @@
+{%MainUnit lazutf8.pas}
+
+{$IF DEFINED(UTF8_RTL) AND NOT DEFINED(WINCE)}
+ {$DEFINE ArgsWAsUTF8}
+{$ENDIF}
+
+var
+ //Function prototypes
+ _ParamStrUtf8: Function(Param: Integer): string;
+
+var
+ ArgsW: Array of WideString;
+ ArgsWCount: Integer; // length(ArgsW)+1
+ {$IFDEF ArgsWAsUTF8}
+ ArgsUTF8: Array of String; // the ArgsW array as UTF8
+ OldArgV: PPChar = nil;
+ {$IFEND}
+
+//************ START "Stubs" that just call Ansi or Wide implementation
+
+function ParamStrUTF8(Param: Integer): string;
+begin
+ Result := _ParamStrUtf8(Param);
+end;
+
+//************ END "Stubs" that just call Ansi or Wide implementation
+
+
+//*************** START Non WideString implementations
+{$ifndef wince}
+function ParamStrUtf8Ansi(Param: Integer): String;
+begin
+ Result:=SysToUTF8(ObjPas.ParamStr(Param));
+end;
+{$endif wince}
+
+//*************** END Non WideString impementations
+
+
+
+
+//*************** START WideString impementations
+
+
+{$IFDEF ArgsWAsUTF8}
+procedure SetupArgvAsUtf8;
+var
+ i: Integer;
+begin
+ SetLength(ArgsUTF8,length(ArgsW));
+ OldArgV:=argv;
+ GetMem(argv,SizeOf(Pointer)*length(ArgsW));
+ for i:=0 to length(ArgsW)-1 do
+ begin
+ ArgsUTF8[i]:=ArgsW{%H-}[i];
+ argv[i]:=PChar(ArgsUTF8[i]);
+ end;
+end;
+{$endif}
+
+procedure SetupCommandlineParametersWide;
+var
+ ArgLen, Start, CmdLen, i, j: SizeInt;
+ Quote : Boolean;
+ Buf: array[0..259] of WChar; // need MAX_PATH bytes, not 256!
+ PCmdLineW: PWideChar;
+ CmdLineW: WideString;
+
+ procedure AllocArg(Idx, Len:longint);
+ begin
+ if (Idx >= ArgsWCount) then
+ SetLength(ArgsW, Idx + 1);
+ SetLength(ArgsW[Idx], Len);
+ end;
+
+begin
+ { create commandline, it starts with the executed filename which is argv[0] }
+ { Win32 passes the command NOT via the args, but via getmodulefilename}
+ ArgsWCount := 0;
+ ArgLen := GetModuleFileNameW(0, @buf[0], sizeof(buf));
+
+ //writeln('ArgLen = ',Arglen);
+
+ buf[ArgLen] := #0; // be safe, no terminating 0 on XP
+ allocarg(0,arglen);
+ move(buf[0],ArgsW[0][1],arglen * SizeOf(WChar));
+
+ //writeln('ArgsW[0] = ',ArgsW[0]);
+
+ PCmdLineW := nil;
+ { Setup cmdline variable }
+ PCmdLineW := GetCommandLineW;
+ CmdLen := StrLen(PCmdLineW);
+
+ //writeln('StrLen(PCmdLineW) = ',CmdLen);
+
+ SetLength(CmdLineW, CmdLen);
+ Move(PCmdLineW^, CmdLineW[1], CmdLen * SizeOf(WChar));
+
+
+ //debugln(CmdLineW);
+ //for i := 1 to CmdLen do DbgOut(DbgS(i mod 10)); debugln;
+
+ i := 1;
+ while (i <= CmdLen) do
+ begin
+ //debugln('Next');
+ //DbgOut('i=',DbgS(i),' CmdLineW[',DbgS(i),']=');if i<=CmdLen then debugln(CmdLineW[i]) else debugln('#0');
+ //skip leading spaces
+ while (i <= CmdLen) and (CmdLineW[i] <= #32) do Inc(i);
+ //DbgOut('After skipping spaces: i=',DbgS(i),' CmdLineW[',DbgS(i),']=');if i<=CmdLen then debugln(CmdLineW[i]) else debugln('#0');
+ if (i > CmdLen) then Break;
+ Quote := False;
+ Start := i;
+ ArgLen := 0;
+ while (i <= CmdLen) do
+ begin //find next commandline parameter
+ case CmdLineW[i] of
+ #1..#32:
+ begin
+ if Quote then
+ begin
+ //debugln('i=',DbgS(i),': Space in Quote');
+ Inc(ArgLen)
+ end
+ else
+ begin
+ //debugln('i=',DbgS(i),': Space in NOT Quote');
+ Break;
+ end;
+ end;
+ '"':
+ begin
+ if (i < CmdLen) and (CmdLineW[i+1] <> '"') then
+ begin
+ //debugln('i=',DbgS(i),': Quote := not Quote');
+ Quote := not Quote
+ end
+ else
+ begin
+ //debugln('i=',DbgS(i),': Skip Quote');
+ Inc(i);
+ end;
+ end;
+ else Inc(ArgLen);
+ end;//case
+ Inc(i);
+ end; //find next commandline parameter
+
+ //debugln('ArgWCount=',DbgS(ArgsWCount),' Start=',DbgS(start),' ArgLen=',DbgS(arglen),' i=',DbgS(i));
+
+ //we already have (a better) ArgW[0]
+ if (ArgsWCount > 0) then
+ begin //Process commandline parameter
+ AllocArg(ArgsWCount, ArgLen);
+ Quote := False;
+ i := Start;
+ j := 1;
+ while (i <= CmdLen) do
+ begin
+ case CmdLineW[i] of
+ #1..#32:
+ begin
+ if Quote then
+ begin
+ //if j > ArgLen then debugln('Error whitespace: j > ArgLen: j=',DbgS(j),' ArgLen=',DbgS(arglen));
+ ArgsW[ArgsWCount][j] := CmdLineW[i];
+ Inc(j);
+ end
+ else
+ Break;
+ end;
+ '"':
+ begin
+ if (i < CmdLen) and (CmdLineW[i+1] <> '"') then
+ Quote := not Quote
+ else
+ Inc(i);
+ end;
+ else
+ begin
+ //if j > ArgLen then debugln('Error Quote: j > ArgLen: j=',DbgS(j),' ArgLen=',DbgS(arglen));
+ ArgsW[ArgsWCount][j] := CmdLineW[i];
+ Inc(j);
+ end;
+ end;
+ Inc(i);
+ end;
+
+ //debugln('ArgsW[',DbgS(ArgsWCount),'] = ',ArgsW[ArgsWCount]);
+ end; // Process commandline parameter
+ Inc(ArgsWCount);
+
+ end;
+ Dec(ArgsWCount);
+ //Note:
+ //On WinCe Argsv is a static function, so we cannot change it.
+ //This might change in the future if Argsv on WinCE will be declared as a function variable
+ {$IFDEF ArgsWAsUTF8}
+ if DefaultSystemCodePage=CP_UTF8 then
+ SetupArgvAsUtf8;
+ {$IFEND}
+end;
+
+function ParamStrUtf8Wide(Param: Integer): String;
+begin
+ if ArgsWCount <> ParamCount then
+ begin
+ //DebugLn('Error: ParamCount <> ArgsWCount!');
+ Result := SysToUtf8(ObjPas.ParamStr(Param));
+ end
+ else
+ begin
+ if (Param <= ArgsWCount) then
+ {$IFDEF ACP_RTL}
+ Result := String(UnicodeString(ArgsW[Param]))
+ {$ELSE}
+ Result := Utf8Encode(ArgsW[Param])
+ {$ENDIF ACP_RTL}
+ else
+ Result := '';
+ end;
+end;
+
+{$IFNDEF WINCE}
+function GetGetEnvironmentVariableCountWide: integer;
+var
+ hp,p : PWideChar;
+begin
+ Result:=0;
+ p:=GetEnvironmentStringsW;
+ if p=nil then exit;
+ hp:=p;
+ while hp^<>#0 do
+ begin
+ Inc(Result);
+ hp:=hp+strlen(hp)+1;
+ end;
+ FreeEnvironmentStringsW(p);
+end;
+
+
+function GetEnvironmentStringWide(Index: Integer): UnicodeString;
+var
+ hp,p : PWideChar;
+begin
+ Result:='';
+ p:=GetEnvironmentStringsW;
+ if p=nil then exit;
+ hp:=p;
+ while (hp^<>#0) and (Index>1) do
+ begin
+ Dec(Index);
+ hp:=hp+strlen(hp)+1;
+ end;
+ if (hp^<>#0) then
+ Result:=hp;
+ FreeEnvironmentStringsW(p);
+end;
+{$ENDIF WINCE}
+
+function GetEnvironmentVariableWide(const EnvVar: string): UnicodeString;
+{$IF FPC_FULLVERSION>=30000}
+begin
+ Result:=GetEnvironmentVariable(UTF8ToUTF16(EnvVar));
+end;
+{$ELSE}
+var
+ s, upperenv : Unicodestring;
+ i : longint;
+ hp,p : pwidechar;
+begin
+ Result:='';
+ p:=GetEnvironmentStringsW;
+ hp:=p;
+ upperenv:=uppercase(envvar);
+ while hp^<>#0 do
+ begin
+ s:=hp;
+ i:=pos('=',s);
+ if uppercase(copy(s,1,i-1))=upperenv then
+ begin
+ Result:=copy(s,i+1,length(s)-i);
+ break;
+ end;
+ { next string entry}
+ hp:=hp+strlen(hp)+1;
+ end;
+ FreeEnvironmentStringsW(p);
+end;
+{$ENDIF}
+
+
+//*************** END WideString impementations
+
+{$ifdef WinCE}
+function ConsoleToUTF8(const s: string): string;// converts console encoding to UTF8
+begin
+ Result := SysToUTF8(s);
+end;
+{$else}
+function ConsoleToUTF8(const s: string): string;// converts console encoding to UTF8
+var
+ Dst: PChar;
+begin
+ Dst := AllocMem((Length(s) + 1) * SizeOf(Char));
+ if OemToChar(PChar(s), Dst) then
+ Result := StrPas(Dst)
+ else
+ Result := s;
+ FreeMem(Dst);
+ Result := WinCPToUTF8(Result);
+end;
+{$endif not wince}
+
+{$ifdef WinCe}
+function UTF8ToConsole(const s: string): string; // converts UTF8 to console string (used by Write, WriteLn)
+begin
+ Result := UTF8ToSys(s);
+end;
+{$else}
+function UTF8ToConsole(const s: string): string; // converts UTF8 to console string (used by Write, WriteLn)
+var
+ Dst: PChar;
+begin
+ Result := UTF8ToWinCP(s);
+ Dst := AllocMem((Length(Result) + 1) * SizeOf(Char));
+ if CharToOEM(PChar(Result), Dst) then
+ Result := StrPas(Dst);
+ FreeMem(Dst);
+ SetCodePage(RawByteString(Result), CP_OEMCP, False);
+end;
+{$endif not WinCE}
+
+{$ifdef WinCE}
+function WinCPToUTF8(const s: string): string; inline;
+begin
+ Result := SysToUtf8(s);
+end;
+{$else}
+// for all Windows supporting 8bit codepages (e.g. not WinCE)
+function WinCPToUTF8(const s: string): string;
+// result has codepage CP_ACP
+var
+ UTF16WordCnt: SizeInt;
+ UTF16Str: UnicodeString;
+begin
+ Result:=s;
+ if IsASCII(Result) then begin
+ {$ifdef FPC_HAS_CPSTRING}
+ // prevent codepage conversion magic
+ SetCodePage(RawByteString(Result), CP_ACP, False);
+ {$endif}
+ exit;
+ end;
+ UTF16WordCnt:=MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Pointer(s), length(s), nil, 0);
+ // this will null-terminate
+ if UTF16WordCnt>0 then
+ begin
+ setlength(UTF16Str, UTF16WordCnt);
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Pointer(s), length(s), @UTF16Str[1], UTF16WordCnt);
+ Result:=UTF8Encode(UTF16Str);
+ {$ifdef FPC_HAS_CPSTRING}
+ // prevent codepage conversion magic
+ SetCodePage(RawByteString(Result), CP_ACP, False);
+ {$endif}
+ end;
+end;
+{$endif not wince}
+
+{$ifdef WinCe}
+function UTF8ToWinCP(const s: string): string; inline;
+begin
+ Result := Utf8ToSys(s);
+end;
+{$else}
+function UTF8ToWinCP(const s: string): string;
+// result has codepage CP_ACP
+var
+ src: UnicodeString;
+ len: LongInt;
+begin
+ Result:=s;
+ if IsASCII(Result) then begin
+ {$ifdef FPC_HAS_CPSTRING}
+ // prevent codepage conversion magic
+ SetCodePage(RawByteString(Result), CP_ACP, False);
+ {$endif}
+ exit;
+ end;
+ src:=UTF8Decode(s);
+ if src='' then
+ exit;
+ len:=WideCharToMultiByte(CP_ACP,0,PUnicodeChar(src),length(src),nil,0,nil,nil);
+ SetLength(Result,len);
+ if len>0 then begin
+ WideCharToMultiByte(CP_ACP,0,PUnicodeChar(src),length(src),@Result[1],length(Result),nil,nil);
+ {$ifdef FPC_HAS_CPSTRING}
+ // prevent codepage conversion magic
+ SetCodePage(RawByteString(Result), CP_ACP, False);
+ {$endif}
+ end;
+end;
+{$endif not wince}
+
+{$ifdef debugparamstrutf8}
+procedure ParamStrUtf8Error;
+var
+ i: Integer;
+begin
+ writeln('Error in Windows WideString implementation of ParamStrUtf8');
+ writeln('Using SysToUtf8(ParamsStr(Param)) as fallback');
+ writeln('ParamCount = ',ParamCount,', ArgsWCount = ',ArgsWCount);
+ for i := 0 to ParamCount do writeln('ParamStr(',i,') = "',ParamStr(i),'"');
+ writeln;
+ for i := 0 to ArgsWCount do writeln('ParamStrUtf8(',i,') = "',ArgsW[i],'"');
+end;
+{$endif}
+
+function GetLocaleStr(aLocaleID, aLCType: Longint; const Def: string): String;
+var
+ L: Integer;
+ Buf: array[0..255] of WideChar;
+begin
+ L := GetLocaleInfoW(aLocaleID, aLCType, Buf, SizeOf(Buf));
+ if L > 0 then
+ begin
+ Result:='';
+ widestringmanager.Wide2AnsiMoveProc(PWideChar(@Buf[0]),Result,CP_UTF8,L-1);
+ end
+ else
+ Result := Def;
+end;
+
+function GetLocaleCharUTF8(aLocaleID, aLCType: Longint; Def: Char): Char;
+var
+ Buf: array[0..3] of WideChar; // sdate allows 4 chars (3+ending #0)
+ GLI, I: LongInt;
+ WRes: WideChar;
+begin
+ //Use Widestring Api so it works on WinCE as well
+ GLI := GetLocaleInfoW(aLocaleID, aLCType, Buf, Length(Buf)); // GLI is char count with the ending #0 char
+ if GLI > 2 then
+ begin // more than 1 char -> try to find first non-space character
+ for I := 0 to GLI-2 do
+ begin
+ WRes := Buf[I];
+ case Buf[I] of
+ #32, #$00A0, #$2002, #$2003, #$2009, #$202F: begin end;// go over spaces
+ else
+ Break; // stop at non-space
+ end;
+ end;
+ end else
+ if GLI = 2 then // 1 char
+ WRes := Buf[0]
+ else
+ WRes := Def;
+
+ case WRes of
+ #0..#127: Result := WRes;// ASCII - OK
+ #$00A0: Result := ' '; // non breakable space
+ #$00B7: Result := '.'; // middle stop
+ #$02D9: Result := ''''; // dot above, italian handwriting
+ #$066B: Result := ','; // arabic decimal separator, persian thousand separator
+ #$066C: Result := ''''; // arabic thousand separator
+ #$2002: Result := ' '; // long space
+ #$2003: Result := ' '; // long space
+ #$2009: Result := ' '; // thin space
+ #$202F: Result := ' '; // narrow non breakable space
+ #$2014: Result := '-'; // persian decimal mark
+ #$2396: Result := ''''; // codepoint 9110 decimal separator
+ { Utf8 Utf16
+ C2 A0 -> 00A0
+ C2 B7 -> 00B7
+ CB 99 -> 02D9
+ D9 AB -> 066B
+ D9 AC -> 066C
+ E2 80 82 -> 2002
+ E2 80 83 -> 2003
+ E2 80 89 -> 2009
+ E2 80 AF -> 202F
+ E2 80 94 -> 2014
+ E2 8E 96 -> 2396
+ }
+ else // unicode character -> we need default ASCII char
+ Result := Def;
+ end; //case
+end;
+
+procedure GetLocaleFormatSettingsUTF8(LCID: Integer; var aFormatSettings: TFormatSettings);
+var
+ HF : Shortstring;
+ LID : Windows.LCID;
+ I,Day : longint;
+begin
+ LID := LCID;
+ with aFormatSettings do
+ begin
+ { Date stuff }
+ for I := 1 to 12 do
+ begin
+ ShortMonthNames[I]:=GetLocaleStr(LID,LOCALE_SABBREVMONTHNAME1+I-1,ShortMonthNames[i]);
+ LongMonthNames[I]:=GetLocaleStr(LID,LOCALE_SMONTHNAME1+I-1,LongMonthNames[i]);
+ end;
+ for I := 1 to 7 do
+ begin
+ Day := (I + 5) mod 7;
+ ShortDayNames[I]:=GetLocaleStr(LID,LOCALE_SABBREVDAYNAME1+Day,ShortDayNames[i]);
+ LongDayNames[I]:=GetLocaleStr(LID,LOCALE_SDAYNAME1+Day,LongDayNames[i]);
+ end;
+ DateSeparator := GetLocaleCharUTF8(LID, LOCALE_SDATE, '/');
+ ShortDateFormat := GetLocaleStr(LID, LOCALE_SSHORTDATE, 'm/d/yy');
+ LongDateFormat := GetLocaleStr(LID, LOCALE_SLONGDATE, 'mmmm d, yyyy');
+ { Time stuff }
+ TimeSeparator := GetLocaleCharUTF8(LID, LOCALE_STIME, ':');
+ TimeAMString := GetLocaleStr(LID, LOCALE_S1159, 'AM');
+ TimePMString := GetLocaleStr(LID, LOCALE_S2359, 'PM');
+ if StrToIntDef(GetLocaleStr(LID, LOCALE_ITLZERO, '0'), 0) = 0 then
+ HF:='h'
+ else
+ HF:='hh';
+ // No support for 12 hour stuff at the moment...
+ ShortTimeFormat := HF+':nn';
+ LongTimeFormat := HF + ':nn:ss';
+ { Currency stuff }
+ CurrencyString:=GetLocaleStr(LID, LOCALE_SCURRENCY, '');
+ CurrencyFormat:=StrToIntDef(GetLocaleStr(LID, LOCALE_ICURRENCY, '0'), 0);
+ NegCurrFormat:=StrToIntDef(GetLocaleStr(LID, LOCALE_INEGCURR, '0'), 0);
+ { Number stuff }
+ ThousandSeparator:=GetLocaleCharUTF8(LID, LOCALE_STHOUSAND, ',');
+ DecimalSeparator:=GetLocaleCharUTF8(LID, LOCALE_SDECIMAL, '.');
+ CurrencyDecimals:=StrToIntDef(GetLocaleStr(LID, LOCALE_ICURRDIGITS, '0'), 0);
+ ListSeparator := GetLocaleCharUTF8(LID, LOCALE_SLIST, ',');
+ end;
+end;
+
+procedure GetFormatSettingsUTF8;
+begin
+ {$ifndef wince}
+ GetLocaleFormatSettingsUTF8(GetThreadLocale, FormatSettings);
+ {$else}
+ GetLocaleFormatSettingsUTF8(GetUserDefaultLCID, FormatSettings);
+ {$endif}
+end;
+
+{$IFDEF UTF8_RTL}
+function UTF8StrCompAnsiString(S1, S2: PChar): PtrInt;
+begin
+ Result:=UTF8CompareStrP(S1,S2);
+end;
+
+function UTF8StrICompAnsiString(S1, S2: PChar): PtrInt;
+var
+ U1, U2: String;
+begin
+ U1:=StrPas(S1);
+ U2:=StrPas(S2);
+ Result:=UTF8CompareText(U1,U2);
+end;
+
+function UTF8StrLCompAnsiString(S1, S2: PChar; Count: PtrUInt): PtrInt;
+begin
+ Result:=UTF8CompareStr(S1,Count,S2,Count);
+end;
+
+function UTF8StrLICompAnsiString(S1, S2: PChar; Count: PtrUInt): PtrInt;
+var
+ U1, U2: String;
+begin
+ if Count>0 then begin
+ SetLength(U1,Count);
+ Move(S1^,PByte(U1)^,Count);
+ SetLength(U2,Count);
+ Move(S2^,PByte(U2)^,Count);
+ Result:=UTF8CompareText(U1,U2);
+ end else
+ Result:=0;
+end;
+{$ENDIF}
+
+procedure InitLazUtf8;
+begin
+ {$ifndef WinCE}
+ if Win32MajorVersion <= 4 then
+ begin
+ _ParamStrUtf8 := @ParamStrUtf8Ansi;
+ end
+ else
+ {$endif}
+ begin
+ try
+ ArgsWCount := -1;
+ _ParamStrUtf8 := @ParamStrUtf8Wide;
+ SetupCommandlineParametersWide;
+ {$ifdef debugparamstrutf8}
+ if ParamCount <> ArgsWCount then ParamStrUtf8Error;
+ {$endif}
+ Except
+ begin
+ ArgsWCount := -1;
+ {$ifdef debugparamstrutf8}
+ ParamStrUtf8Error;
+ {$endif}
+ end;
+ end;
+ end;
+ {$IFDEF UTF8_RTL}
+ GetFormatSettingsUTF8;
+ widestringmanager.UpperAnsiStringProc:=@UTF8UpperString;
+ widestringmanager.LowerAnsiStringProc:=@UTF8LowerString;
+ widestringmanager.CompareStrAnsiStringProc:=@UTF8CompareStr;
+ widestringmanager.CompareTextAnsiStringProc:=@UTF8CompareText;
+ widestringmanager.StrCompAnsiStringProc:=@UTF8StrCompAnsiString;
+ widestringmanager.StrICompAnsiStringProc:=@UTF8StrICompAnsiString;
+ widestringmanager.StrLCompAnsiStringProc:=@UTF8StrLCompAnsiString;
+ widestringmanager.StrLICompAnsiStringProc:=@UTF8StrLICompAnsiString;
+ // Does anyone need these two?
+ //widestringmanager.StrLowerAnsiStringProc;
+ //widestringmanager.StrUpperAnsiStringProc;
+ {$IFEND}
+end;
+
+procedure FinalizeLazUTF8;
+{$IFDEF ArgsWAsUTF8}
+var
+ p: PPChar;
+{$ENDIF}
+begin
+ {$IFDEF ArgsWAsUTF8}
+ // restore argv and free memory
+ if OldArgV<>nil then
+ begin
+ p:=argv;
+ argv:=OldArgV;
+ Freemem(p);
+ end;
+ {$IFEND}
+end;