Skip to content

Commit 2fd7008

Browse files
author
Tobin Ford
committed
chamber class usability improvements
1 parent 81e62ca commit 2fd7008

File tree

12 files changed

+399
-266
lines changed

12 files changed

+399
-266
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,7 @@ TEMP/
107107
slurm-*.out
108108

109109
# Run information
110-
tests\data\h5_pytest.h5
110+
tests\data\h5_pytest.h5
111+
112+
# ruff linter and formatter
113+
.ruff_cache

pvdeg/chamber.py

+148-35
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
import json
1212
import os
1313
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
1518

1619
from pvdeg import (
1720
humidity,
@@ -68,9 +71,10 @@ def start_times(df: pd.DataFrame) -> pd.DataFrame:
6871

6972
# return df
7073

74+
7175
def add_previous_setpoints(df: pd.DataFrame) -> pd.DataFrame:
7276
"""
73-
Create setpoints columns with values shifted by 1.
77+
Create setpoints columns with values shifted by 1.
7478
At row k, the shifted value will come from row k - 1.
7579
"""
7680

@@ -81,7 +85,7 @@ def add_previous_setpoints(df: pd.DataFrame) -> pd.DataFrame:
8185

8286
# only care about setpoints, not ramp rates
8387
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}
8589

8690
for name in setpoint_names:
8791
df[f"previous_{name}"] = df[name].shift(1)
@@ -150,9 +154,9 @@ def fill_linear_region(
150154
if rate != 0:
151155
ramp_time = linear_ramp_time(y_0=set_0, y_f=set_f, rate=rate)
152156

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:
156160
raise ValueError(
157161
f"""
158162
Ramp speed is too slow, will not finish ramping up before next set point.
@@ -348,7 +352,6 @@ def _temp_calc_no_irradiance(
348352
# the temperature increase from irradiance at a timestep has a constant factor of
349353
# K = surface area * absorptance so we can bring this out of the loop if we refactor the temperature irradiance
350354

351-
352355
# @njit
353356
def _temp_calc_irradiance(
354357
temps: np.ndarray,
@@ -409,10 +412,10 @@ def sample_temperature(
409412
if not isinstance(air_temperature, np.ndarray):
410413
raise ValueError("Air_temperature must be a numpy array or pandas series")
411414

412-
if "irradiance_full" in setpoints_df.columns:
415+
if "setpoint_irradiance_full" in setpoints_df.columns:
413416
sample_temp = _temp_calc_irradiance(
414417
temps=air_temperature,
415-
irradiances=setpoints_df["irradiance_full"].to_numpy(),
418+
irradiances=setpoints_df["setpoint_irradiance_full"].to_numpy(),
416419
times=setpoints_df.index.to_numpy(),
417420
tau=tau_s,
418421
temp_0=sample_temp_0,
@@ -422,7 +425,7 @@ def sample_temperature(
422425

423426
else:
424427
print(f"""
425-
"irradiance_full" not in setpoints_df.columns
428+
"setpoint_irradiance_full" not in setpoints_df.columns
426429
Current column names {setpoints_df.columns}.
427430
calculating sample temperature without irradiance"
428431
""")
@@ -475,8 +478,9 @@ def __init__(
475478
length=None,
476479
width=None,
477480
):
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)
480484

481485
self.absorptance = absorptance
482486
self.length = length
@@ -488,26 +492,43 @@ def __init__(
488492
if encapsulant:
489493
self.setEncapsulant(encapsulant_thickness)
490494

491-
def setEncapsulant(self, id: str, thickness: float) -> None:
495+
def setEncapsulant(self, pvdeg_file: str, key: str, thickness: float, fp: str = None) -> None:
492496
"""
493497
Set encapsulant diffusivity activation energy, prefactor and solubility activation energy, prefactor.
494498
495499
Activation Energies will be in kJ/mol
496500
497501
Parameters:
498502
-----------
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.
501510
thickness: float
502511
thickness of encapsulant [mm]
503512
"""
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"]
508529
self.encap_thickness = thickness
509530

510-
def setBacksheet(self, id: str, thickness: float) -> None:
531+
def setBacksheet(self, pvdeg_file: str, key: str, thickness: float, fp: str = None ) -> None:
511532
"""
512533
Set backsheet permiability activation energy and prefactor.
513534
@@ -519,9 +540,31 @@ def setBacksheet(self, id: str, thickness: float) -> None:
519540
name of material from `PVDegradationTools/data/materials.json`
520541
thickness: float
521542
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]
522555
"""
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"]
525568
self.back_thickness = thickness
526569

527570
def setDimensions(self, length: float = None, width: float = None) -> None:
@@ -621,15 +664,16 @@ def calc_temperatures(
621664
)
622665

623666
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
626669
):
627670
# gti calculation is very slow because of integration
628671
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"]
631675
) # this may be misleading
632-
print('Saved in self.setpoints as "irradiance_full')
676+
print('Saved in self.setpoints as "setpoints_irradiance_full')
633677

634678
self.sample_temperature = sample_temperature(
635679
self.setpoints,
@@ -683,22 +727,30 @@ def calc_equilibrium_ecapsulant_water(self):
683727

684728
def calc_back_encapsulant_moisture(self, n_steps: int = 20):
685729
"""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+
686734
res, _ = humidity.moisture_eva_back(
687735
eva_moisture_0=self.equilibrium_encapsulant_water.iloc[0],
688736
sample_temp=self.sample_temperature,
689737
rh_at_sample_temp=self.sample_relative_humidity,
690738
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,
694743
pet_prefactor=self.permiability_back_pre,
695744
thickness_eva=self.encap_thickness, # mm
696745
thickness_pet=self.back_thickness, # mm
697746
n_steps=n_steps,
698747
)
699748

700749
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"
702754
)
703755

704756
def calc_relative_humidity_internal_on_back_of_cells(self):
@@ -710,8 +762,11 @@ def calc_relative_humidity_internal_on_back_of_cells(self):
710762
),
711763
rh_at_sample_temp=self.sample_relative_humidity.to_numpy(dtype=np.float64),
712764
)
765+
713766
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"
715770
)
716771

717772
def calc_dew_point(self):
@@ -808,14 +863,72 @@ def gti_from_irradiance_340(self) -> pd.Series:
808863
gti: pd.Series
809864
full spectrum irradiance using ASTM G173-03 AM1.5 spectrum.
810865
"""
811-
self.setpoints["irradiance_full"] = spectral.get_GTI_from_irradiance_340(
866+
self.setpoints["setpoint_irradiance_full"] = spectral.get_GTI_from_irradiance_340(
812867
self.setpoints["setpoint_irradiance_340"]
813868
)
814869

815-
return self.setpoints["irradiance_full"]
870+
return self.setpoints["setpoint_irradiance_full"]
816871

817872
def _ipython_display_(self):
818873
"""
819874
Display the setpoints of the chamber instance.
820875
"""
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))

pvdeg/collection.py

+7-11
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,27 @@
88
import photovoltaic as pv
99

1010

11-
def collection_probability(x, thickness, s, l, d):
11+
def collection_probability(x, thickness: float, s: float, l: float, d: float):
1212
"""
1313
Returns the collection probability (unit 0 to 1) at a distance x (cm) from the junction.
1414
See [1]_.
1515
1616
Parameters
1717
----------
18-
x : array-like
18+
x: array-like
1919
array of x positions from a junction to a surface (typically [cm]).
20-
21-
thickness : numeric
20+
thickness: numeric
2221
Layer thickness [cm].
23-
24-
s : numeric
22+
s: numeric
2523
Surface recombination velocity [cm/s].
26-
27-
l : numeric
24+
l: numeric
2825
Minority carrier diffusion length [cm].
29-
30-
d : numeric
26+
d: numeric
3127
Minority carrier diffusivity [cm^2/Vs].
3228
3329
Returns
3430
-------
35-
cp : array-like
31+
cp: array-like
3632
Collection probability along x.
3733
3834
References

0 commit comments

Comments
 (0)