11
11
import json
12
12
import os
13
13
import matplotlib .pyplot as plt
14
- from IPython .display import display
14
+ from IPython .display import display , HTML
15
+
16
+ import io
17
+ import base64
15
18
16
19
from pvdeg import (
17
20
humidity ,
@@ -68,9 +71,10 @@ def start_times(df: pd.DataFrame) -> pd.DataFrame:
68
71
69
72
# return df
70
73
74
+
71
75
def add_previous_setpoints (df : pd .DataFrame ) -> pd .DataFrame :
72
76
"""
73
- Create setpoints columns with values shifted by 1.
77
+ Create setpoints columns with values shifted by 1.
74
78
At row k, the shifted value will come from row k - 1.
75
79
"""
76
80
@@ -81,7 +85,7 @@ def add_previous_setpoints(df: pd.DataFrame) -> pd.DataFrame:
81
85
82
86
# only care about setpoints, not ramp rates
83
87
setpoints = set (df .columns ).difference (ignore_columns )
84
- setpoint_names = {name .rstrip ("_ramp" ) for name in setpoints }
88
+ setpoint_names = {name .rstrip ("_ramp" ) for name in setpoints }
85
89
86
90
for name in setpoint_names :
87
91
df [f"previous_{ name } " ] = df [name ].shift (1 )
@@ -150,9 +154,9 @@ def fill_linear_region(
150
154
if rate != 0 :
151
155
ramp_time = linear_ramp_time (y_0 = set_0 , y_f = set_f , rate = rate )
152
156
153
- if ramp_time > step_time : # adding a tolerance
154
- # not np.isclose(ramp_time, step_time, rtol=0.01):
155
- # if ramp_time > step_time:
157
+ if ramp_time > step_time : # adding a tolerance
158
+ # not np.isclose(ramp_time, step_time, rtol=0.01):
159
+ # if ramp_time > step_time:
156
160
raise ValueError (
157
161
f"""
158
162
Ramp speed is too slow, will not finish ramping up before next set point.
@@ -348,7 +352,6 @@ def _temp_calc_no_irradiance(
348
352
# the temperature increase from irradiance at a timestep has a constant factor of
349
353
# K = surface area * absorptance so we can bring this out of the loop if we refactor the temperature irradiance
350
354
351
-
352
355
# @njit
353
356
def _temp_calc_irradiance (
354
357
temps : np .ndarray ,
@@ -409,10 +412,10 @@ def sample_temperature(
409
412
if not isinstance (air_temperature , np .ndarray ):
410
413
raise ValueError ("Air_temperature must be a numpy array or pandas series" )
411
414
412
- if "irradiance_full " in setpoints_df .columns :
415
+ if "setpoint_irradiance_full " in setpoints_df .columns :
413
416
sample_temp = _temp_calc_irradiance (
414
417
temps = air_temperature ,
415
- irradiances = setpoints_df ["irradiance_full " ].to_numpy (),
418
+ irradiances = setpoints_df ["setpoint_irradiance_full " ].to_numpy (),
416
419
times = setpoints_df .index .to_numpy (),
417
420
tau = tau_s ,
418
421
temp_0 = sample_temp_0 ,
@@ -422,7 +425,7 @@ def sample_temperature(
422
425
423
426
else :
424
427
print (f"""
425
- "irradiance_full " not in setpoints_df.columns
428
+ "setpoint_irradiance_full " not in setpoints_df.columns
426
429
Current column names { setpoints_df .columns } .
427
430
calculating sample temperature without irradiance"
428
431
""" )
@@ -475,8 +478,9 @@ def __init__(
475
478
length = None ,
476
479
width = None ,
477
480
):
478
- f = open (os .path .join (DATA_DIR , "materials.json" ))
479
- self .materials = json .load (f )
481
+ # f = utilities.read_material()
482
+ # f = open(os.path.join(DATA_DIR, "materials.json"))
483
+ # self.materials = json.load(f)
480
484
481
485
self .absorptance = absorptance
482
486
self .length = length
@@ -488,26 +492,43 @@ def __init__(
488
492
if encapsulant :
489
493
self .setEncapsulant (encapsulant_thickness )
490
494
491
- def setEncapsulant (self , id : str , thickness : float ) -> None :
495
+ def setEncapsulant (self , pvdeg_file : str , key : str , thickness : float , fp : str = None ) -> None :
492
496
"""
493
497
Set encapsulant diffusivity activation energy, prefactor and solubility activation energy, prefactor.
494
498
495
499
Activation Energies will be in kJ/mol
496
500
497
501
Parameters:
498
502
-----------
499
- id: str
500
- name of material from `PVDegradationTools/data/materials.json`
503
+ pvdeg_file: str
504
+ keyword for material json file in `pvdeg/data`. Options:
505
+ >>> "AApermeation", "H2Opermeation", "O2permeation"
506
+ fp: str
507
+ file path to material parameters json with same schema as material parameters json files in `pvdeg/data`. `pvdeg_file` will override `fp` if both are provided.
508
+ key: str
509
+ key corresponding to specific material in the file. In the pvdeg files these have arbitrary names. Inspect the files or use `display_json` or `search_json` to identify the key for desired material.
501
510
thickness: float
502
511
thickness of encapsulant [mm]
503
512
"""
504
- self .diffusivity_encap_ea = (self .materials )[id ]["Ead" ]
505
- self .diffusivity_encap_pre = (self .materials )[id ]["Do" ]
506
- self .solubility_encap_ea = (self .materials )[id ]["Eas" ]
507
- self .solubility_encap_pre = (self .materials )[id ]["So" ]
513
+
514
+ material_dict = utilities .read_material (
515
+ pvdeg_file = pvdeg_file ,
516
+ fp = fp ,
517
+ key = key ,
518
+ )
519
+
520
+ self .diffusivity_encap_ea = material_dict ["Ead" ]
521
+ self .diffusivity_encap_pre = material_dict ["Do" ]
522
+ self .solubility_encap_ea = material_dict ["Eas" ]
523
+ self .solubility_encap_pre = material_dict ["So" ]
524
+
525
+ # self.diffusivity_encap_ea = (self.materials)[key]["Ead"]
526
+ # self.diffusivity_encap_pre = (self.materials)[key]["Do"]
527
+ # self.solubility_encap_ea = (self.materials)[key]["Eas"]
528
+ # self.solubility_encap_pre = (self.materials)[key]["So"]
508
529
self .encap_thickness = thickness
509
530
510
- def setBacksheet (self , id : str , thickness : float ) -> None :
531
+ def setBacksheet (self , pvdeg_file : str , key : str , thickness : float , fp : str = None ) -> None :
511
532
"""
512
533
Set backsheet permiability activation energy and prefactor.
513
534
@@ -519,9 +540,31 @@ def setBacksheet(self, id: str, thickness: float) -> None:
519
540
name of material from `PVDegradationTools/data/materials.json`
520
541
thickness: float
521
542
thickness of backsheet [mm]
543
+
544
+ Parameters:
545
+ -----------
546
+ pvdeg_file: str
547
+ keyword for material json file in `pvdeg/data`. Options:
548
+ >>> "AApermeation", "H2Opermeation", "O2permeation"
549
+ fp: str
550
+ file path to material parameters json with same schema as material parameters json files in `pvdeg/data`. `pvdeg_file` will override `fp` if both are provided.
551
+ key: str
552
+ key corresponding to specific material in the file. In the pvdeg files these have arbitrary names. Inspect the files or use `display_json` or `search_json` to identify the key for desired material.
553
+ thickness: float
554
+ thickness of backsheet [mm]
522
555
"""
523
- self .permiability_back_ea = (self .materials )[id ]["Eap" ]
524
- self .permiability_back_pre = (self .materials )[id ]["Po" ]
556
+
557
+ material_dict = utilities .read_material (
558
+ pvdeg_file = pvdeg_file ,
559
+ fp = fp ,
560
+ key = key ,
561
+ )
562
+
563
+ self .permiability_back_ea = material_dict ["Eap" ]
564
+ self .permiability_back_pre = material_dict ["Po" ]
565
+
566
+ # self.permiability_back_ea = (self.materials)[id]["Eap"]
567
+ # self.permiability_back_pre = (self.materials)[id]["Po"]
525
568
self .back_thickness = thickness
526
569
527
570
def setDimensions (self , length : float = None , width : float = None ) -> None :
@@ -621,15 +664,16 @@ def calc_temperatures(
621
664
)
622
665
623
666
if (
624
- "irradiance_340 " in self .setpoints .columns
625
- and "irradiance_full " not in self .setpoints .columns
667
+ "setpoint_irradiance_340 " in self .setpoints .columns
668
+ and "setpoint_irradiance_full " not in self .setpoints .columns
626
669
):
627
670
# gti calculation is very slow because of integration
628
671
print ("Calculating GTI..." )
629
- self .setpoints ["irradiance_full" ] = spectral .get_GTI_from_irradiance_340 (
630
- self .setpoints ["irradiance_340" ]
672
+ # should this be setpoint irradiance full, it is not a setpoint
673
+ self .setpoints ["setpoint_irradiance_full" ] = spectral .get_GTI_from_irradiance_340 (
674
+ self .setpoints ["setpoint_irradiance_340" ]
631
675
) # this may be misleading
632
- print ('Saved in self.setpoints as "irradiance_full ' )
676
+ print ('Saved in self.setpoints as "setpoints_irradiance_full ' )
633
677
634
678
self .sample_temperature = sample_temperature (
635
679
self .setpoints ,
@@ -683,22 +727,30 @@ def calc_equilibrium_ecapsulant_water(self):
683
727
684
728
def calc_back_encapsulant_moisture (self , n_steps : int = 20 ):
685
729
"""Calculate the moisture on the backside of the encapsulant"""
730
+
731
+ # kJ/mol -> eV
732
+ permiability = utilities .kj_mol_to_ev (self .permiability_back_ea )
733
+
686
734
res , _ = humidity .moisture_eva_back (
687
735
eva_moisture_0 = self .equilibrium_encapsulant_water .iloc [0 ],
688
736
sample_temp = self .sample_temperature ,
689
737
rh_at_sample_temp = self .sample_relative_humidity ,
690
738
equilibrium_eva_water = self .equilibrium_encapsulant_water ,
691
- pet_permiability = utilities .kj_mol_to_ev (
692
- self .permiability_back_ea
693
- ), # kJ/mol -> eV
739
+ # pet_permiability=utilities.kj_mol_to_ev(
740
+ # self.permiability_back_ea
741
+ # ), # kJ/mol -> eV
742
+ pet_permiability = permiability ,
694
743
pet_prefactor = self .permiability_back_pre ,
695
744
thickness_eva = self .encap_thickness , # mm
696
745
thickness_pet = self .back_thickness , # mm
697
746
n_steps = n_steps ,
698
747
)
699
748
700
749
self .back_encapsulant_moisture = pd .Series (
701
- res , index = self .setpoints .index , name = "Back Encapsulant Moisture"
750
+ # res, index=self.setpoints.index, name="Back Encapsulant Moisture"
751
+ res ,
752
+ index = self .setpoints .index ,
753
+ name = "Back Encapsulant Moisture"
702
754
)
703
755
704
756
def calc_relative_humidity_internal_on_back_of_cells (self ):
@@ -710,8 +762,11 @@ def calc_relative_humidity_internal_on_back_of_cells(self):
710
762
),
711
763
rh_at_sample_temp = self .sample_relative_humidity .to_numpy (dtype = np .float64 ),
712
764
)
765
+
713
766
self .relative_humidity_internal_on_back_of_cells = pd .Series (
714
- res , self .setpoints .index , name = "Relative Humidity Internal Cells Backside"
767
+ res ,
768
+ self .setpoints .index ,
769
+ name = "Relative Humidity Internal Cells Backside"
715
770
)
716
771
717
772
def calc_dew_point (self ):
@@ -808,14 +863,72 @@ def gti_from_irradiance_340(self) -> pd.Series:
808
863
gti: pd.Series
809
864
full spectrum irradiance using ASTM G173-03 AM1.5 spectrum.
810
865
"""
811
- self .setpoints ["irradiance_full " ] = spectral .get_GTI_from_irradiance_340 (
866
+ self .setpoints ["setpoint_irradiance_full " ] = spectral .get_GTI_from_irradiance_340 (
812
867
self .setpoints ["setpoint_irradiance_340" ]
813
868
)
814
869
815
- return self .setpoints ["irradiance_full " ]
870
+ return self .setpoints ["setpoint_irradiance_full " ]
816
871
817
872
def _ipython_display_ (self ):
818
873
"""
819
874
Display the setpoints of the chamber instance.
820
875
"""
821
- display (self .setpoints )
876
+
877
+ # Create the plot
878
+ fig , ax = plt .subplots (figsize = (8 , 4 ))
879
+ self .setpoints .plot (ax = ax )
880
+ ax .set_title ("Setpoints Plot (Units Not Demonstrated on Y Axis)" )
881
+ ax .set_xlabel ("Index" )
882
+ ax .set_ylabel ("Value" )
883
+ plt .tight_layout ()
884
+
885
+ # Save the plot to a BytesIO object
886
+ img_buffer = io .BytesIO ()
887
+ plt .savefig (img_buffer , format = 'png' , bbox_inches = 'tight' )
888
+ plt .close (fig )
889
+ img_buffer .seek (0 )
890
+
891
+ # Encode the image as base64
892
+ img_base64 = base64 .b64encode (img_buffer .read ()).decode ('utf-8' )
893
+
894
+ html_content = f"""
895
+ <div style="border:1px solid #ddd; border-radius: 5px; padding: 3px; margin-top: 5px;">
896
+ <h2>Chamber Simulation</h2>
897
+
898
+ <div id="setpoints_dataframe" onclick="toggleVisibility('content_setpoints_dataframe')" style="cursor: pointer; background-color: #000000; color: #FFFFFF; padding: 5px; border-radius: 3px; margin-bottom: 1px;">
899
+ <h4 style="font-family: monospace; margin: 0;">
900
+ <span id="arrow_content_setpoints_dataframe" style="color: #b676c2;">►</span>
901
+ Setpoints Dataframe
902
+ </h4>
903
+ </div>
904
+ <div id="content_setpoints_dataframe" style="display:none; margin-left: 20px; padding: 5px;">
905
+ { self .setpoints ._repr_html_ ()}
906
+ </div>
907
+
908
+ <div id="setpoints_plot" onclick="toggleVisibility('content_setpoints_plot')" style="cursor: pointer; background-color: #000000; color: #FFFFFF; padding: 5px; border-radius: 3px; margin-bottom: 1px;">
909
+ <h4 style="font-family: monospace; margin: 0;">
910
+ <span id="arrow_content_setpoints_plot" style="color: #b676c2;">►</span>
911
+ Setpoints Plot
912
+ </h4>
913
+ </div>
914
+ <div id="content_setpoints_plot" style="display:none; margin-left: 20px; padding: 5px;">
915
+ <h3>Setpoints Plot</h3>
916
+ <img src="data:image/png;base64,{ img_base64 } " alt="Setpoints Plot" style="max-width:100%; height:auto;">
917
+ </div>
918
+
919
+ </div>
920
+ <script>
921
+ function toggleVisibility(id) {{
922
+ var content = document.getElementById(id);
923
+ var arrow = document.getElementById('arrow_' + id);
924
+ if (content.style.display === 'none') {{
925
+ content.style.display = 'block';
926
+ arrow.innerHTML = '▼';
927
+ }} else {{
928
+ content.style.display = 'none';
929
+ arrow.innerHTML = '►';
930
+ }}
931
+ }}
932
+ </script>
933
+ """
934
+ display (HTML (html_content ))
0 commit comments