Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

471 repr #544

Merged
merged 4 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 58 additions & 10 deletions bifacial_radiance/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,23 @@ def _checkRaypath():
os.environ['RAYPATH'] = splitter.join(filter(None, raysplit + ['.'+splitter]))
except (KeyError, AttributeError, TypeError):
raise Exception('No RAYPATH set for RADIANCE. Please check your RADIANCE installation.')


class SuperClass:
def __repr__(self):
return str(type(self)) + ' : ' + str({key: self.__dict__[key] for key in self.columns})
#return str(self.__dict__)
@property
def columns(self):
return [attr for attr in dir(self) if not (attr.startswith('_') or attr.startswith('methods')
or attr.startswith('columns') or callable(getattr(self,attr)))]
@property
def methods(self):
return [attr for attr in dir(self) if (not (attr.startswith('_') or attr.startswith('methods')
or attr.startswith('columns')) and callable(getattr(self,attr)))]



class RadianceObj:
class RadianceObj(SuperClass):
"""
The RadianceObj top level class is used to work on radiance objects,
keep track of filenames, sky values, PV module configuration, etc.
Expand All @@ -345,7 +358,8 @@ class RadianceObj:

"""
def __repr__(self):
return str(self.__dict__)
#return str(self.__dict__)
return str(type(self)) + ' : ' + str({key: self.__dict__[key] for key in self.columns if key != 'trackerdict'})
def __init__(self, name=None, path=None, hpc=False):
'''
initialize RadianceObj with path of Radiance materials and objects,
Expand Down Expand Up @@ -2622,7 +2636,7 @@ def analysis1axis(self, trackerdict=None, singleindex=None, accuracy='low',

# End RadianceObj definition

class GroundObj:
class GroundObj(SuperClass):
"""
Class to set and return details for the ground surface materials and reflectance.
If 1 albedo value is passed, it is used as default.
Expand Down Expand Up @@ -2818,7 +2832,7 @@ def _makeGroundString(self, index=0, cumulativesky=False):



class SceneObj:
class SceneObj(SuperClass):
'''
Scene information including PV module type, bifaciality, array info
pv module orientation defaults: Azimuth = 180 (south)
Expand All @@ -2839,8 +2853,7 @@ class SceneObj:
-------

'''
def __repr__(self):
return str(self.__dict__)

def __init__(self, module=None, name=None):
''' initialize SceneObj
'''
Expand Down Expand Up @@ -3116,7 +3129,7 @@ def saveImage(self, filename=None, view=None):



class MetObj:
class MetObj(SuperClass):
"""
Meteorological data from EPW file.

Expand All @@ -3140,6 +3153,29 @@ class MetObj:
SAM and PVSyst use left-labeled interval data and NSRDB uses centered.

"""
@property
def tmydata(self):
keys = ['ghi', 'dhi', 'dni', 'albedo', 'dewpoint', 'pressure',
'temp_air', 'wind_speed', 'meastracker_angle', 'tracker_theta',
'surface_tilt', 'surface_azimuth']
return pd.DataFrame({key:self.__dict__.get(key, None) for key in keys },
index = self.__dict__['datetime']).dropna(axis=1)

@property
def metadata(self):
keys = ['latitude', 'longitude', 'elevation', 'timezone', 'city', 'label',
'timezone']
return {key:self.__dict__.get(key, None) for key in keys}

def __repr__(self):
# return metadata and tmydata stats...
import io
buf = io.StringIO()
self.tmydata.info(memory_usage=False, buf=buf)
tmyinfo = buf.getvalue()
buf.close()
return f"<class 'bifacial_radiance.main.MetObj'>.metadata:\n"\
f"{self.metadata}\n<class 'bifacial_radiance.main.MetObj'>.tmydata:\n {tmyinfo}\n"

def __init__(self, tmydata, metadata, label = 'right'):

Expand Down Expand Up @@ -3379,6 +3415,7 @@ def _set1axis(self, azimuth=180, limit_angle=45, angledelta=None,
'surf_azm':self.surface_azimuth[i],
'surf_tilt':self.surface_tilt[i],
'theta':self.tracker_theta[i],
'dni':self.dni[i],
'ghi':self.ghi[i],
'dhi':self.dhi[i],
'temp_air':self.temp_air[i],
Expand Down Expand Up @@ -3540,6 +3577,7 @@ def _makeTrackerCSV(self, theta_list, trackingdata):
trackerdict[theta]['count'] = datetimetemp.__len__()
#Create new temp csv file with zero values for all times not equal to datetimetemp
# write 8760 2-column csv: GHI,DHI
dni_temp = []
ghi_temp = []
dhi_temp = []
for g, d, time in zip(self.ghi, self.dhi,
Expand Down Expand Up @@ -3573,13 +3611,23 @@ def _makeTrackerCSV(self, theta_list, trackingdata):
return trackerdict


class AnalysisObj:
class AnalysisObj(SuperClass):
"""
Analysis class for performing raytrace to obtain irradiance measurements
at the array, as well plotting and reporting results.
"""
def __printval__(self, attr):
try:
t = type(getattr(self,attr, None)[0])
except TypeError:
t = None
if t is float:
return np.array(getattr(self,attr)).round(3).tolist()
else:
return getattr(self,attr)

def __repr__(self):
return str(self.__dict__)
return str(type(self)) + ' : ' + str({key: self.__printval__(key) for key in self.columns})
def __init__(self, octfile=None, name=None, hpc=False):
"""
Initialize AnalysisObj by pointing to the octfile. Scan information
Expand Down
3 changes: 2 additions & 1 deletion bifacial_radiance/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class ModuleObj(SuperClass):
Pass this object into makeScene or makeScene1axis.
"""


def __repr__(self):
return str(type(self)) + ' : ' + str(self.getDataDict())
def __init__(self, name=None, x=None, y=None, z=None, bifi=1, modulefile=None,
text=None, customtext='', xgap=0.01, ygap=0.0, zgap=0.1,
numpanels=1, rewriteModulefile=True, cellModule=None,
Expand Down
1 change: 1 addition & 0 deletions docs/sphinx/source/whatsnew/pending.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Bug fixes
Documentation
~~~~~~~~~~~~~~
* No longer provide a warning message when both `hub_height` and `clearance_height` are passed to :py:class:`~bifacial_radiance.AnalysisObj.moduleAnalysis` (:pull:`540`)
* More useful __repr__ output in :py:class:`~bifacial_radiance.AnalysisObj and :py:class:`~bifacial_radiance.MetObj (:issue:`471`)

Contributors
~~~~~~~~~~~~
Expand Down
13 changes: 11 additions & 2 deletions tests/test_bifacial_radiance.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def test_RadianceObj_set1axis():
# test set1axis. requires metdata for boulder.
name = "_test_set1axis"
demo = bifacial_radiance.RadianceObj(name)
assert str(demo)[-16:-2]==name #this depends on the insertion order of the dictionary repr of demo - may not be consistent
assert len(str(demo)) > 300 # Make sure something is printed out here for demo.__repr__
#try:
# epwfile = demo.getEPW(lat=40.01667, lon=-105.25) # From EPW: {N 40° 1'} {W 105° 15'}
#except: # adding an except in case the internet connection in the lab forbids the epw donwload.
Expand Down Expand Up @@ -196,6 +196,7 @@ def test_1axis_gencumSky():

demo = bifacial_radiance.RadianceObj(name) # Create a RadianceObj 'object'
demo.setGround(albedo) # input albedo number or material name like 'concrete'. To see options, run this without any input.
assert demo.ground.methods == ['printGroundMaterials']
metdata = demo.readWeatherFile(weatherFile=MET_FILENAME, starttime='01_01_01', endtime = '01_01_23', coerce_year=2001) # read in the EPW weather data from above
moduleText = '! genbox black test-module 0.98 1.95 0.02 | xform -t -0.49 -2.0 0 -a 2 -t 0 2.05 0'
module=demo.makeModule(name='test-module',x=0.984,y=1.95, numpanels = 2, ygap = 0.1, text=moduleText)
Expand Down Expand Up @@ -322,7 +323,7 @@ def test_AnalysisObj_linePtsMake3D():
linepts = analysis._linePtsMake3D(0,0,0,1,1,1,0,0,0,1,2,3,'0 1 0')
assert linepts == '0 0 0 0 1 0 \r1 1 1 0 1 0 \r0 0 0 0 1 0 \r1 1 1 0 1 0 \r0 0 0 0 1 0 \r1 1 1 0 1 0 \r' # v2.5.0 new linepts because now x and z also increase not only y.
#assert linepts == '0 0 0 0 1 0 \r0 1 0 0 1 0 \r0 0 1 0 1 0 \r0 1 1 0 1 0 \r0 0 2 0 1 0 \r0 1 2 0 1 0 \r'
assert str(analysis)[12:16]=='None'
assert str(analysis)[-5:-1]=='None' # this depends on the order of the dict. but generally aligns with 'octfile' in alphabetical order..


def test_gendaylit2manual():
Expand Down Expand Up @@ -374,6 +375,7 @@ def test_left_label_metdata():
# right labeled MetObj
import pvlib
import pandas as pd
import unittest
(tmydata, metadata) = pvlib.iotools.epw.read_epw(MET_FILENAME, coerce_year=2001)
# rename different field parameters to match output from
# pvlib.tmy.readtmy: DNI, DHI, DryBulb, Wspd
Expand All @@ -385,6 +387,12 @@ def test_left_label_metdata():
'albedo':'Alb'
}, inplace=True)
metdata1 = bifacial_radiance.MetObj(tmydata, metadata, label='left')
columnlist = ['ghi', 'dhi', 'dni', 'albedo', 'dewpoint', 'pressure', 'temp_air','wind_speed']
assert all([col in list(metdata1.tmydata.columns) for col in columnlist])
metadatalist = ['city', 'elevation', 'label', 'latitude', 'longitude', 'timezone']
assert all([col in list(metdata1.metadata.keys()) for col in metadatalist])


demo = bifacial_radiance.RadianceObj('test')
metdata2 = demo.readWeatherFile(weatherFile=MET_FILENAME, label='right', coerce_year=2001)
pd.testing.assert_frame_equal(metdata1.solpos[:-1], metdata2.solpos[:-1])
Expand Down Expand Up @@ -417,6 +425,7 @@ def test_analyzeRow():
assert rowscan[rowscan.keys()[2]][0][0] == rowscan[rowscan.keys()[2]][1][0]
# Assert Y is different for two different modules
assert rowscan[rowscan.keys()[1]][0][0]+2 == rowscan[rowscan.keys()[1]][1][0]
assert (analysis.__printval__('x')[1] == 0) & (analysis.x[1] != 0)


def test_addMaterialGroundRad():
Expand Down
6 changes: 4 additions & 2 deletions tests/test_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,14 @@ def test_CellLevelModule():

module.addCellModule(**cellParams, centerJB=0.01) #centerJB simulations still under development.
# assert module.text == '! genbox black cellPVmodule 0.156 0.156 0.02 | xform -t -0.44 -0.87 0 -a 6 -t 0.176 0 0 -a 5.0 -t 0 0.176 0 -a 2 -t 0 0.772 0 | xform -t 0 0.181 0 -a 1 -t 0 1.73 0'

assert len(module.cellModule.__repr__()) == 119
assert len(module.__repr__()) > 490

def test_TorqueTubes_Module():
name = "_test_TorqueTubes"
demo = bifacial_radiance.RadianceObj(name) # Create a RadianceObj 'object'
module = demo.makeModule(name='square', y=0.95,x=1.59, tubeParams={'tubetype':'square', 'axisofrotation':False}, hpc=True) #suppress saving .json
# test pre-0.4.0 compatibility keys 'bool' and 'torqueTubeMaterial'. Remove these when it's deprecated..
module = demo.makeModule(name='square', y=0.95,x=1.59, tubeParams={'torqueTubeMaterial':'Metal_Grey','bool':True, 'tubetype':'square', 'axisofrotation':False}, hpc=True) #suppress saving .json
assert module.x == 1.59
assert module.text == '! genbox black square 1.59 0.95 0.02 | xform -t -0.795 -0.475 0 -a 1 -t 0 0.95 0\r\n! genbox Metal_Grey tube1 1.6 0.1 0.1 | xform -t -0.8 -0.05 -0.2'
module = demo.makeModule(name='round', y=0.95,x=1.59, tubeParams={'tubetype':'round', 'axisofrotation':False}, hpc=True)
Expand Down