Skip to content

Create pvlib.iotools.read_panond for reading .pan and .ond files #1749

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 63 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
bbe626a
pan ond reader
May 24, 2023
9489d2b
pan ond reader
May 24, 2023
907df2c
More info in nested levels
May 25, 2023
38c4b59
Added Comments
May 25, 2023
8dffc01
Added whatsnew documentation
May 25, 2023
01a9e18
Corrected formatting
May 25, 2023
258e3f5
Adressing stickler comments
May 25, 2023
9918451
Updated testing script
May 25, 2023
c7aa820
Merge branch 'pvlib:main' into Inverter_model
ckrening May 25, 2023
dc45fad
Merge pull request #1 from ckrening/panond
ckrening May 25, 2023
eec4001
addressing stickler comments
May 25, 2023
ea36317
Merge pull request #2 from ckrening/panond_2
ckrening May 25, 2023
4e434b2
stickler comments v3
May 25, 2023
91f5662
Merge pull request #3 from ckrening/panond_2
ckrening May 25, 2023
7475e3c
stickler comment correction
May 25, 2023
dfe34b3
Merge pull request #4 from ckrening/panond_2
ckrening May 25, 2023
238db5c
Updated notes and comments
Jun 6, 2023
d75b031
Merge pull request #5 from ckrening/panond_2
ckrening Jun 6, 2023
360a7e7
Minor comment updates and sphinx doc update
Jun 6, 2023
b768985
Merge pull request #6 from ckrening/panond_2
ckrening Jun 6, 2023
a8d56a4
edited comments for grammar/spelling
Jun 22, 2023
519d4a7
Merge pull request #7 from ckrening/panond_2
ckrening Jun 22, 2023
eb7f3ee
improved testing panond reader
Jun 29, 2023
da5e7b9
Merge pull request #8 from ckrening/panond_2
ckrening Jun 29, 2023
fa57d53
pan ond reader
May 24, 2023
a8056e3
pan ond reader
May 24, 2023
b65eb63
More info in nested levels
May 25, 2023
cde8ce0
Added Comments
May 25, 2023
6b271cc
Added whatsnew documentation
May 25, 2023
abb0c84
Corrected formatting
May 25, 2023
50f8b4a
Adressing stickler comments
May 25, 2023
2f84976
Updated testing script
May 25, 2023
68a2899
addressing stickler comments
May 25, 2023
6a0eee3
stickler comments v3
May 25, 2023
87ae181
stickler comment correction
May 25, 2023
efc2bea
Updated notes and comments
Jun 6, 2023
dd70a04
Minor comment updates and sphinx doc update
Jun 6, 2023
dcd26a0
edited comments for grammar/spelling
Jun 22, 2023
c76c2c5
improved testing panond reader
Jun 29, 2023
10e8275
Merge branch 'main' into main
ckrening Jul 12, 2023
4f2e9d0
Update pvlib/iotools/panond.py
ckrening Aug 14, 2023
108de80
Update pvlib/iotools/panond.py
ckrening Aug 14, 2023
b90b9a6
Adressing some github comments
Aug 14, 2023
bd5b7fb
Addressing github comments pt.2
Aug 14, 2023
c3b8099
github comments pt.3
Aug 14, 2023
fb8c7b9
Merge branch 'main' into panond_2
ckrening Aug 14, 2023
6f28ec8
Merge pull request #9 from ckrening/panond_2
ckrening Aug 14, 2023
bbbb480
flake8 formatting
Aug 14, 2023
425e8f9
Merge pull request #10 from ckrening/panond_2
ckrening Aug 14, 2023
3255078
Merge branch 'main' into main
ckrening Aug 14, 2023
d84a8f0
Flake8 formatting
Aug 15, 2023
f66274f
Merge pull request #11 from ckrening/panond_2
ckrening Aug 15, 2023
a538f50
Merge remote-tracking branch 'upstream/main' into pr/1749
kandersolar Sep 13, 2023
b37b98c
remove unnecessary doc entries
kandersolar Sep 13, 2023
b322707
whatsnew cleanup
kandersolar Sep 13, 2023
9c8744f
encoding parameter, and fix parsing both file buffers and file-like o…
kandersolar Sep 13, 2023
5c03b30
docstring improvements
kandersolar Sep 13, 2023
febfcfd
test improvements
kandersolar Sep 13, 2023
92a8db2
lint
kandersolar Sep 13, 2023
5f196bc
update FAQ entry on PAN/OND files
kandersolar Sep 13, 2023
859151a
Apply suggestions from code review
kandersolar Sep 13, 2023
2bd2033
make parse function private
kandersolar Sep 13, 2023
94d90d6
fix overlooked whatsnew issue
kandersolar Sep 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/sphinx/source/whatsnew/v0.10.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ Deprecations

Enhancements
~~~~~~~~~~~~

* Added .pan/.ond reader function
:py:func:`pvlib.iotools.panond`
(:issue:`1747` )

Bug fixes
~~~~~~~~~
Expand All @@ -45,3 +47,4 @@ Requirements
Contributors
~~~~~~~~~~~~
* Taos Transue (:ghuser:`reepoi`)
* Connor Krening (:ghuser:'ckrening')
146 changes: 146 additions & 0 deletions pvlib/data/CPS SCH275KTL-DO-US-800-250kW_275kVA_1.OND
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
PVObject_=pvGInverter
Comment=ChintPower CPS SCH275KTL-DO/US-800 Manufacturer 2020
Version=6.81
ParObj1=2020
Flags=$00381562

PVObject_Commercial=pvCommercial
Comment=www.chintpower.com (China)
Flags=$0041
Manufacturer=ChintPower
Model=CPS SCH275KTL-DO/US-800
DataSource=Manufacturer 2020
YearBeg=2020
Width=0.680
Height=0.337
Depth=1.100
Weight=95.000
NPieces=0
PriceDate=02/06/20 00:02
Currency=EUR
Remarks, Count=2
Str_1=Protection: -30 - +60, IP 66: outdoor installable
Str_2
End of Remarks
End of PVObject pvCommercial
Transfo=Without

Converter=TConverter
PNomConv=250.000
PMaxOUT=250.000
VOutConv=800.0
VMppMin=500
VMPPMax=1500
VAbsMax=1500
PSeuil=500.0
EfficMax=99.01
EfficEuro=98.49
FResNorm=0.00
ModeOper=MPPT
CompPMax=Lim
CompVMax=Lim
MonoTri=Tri
ModeAffEnum=Efficf_POut
UnitAffEnum=kW
PNomDC=253.000
PMaxDC=375.000
IDCMax=0.0
IMaxDC=360.0
INomAC=181.0
IMaxAC=199.0
TPNom=45.0
TPMax=40.0
TPLim1=50.0
TPLimAbs=60.0
PLim1=225.000
PLimAbs=90.000
PInEffMax =150000.000
PThreshEff=3332.4
HasdefaultPThresh=False

ProfilPIO=TCubicProfile
NPtsMax=11
NPtsEff=9
LastCompile=$8085
Mode=1
Point_1=1250,0
Point_2=7500,6923
Point_3=12500,11875
Point_4=25000,24250
Point_5=50000,49100
Point_6=75000,73875
Point_7=150000,148515
Point_8=250000,246500
Point_9=275000,270325
Point_10=0,0
Point_11=0,0
End of TCubicProfile
VNomEff=880.0,1174.0,1300.0,
EfficMaxV=98.260,99.040,98.860,
EfficEuroV=97.986,98.860,98.661,

ProfilPIOV1=TCubicProfile
NPtsMax=11
NPtsEff=9
LastCompile=$8089
Mode=1
Point_1=300.0,0.0
Point_2=13012.7,12500.0
Point_3=25720.2,25000.0
Point_4=51093.4,50000.0
Point_5=76437.0,75000.0
Point_6=127213.5,125000.0
Point_7=190995.2,187500.0
Point_8=255440.9,250000.0
Point_9=281301.1,275000.0
Point_10=0.0,0.0
Point_11=0.0,0.0
End of TCubicProfile

ProfilPIOV2=TCubicProfile
NPtsMax=11
NPtsEff=9
LastCompile=$8089
Mode=1
Point_1=300.0,0.0
Point_2=12850.8,12500.0
Point_3=25401.3,25000.0
Point_4=50581.7,50000.0
Point_5=75795.9,75000.0
Point_6=126211.6,125000.0
Point_7=189623.8,187500.0
Point_8=253138.9,250000.0
Point_9=278763.3,275000.0
Point_10=0.0,0.0
Point_11=0.0,0.0
End of TCubicProfile

ProfilPIOV3=TCubicProfile
NPtsMax=11
NPtsEff=9
LastCompile=$8089
Mode=1
Point_1=300.0,0.0
Point_2=12953.4,12500.0
Point_3=25512.8,25000.0
Point_4=50679.1,50000.0
Point_5=75895.6,75000.0
Point_6=126441.4,125000.0
Point_7=189835.0,187500.0
Point_8=253472.6,250000.0
Point_9=279017.9,275000.0
Point_10=0.0,0.0
Point_11=0.0,0.0
End of TCubicProfile
End of TConverter
NbInputs=36
NbMPPT=12
TanPhiMin=-0.750
TanPhiMax=0.750
NbMSInterne=2
MasterSlave=No_M_S
IsolSurvey =Yes
DC_Switch=Yes
MS_Thresh=0.8
Night_Loss=5.00
End of PVObject pvGInverter
75 changes: 75 additions & 0 deletions pvlib/data/ET-M772BH550GL.PAN
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
PVObject_=pvModule
Version=7.2
Flags=$00900243

PVObject_Commercial=pvCommercial
Comment=ET SOLAR
Flags=$0041
Manufacturer=ET SOLAR
Model=ET-M772BH550GL
DataSource=Manufacturer 2021
YearBeg=2021
Width=1.134
Height=2.278
Depth=0.035
Weight=32.000
NPieces=100
PriceDate=06/04/22 12:39
End of PVObject pvCommercial

Technol=mtSiMono
NCelS=72
NCelP=2
NDiode=3
SubModuleLayout=slTwinHalfCells
FrontSurface=fsARCoating
GRef=1000
TRef=25.0
PNom=550.0
PNomTolUp=0.90
BifacialityFactor=0.700
Isc=14.000
Voc=49.90
Imp=13.110
Vmp=41.96
muISC=7.28
muVocSpec=-128.0
muPmpReq=-0.340
RShunt=300
Rp_0=2000
Rp_Exp=5.50
RSerie=0.203
Gamma=0.980
muGamma=-0.0001
VMaxIEC=1500
VMaxUL=1500
Absorb=0.90
ARev=3.200
BRev=16.716
RDiode=0.010
VRevDiode=-0.70
IMaxDiode=30.0
AirMassRef=1.500
CellArea=165.1
SandiaAMCorr=50.000

PVObject_IAM=pvIAM
Flags=$00
IAMMode=UserProfile
IAMProfile=TCubicProfile
NPtsMax=9
NPtsEff=9
LastCompile=$B18D
Mode=3
Point_1=0.0,1.00000
Point_2=20.0,1.00000
Point_3=30.0,1.00000
Point_4=40.0,0.99000
Point_5=50.0,0.98000
Point_6=60.0,0.96000
Point_7=70.0,0.89000
Point_8=80.0,0.66000
Point_9=90.0,0.00000
End of TCubicProfile
End of PVObject pvIAM
End of PVObject pvModule
1 change: 1 addition & 0 deletions pvlib/iotools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
from pvlib.iotools.sodapro import get_cams # noqa: F401
from pvlib.iotools.sodapro import read_cams # noqa: F401
from pvlib.iotools.sodapro import parse_cams # noqa: F401
from pvlib.iotools.panond import read_panond, parse_panond # noqa: F401
148 changes: 148 additions & 0 deletions pvlib/iotools/panond.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
"""
Get .PAN or .OND file data into a nested dictionary.
"""

import io


def num_type(value):
"""
Determine if a value is float, int or a string
"""
if '.' in value:
try: # Detect float
value_out = float(value)
return value_out

except ValueError: # Otherwise leave as string
value_out = value
return value_out

else:

try: # Detect int
value_out = int(value)
return value_out

except ValueError: # Otherwise leave as string
value_out = value
return value_out


def element_type(element):
"""
Determine if an element is a list then pass to num_type()
"""
if ',' in element: # Detect a list
values = element.split(',')
element_out = []
for val in values: # Determine datatype of each value
element_out.append(num_type(val))

return element_out

else:
return num_type(element)


def parse_panond(fbuf):
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would vote than we only have a read_panond function and eliminate the parse_panond.

We can always create a parse_panond function in the future, but it's annoying to deprecate it.

I believe the reasoning for having the parse function is to avoid duplicate code when you have a get and read function that need similar data handling.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point that no get makes parse less relevant. Aside from supporting get, parse is also helpful when file contents might be already in-memory (retrieved from a database or cloud storage, e.g.) instead of a file on disk. Is that helpful for PAN/OND files? The use cases I can think of feel rather contrived. I will make the parse function private.

Parse a .pan or .ond text file into a nested dictionary.

Parameters
----------
fbuf : File-like object
Buffer of a .pan or .ond file

Returns
-------
comp : Nested Dictionary
Contents of the .pan or .ond file following the indentation of the
file. The value of datatypes are assumed during reading. The value
units are the default used by PVsyst.

Raises
------

Notes
-----

See Also
--------

References
----------
"""
comp = {} # Component
dict_levels = [comp]

fbuf.seek(0)
lines = fbuf.getvalue().splitlines()

for i in range(0, len(lines) - 1):
if lines[i] == '': # Skipping blank lines
continue
# Reading blank lines. Stopping one short to avoid index error.
# Last line never contains important data.
indent_lvl_1 = (len(lines[i]) - len(lines[i].lstrip(' '))) // 2
indent_lvl_2 = (len(lines[i + 1]) - len(lines[i + 1].lstrip(' '))) // 2
line_data = lines[i].split('=')
key = line_data[0].strip()
if len(line_data) > 1:
value = element_type(line_data[1].strip())
else:
value = None
# add a level to the dict. The key here will be ignored.
# Not vital to file function.
if indent_lvl_2 > indent_lvl_1:
current_level = dict_levels[indent_lvl_1]
new_level = {}
current_level[key] = new_level
dict_levels = dict_levels[: indent_lvl_1 + 1] + [new_level]
current_level = dict_levels[indent_lvl_1 + 1]
current_level[key] = value

elif indent_lvl_2 <= indent_lvl_1: # add key/value to dict
current_level = dict_levels[indent_lvl_1]
current_level[key] = value

return comp


def read_panond(file):
"""
Retrieve Module or Inverter data from a .pan or .ond text file,
respectively.

Parameters
----------
file : string or path object
Name or path of a .pan/.ond file

Returns
-------
content : Nested Dictionary
Contents of the .pan or .ond file following the indentation of the
file. The value of datatypes are assumed during reading. The value
units are the default used by PVsyst.

Raises
------

Notes
-----

See Also
--------

References
----------
"""

with open(file, "r", encoding='utf-8-sig') as file:
f_content = file.read()
fbuf = io.StringIO(f_content)

content = parse_panond(fbuf)

return content
Loading