solarpandas 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. solarpandas-0.1.0/PKG-INFO +335 -0
  2. solarpandas-0.1.0/README.md +298 -0
  3. solarpandas-0.1.0/pyproject.toml +81 -0
  4. solarpandas-0.1.0/src/solarpandas/__init__.py +33 -0
  5. solarpandas-0.1.0/src/solarpandas/accessors/__init__.py +36 -0
  6. solarpandas-0.1.0/src/solarpandas/accessors/clearsky.py +265 -0
  7. solarpandas-0.1.0/src/solarpandas/accessors/param.py +105 -0
  8. solarpandas-0.1.0/src/solarpandas/accessors/qcflag.py +170 -0
  9. solarpandas-0.1.0/src/solarpandas/accessors/qcontrol.py +437 -0
  10. solarpandas-0.1.0/src/solarpandas/accessors/solarplot.py +624 -0
  11. solarpandas-0.1.0/src/solarpandas/accessors/solpos.py +375 -0
  12. solarpandas-0.1.0/src/solarpandas/base.py +463 -0
  13. solarpandas-0.1.0/src/solarpandas/config.py +263 -0
  14. solarpandas-0.1.0/src/solarpandas/helpers.py +83 -0
  15. solarpandas-0.1.0/src/solarpandas/iohelpers.py +53 -0
  16. solarpandas-0.1.0/src/solarpandas/logtools.py +103 -0
  17. solarpandas-0.1.0/src/solarpandas/mplstyles/__init__.py +28 -0
  18. solarpandas-0.1.0/src/solarpandas/mplstyles/dtmap.mplstyle +5 -0
  19. solarpandas-0.1.0/src/solarpandas/mplstyles/qc.mplstyle +9 -0
  20. solarpandas-0.1.0/src/solarpandas/origin/__init__.py +1 -0
  21. solarpandas-0.1.0/src/solarpandas/origin/bsrn/__init__.py +6 -0
  22. solarpandas-0.1.0/src/solarpandas/origin/bsrn/cf-metadata.json +425 -0
  23. solarpandas-0.1.0/src/solarpandas/origin/bsrn/core.py +905 -0
  24. solarpandas-0.1.0/src/solarpandas/origin/bsrn/helpers.py +203 -0
  25. solarpandas-0.1.0/src/solarpandas/origin/bsrn/lr_parsers.py +637 -0
  26. solarpandas-0.1.0/src/solarpandas/origin/bsrn/tables.py +124 -0
  27. solarpandas-0.1.0/src/solarpandas/origin/bsrn/types.py +12 -0
  28. solarpandas-0.1.0/src/solarpandas/origin/bsrn/utils.py +68 -0
  29. solarpandas-0.1.0/src/solarpandas/py.typed +0 -0
  30. solarpandas-0.1.0/src/solarpandas/qcontrol/Kspace.py +303 -0
  31. solarpandas-0.1.0/src/solarpandas/qcontrol/__init__.py +7 -0
  32. solarpandas-0.1.0/src/solarpandas/qcontrol/closure.py +116 -0
  33. solarpandas-0.1.0/src/solarpandas/qcontrol/erl.py +170 -0
  34. solarpandas-0.1.0/src/solarpandas/qcontrol/helpers.py +83 -0
  35. solarpandas-0.1.0/src/solarpandas/qcontrol/ppl.py +227 -0
  36. solarpandas-0.1.0/src/solarpandas/qcontrol/qcrad.py +23 -0
  37. solarpandas-0.1.0/src/solarpandas/qcontrol/timeshift.py +96 -0
  38. solarpandas-0.1.0/src/solarpandas/qcontrol/tracker.py +112 -0
  39. solarpandas-0.1.0/src/solarpandas/sample_data/__init__.py +35 -0
  40. solarpandas-0.1.0/src/solarpandas/sample_data/car_bsrn_2016.parquet +0 -0
  41. solarpandas-0.1.0/src/solarpandas/types/__init__.py +7 -0
  42. solarpandas-0.1.0/src/solarpandas/types/annotated.py +79 -0
  43. solarpandas-0.1.0/src/solarpandas/types/qcflag.py +268 -0
  44. solarpandas-0.1.0/src/solarpandas/validate.py +334 -0
@@ -0,0 +1,335 @@
1
+ Metadata-Version: 2.3
2
+ Name: solarpandas
3
+ Version: 0.1.0
4
+ Summary: Analysis of solar irradiance time series with pandas
5
+ Keywords: solar,irradiance,timeseries,pandas,bsrn,photovoltaics
6
+ Author: Jose A Ruiz Arias
7
+ Author-email: Jose A Ruiz Arias <jararias@uma.es>
8
+ License: CC-BY-NC-SA-4.0
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Natural Language :: English
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Scientific/Engineering
16
+ Classifier: Topic :: Scientific/Engineering :: Physics
17
+ Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
18
+ Classifier: License :: Free To Use But Restricted
19
+ Requires-Dist: colorcet>=3.2.1
20
+ Requires-Dist: datashader>=0.19.1
21
+ Requires-Dist: loguru>=0.7.3
22
+ Requires-Dist: lxml>=6.1.1
23
+ Requires-Dist: matplotlib>=3.10.9
24
+ Requires-Dist: numpy>=2.4.4
25
+ Requires-Dist: pandas>=3.0.2
26
+ Requires-Dist: platformdirs>=4.9.6
27
+ Requires-Dist: pyarrow>=24.0.0
28
+ Requires-Dist: sparta-solar>=0.1.0
29
+ Requires-Dist: sunwhere>=1.4.1
30
+ Requires-Dist: tomlkit>=0.15.0
31
+ Requires-Python: >=3.13
32
+ Project-URL: Documentation, https://jararias.github.io/solarpandas
33
+ Project-URL: Homepage, https://github.com/jararias/solarpandas
34
+ Project-URL: Issues, https://github.com/jararias/solarpandas/issues
35
+ Project-URL: Repository, https://github.com/jararias/solarpandas
36
+ Description-Content-Type: text/markdown
37
+
38
+
39
+ <p align="center">
40
+ <img src="https://raw.githubusercontent.com/jararias/solarpandas/main/docs/images/logo_solarpandas_azul_fondo_transparente.png" alt="logo" width="25%">
41
+ </p>
42
+
43
+ # solarpandas: pandas for solar resource assessment
44
+
45
+ ![python versions](https://img.shields.io/badge/python-3.13-blue.svg)
46
+ ![tests-badge](https://raw.githubusercontent.com/jararias/solarpandas/main/docs/images/badges/tests-badge.svg)
47
+ ![coverage-badge](https://raw.githubusercontent.com/jararias/solarpandas/main/docs/images/badges/coverage-badge.svg)
48
+ [![License](https://img.shields.io/badge/license-CC%20BY--NC--SA%204.0-green.svg)](https://creativecommons.org/licenses/by-nc-sa/4.0/)
49
+
50
+ 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 is a personal project that I have been developing and using for my own research for years. It integrates under a common framework both standard methods in solar resource modeling and libraries and models I have developed myself. The incomparable extensibility of pandas makes it the perfect framework for this. The result is an advanced, modern, and sophisticated library that combines the unique power and versatility of pandas with the most widely used methods in solar resource modeling.
51
+
52
+ ## Main features
53
+
54
+ - **𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 subclasses pandas Series and DataFrame** to embed site location metadata (latitude, longitude and elevation) and optional general-purpose custom metadata. This approach frees the user from having to pass location metadata to every individual routine, as it is automatically propagated across objects and only needs to be specified once, while retaining the rich API of pandas in the SolarSeries and SolarDataFrame objects.
55
+
56
+ ```python
57
+ >>> import solarpandas as sp
58
+
59
+ # pandas class solarpandas class
60
+ # ------------ -----------------
61
+ # Series SolarSeries
62
+ # DataFrame SolarDataFrame
63
+
64
+ >>> sdf = sp.SolarSeries(
65
+ ... data=np.linspace(500, 550, 6), # as in pandas Series
66
+ ... index=pd.date_range("2026-06-01 10", periods=6, freq="30min"), # a sequence of datetimes, as required by pandas Series
67
+ ... name="ghi",
68
+ ... # metadata...
69
+ ... latitude=36.949, # mandatory in solarpandas
70
+ ... longitude=-3.823, # mandatory in solarpandas
71
+ ... elevation=914, # if not providad, set to 0 meters above mean sea level
72
+ ... custom_metadata={ # optional, following json standard rules
73
+ ... "site": "Jayena",
74
+ ... "network": "my-network",
75
+ ... }
76
+ ... )
77
+ >>> sdf
78
+ 2026-06-01 10:00:00 500.0
79
+ 2026-06-01 10:30:00 510.0
80
+ 2026-06-01 11:00:00 520.0
81
+ 2026-06-01 11:30:00 530.0
82
+ 2026-06-01 12:00:00 540.0
83
+ 2026-06-01 12:30:00 550.0
84
+ Freq: 30min, Name: ghi, dtype: float64
85
+ [site=Jayena/my-network latitude=36.9490° longitude=-3.8230° elevation=914.0 m]
86
+
87
+ >>> sdf_hourly = sdf.resample("h").mean()
88
+ >>> sdf_hourly
89
+ 2026-06-01 10:00:00 505.0
90
+ 2026-06-01 11:00:00 525.0
91
+ 2026-06-01 12:00:00 545.0
92
+ Freq: h, dtype: float64
93
+ [site=Jayena/my-network latitude=36.9490° longitude=-3.8230° elevation=914.0 m]
94
+ ```
95
+
96
+ - SolarDataFrame instances **can be serialized and de-serialized** to and from `parquet` or `csv` files **keeping the original metadata**. This opens the door to standardized metadata for solar time series following cf-compliant rules.
97
+
98
+ ```python
99
+ >>> sdf = sp.sample_data.load_carpentras_data()
100
+ >>> sdf.custom_metadata
101
+ {'station': 'CAR',
102
+ 'location': 'Carpentras, France',
103
+ 'network': 'BSRN',
104
+ 'source': 'BSRN FTP server via solarpandas',
105
+ 'institution': 'Jose A Ruiz-Arias (solarpandas dev) and BSRN data providers',
106
+ 'contact': 'xxx@xxx.xxx',
107
+ 'timestamp_alignment': 'center',
108
+ 'surface_type': 'cultivated',
109
+ 'topography_type': 'hilly, rural',
110
+ ...
111
+ 'variables': {
112
+ 'ghi': {
113
+ 'standard_name': 'surface_downwelling_shortwave_flux_in_air',
114
+ 'long_name': 'global horizontal irradiance',
115
+ 'short_name': 'ghi',
116
+ 'units': 'W m-2',
117
+ 'cell_methods': 'time: mean (interval: 1 minute)',
118
+ 'bsrn_name': 'global_horizontal_avg'
119
+ },
120
+ ...
121
+ }
122
+ }
123
+ ```
124
+
125
+ - 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 provides **fast memory-cached accessors for** key aspects of solar resource modeling, such as the calculation of **solar position** (via [sunwhere](https://github.com/jararias/sunwhere)) **and clear-sky irradiance** (via [sparta-solar](https://github.com/jararias/sparta-solar)). These parameters are not stored as columns of the dataframe, keeping it clean and compact, but are instead exposed as virtual columns through the accessors.
126
+
127
+ ```python
128
+ >>> sdf = sp.sample_data.load_carpentras_data()
129
+ >>> sdf
130
+ ghi dni dif
131
+ time
132
+ 2016-01-01 00:00:30+00:00 -1.0 0.0 -1.0
133
+ 2016-01-01 00:01:30+00:00 -1.0 0.0 -1.0
134
+ 2016-01-01 00:02:30+00:00 -1.0 0.0 -1.0
135
+ ... ... ... ...
136
+ 2016-12-31 23:57:30+00:00 -2.0 -1.0 -2.0
137
+ 2016-12-31 23:58:30+00:00 -2.0 -1.0 -2.0
138
+ 2016-12-31 23:59:30+00:00 -2.0 -1.0 -2.0
139
+ [527040 rows x 3 columns]
140
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
141
+
142
+ >>> sdf.solpos.zenith # solar zenith angle
143
+ time
144
+ 2016-01-01 00:00:30+00:00 158.666033
145
+ 2016-01-01 00:01:30+00:00 158.630072
146
+ 2016-01-01 00:02:30+00:00 158.592202
147
+ ...
148
+ 2016-12-31 23:57:30+00:00 158.713107
149
+ 2016-12-31 23:58:30+00:00 158.683687
150
+ 2016-12-31 23:59:30+00:00 158.652329
151
+ Length: 527040, dtype: float64
152
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
153
+
154
+ >>> sdf.solpos.sunrise(units="utc") # sunrise time, UTC
155
+ time
156
+ 2016-01-01 00:00:30+00:00 2016-01-01 07:37:23.580818129
157
+ 2016-01-01 00:01:30+00:00 2016-01-01 07:37:23.564837855
158
+ 2016-01-01 00:02:30+00:00 2016-01-01 07:37:23.548856487
159
+ ...
160
+ 2016-12-31 23:57:30+00:00 2017-01-01 07:37:05.570849828
161
+ 2016-12-31 23:58:30+00:00 2017-01-01 07:37:05.553684227
162
+ 2016-12-31 23:59:30+00:00 2017-01-01 07:37:05.536517540
163
+ Length: 527040, dtype: datetime64[ns]
164
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
165
+
166
+ >>> sdf.lta.ghi # clear-sky ghi assuming a long-term average clear-sky atmosphere
167
+ time
168
+ 2016-01-01 00:00:30+00:00 0.0
169
+ 2016-01-01 00:01:30+00:00 0.0
170
+ 2016-01-01 00:02:30+00:00 0.0
171
+ ...
172
+ 2016-12-31 23:57:30+00:00 0.0
173
+ 2016-12-31 23:58:30+00:00 0.0
174
+ 2016-12-31 23:59:30+00:00 0.0
175
+ Length: 527040, dtype: float64
176
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
177
+
178
+ >>> sdf.cda.ghi # idem, but for a clean and dry clear-sky atmosphere
179
+ time
180
+ 2016-01-01 00:00:30+00:00 0.0
181
+ 2016-01-01 00:01:30+00:00 0.0
182
+ 2016-01-01 00:02:30+00:00 0.0
183
+ ...
184
+ 2016-12-31 23:57:30+00:00 0.0
185
+ 2016-12-31 23:58:30+00:00 0.0
186
+ 2016-12-31 23:59:30+00:00 0.0
187
+ Length: 527040, dtype: float64
188
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
189
+
190
+ >>> sdf.clearsky.ghi # idem, but using a preset clear-sky atmosphere from sparta-solar
191
+ time
192
+ 2016-01-01 00:00:30+00:00 0.0
193
+ 2016-01-01 00:01:30+00:00 0.0
194
+ 2016-01-01 00:02:30+00:00 0.0
195
+ ...
196
+ 2016-12-31 23:57:30+00:00 0.0
197
+ 2016-12-31 23:58:30+00:00 0.0
198
+ 2016-12-31 23:59:30+00:00 0.0
199
+ Name: ghi, Length: 527040, dtype: float64
200
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
201
+
202
+ >>> sdf.clearsky.compute( # ad-hoc non-cached calculation
203
+ ... atmosphere="crs_soda",
204
+ ... model="SPARTA")
205
+ dni dhi dif ghi csi
206
+ time
207
+ 2016-01-01 00:00:30 0.0 0.0 0.0 0.0 0.0
208
+ 2016-01-01 00:01:30 0.0 0.0 0.0 0.0 0.0
209
+ 2016-01-01 00:02:30 0.0 0.0 0.0 0.0 0.0
210
+ ... ... ... ... ... ...
211
+ 2016-12-31 23:57:30 0.0 0.0 0.0 0.0 0.0
212
+ 2016-12-31 23:58:30 0.0 0.0 0.0 0.0 0.0
213
+ 2016-12-31 23:59:30 0.0 0.0 0.0 0.0 0.0
214
+ [527040 rows x 5 columns]
215
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
216
+ ```
217
+
218
+ - 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 is **shipped with BSRN high-level data retrieval** and parsing utilities. When BSRN data is requested for the first time, it is downloaded, parsed, and archived locally in `parquet` format for fast subsequent access.
219
+
220
+ ```python
221
+ >>> from solarpandas.origin import bsrn
222
+
223
+ >>> year_table = bsrn.data_availability(update="auto", as_year_table=True)
224
+ >>> print(year_table)
225
+ site | 9 0 0 1 1 2 2
226
+ | 5 0 5 0 5 0 5
227
+ -----+------------------------------------
228
+ abs | ######
229
+ aes |
230
+ ale | ###########
231
+ asp | ##########################
232
+ bar | ###############################
233
+ ber | ###################### ## #
234
+ bil | ###########################
235
+ ... ...
236
+
237
+ # 2) load station metadata (cached locally)
238
+ >>> meta = bsrn.load_metadata(update="auto")
239
+
240
+ # 3) load BSRN measurements for one station/year
241
+ >>> sdf = bsrn.load_data(
242
+ ... site="car",
243
+ ... years=2016,
244
+ ... logical_record="LR0100",
245
+ ... group="essential")
246
+ ghi dni dif
247
+ time
248
+ 2016-01-01 00:00:30+00:00 -1.0 0.0 -1.0
249
+ 2016-01-01 00:01:30+00:00 -1.0 0.0 -1.0
250
+ 2016-01-01 00:02:30+00:00 -1.0 0.0 -1.0
251
+ ... ... ... ...
252
+ 2016-12-31 23:57:30+00:00 -2.0 -1.0 -2.0
253
+ 2016-12-31 23:58:30+00:00 -2.0 -1.0 -2.0
254
+ 2016-12-31 23:59:30+00:00 -2.0 -1.0 -2.0
255
+ [527040 rows x 3 columns]
256
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
257
+ ```
258
+
259
+ - It has **built-in quality-control workflows** enhanced with a tailored qc-specific ExtensionDType, `qcflag`. The QC workflow is memory-cached and the 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴's `qcflag` dtype provides direct access to QC-specific methods via the `.flag` accessor.
260
+
261
+ ```python
262
+ >>> sdf = sp.sample_data.load_carpentras_data()
263
+ >>> sdf.qc.tests # perform the tests and return them
264
+ ghi_ppl dif_ppl ... closure trackeroff
265
+ time ...
266
+ 2016-01-01 00:00:30+00:00 0 0 ... 0 0
267
+ 2016-01-01 00:01:30+00:00 0 0 ... 0 0
268
+ 2016-01-01 00:02:30+00:00 0 0 ... 0 0
269
+ ... ... ... ... ... ...
270
+ 2016-12-31 23:57:30+00:00 0 0 ... 0 0
271
+ 2016-12-31 23:58:30+00:00 0 0 ... 0 0
272
+ 2016-12-31 23:59:30+00:00 0 0 ... 0 0
273
+ [527040 rows x 13 columns]
274
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=0.0 m]
275
+
276
+ >>> sdf.qc.ghi_ppl # access individual tests
277
+ time
278
+ 2016-01-01 00:00:30+00:00 0
279
+ 2016-01-01 00:01:30+00:00 0
280
+ 2016-01-01 00:02:30+00:00 0
281
+ ..
282
+ 2016-12-31 23:57:30+00:00 0
283
+ 2016-12-31 23:58:30+00:00 0
284
+ 2016-12-31 23:59:30+00:00 0
285
+ Name: ghi_ppl, Length: 527040, dtype: qcflag
286
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=0.0 m]
287
+
288
+ >>> sdf.qc.ghi_ppl.dtype # tests data have a special dtype `qcflag`
289
+ QCFlagDType()
290
+
291
+ # the type `qcflag` provides specific functionalities throught the `.flag` accessor
292
+ >>> sdf.qc.ghi_ppl.flag.counts() # all data points in this dataset pass this test (by default, night time is excluded)
293
+ PASSED 265417
294
+ NOT_VERIFIABLE 1653
295
+ Name: count, dtype: int64
296
+
297
+ # and additional plotting methods:
298
+ >>> sdf.qc.ghi_ppl.flag.pieplot()
299
+ >>> sdf.qc.ghi_ppl.flag.heatmap()
300
+ >>> sdf.qc.ghi_ppl.flag.plot(sdf)
301
+
302
+ # 4) bolean masks from sets of individual tests
303
+ >>> failed_ghi = sdf.qc.failed(component="ghi")
304
+ >>> passed_all = sdf.qc.passed()
305
+
306
+ # 5) mask failed data points
307
+ >>> sdf_masked = sdf.qc.mask_failed(component="ghi")
308
+ >>> sdf.qc.heatmap(component="ghi")
309
+ ```
310
+
311
+ - It provides specialized plotting helpers for solar datasets through the ``.solarplot`` accessor.
312
+
313
+ ```python
314
+ # diurnal line plot
315
+ >>> fig1 = sdf.solarplot.diurnal(column="ghi")
316
+
317
+ # date-time heatmap
318
+ >>> fig2 = sdf.solarplot.heatmap(column="ghi", time_ref="tst", twilight_line=True)
319
+ ```
320
+
321
+ ## Installation
322
+
323
+ With pip:
324
+
325
+ ```bash
326
+ pip install solarpandas
327
+ ```
328
+
329
+ and with [uv](https://docs.astral.sh/uv/):
330
+
331
+ ```bash
332
+ uv add solarpandas
333
+ ```
334
+
335
+ Find further details in the [documentation](https://jararias.github.io/solarpandas).
@@ -0,0 +1,298 @@
1
+
2
+ <p align="center">
3
+ <img src="https://raw.githubusercontent.com/jararias/solarpandas/main/docs/images/logo_solarpandas_azul_fondo_transparente.png" alt="logo" width="25%">
4
+ </p>
5
+
6
+ # solarpandas: pandas for solar resource assessment
7
+
8
+ ![python versions](https://img.shields.io/badge/python-3.13-blue.svg)
9
+ ![tests-badge](https://raw.githubusercontent.com/jararias/solarpandas/main/docs/images/badges/tests-badge.svg)
10
+ ![coverage-badge](https://raw.githubusercontent.com/jararias/solarpandas/main/docs/images/badges/coverage-badge.svg)
11
+ [![License](https://img.shields.io/badge/license-CC%20BY--NC--SA%204.0-green.svg)](https://creativecommons.org/licenses/by-nc-sa/4.0/)
12
+
13
+ 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 is a personal project that I have been developing and using for my own research for years. It integrates under a common framework both standard methods in solar resource modeling and libraries and models I have developed myself. The incomparable extensibility of pandas makes it the perfect framework for this. The result is an advanced, modern, and sophisticated library that combines the unique power and versatility of pandas with the most widely used methods in solar resource modeling.
14
+
15
+ ## Main features
16
+
17
+ - **𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 subclasses pandas Series and DataFrame** to embed site location metadata (latitude, longitude and elevation) and optional general-purpose custom metadata. This approach frees the user from having to pass location metadata to every individual routine, as it is automatically propagated across objects and only needs to be specified once, while retaining the rich API of pandas in the SolarSeries and SolarDataFrame objects.
18
+
19
+ ```python
20
+ >>> import solarpandas as sp
21
+
22
+ # pandas class solarpandas class
23
+ # ------------ -----------------
24
+ # Series SolarSeries
25
+ # DataFrame SolarDataFrame
26
+
27
+ >>> sdf = sp.SolarSeries(
28
+ ... data=np.linspace(500, 550, 6), # as in pandas Series
29
+ ... index=pd.date_range("2026-06-01 10", periods=6, freq="30min"), # a sequence of datetimes, as required by pandas Series
30
+ ... name="ghi",
31
+ ... # metadata...
32
+ ... latitude=36.949, # mandatory in solarpandas
33
+ ... longitude=-3.823, # mandatory in solarpandas
34
+ ... elevation=914, # if not providad, set to 0 meters above mean sea level
35
+ ... custom_metadata={ # optional, following json standard rules
36
+ ... "site": "Jayena",
37
+ ... "network": "my-network",
38
+ ... }
39
+ ... )
40
+ >>> sdf
41
+ 2026-06-01 10:00:00 500.0
42
+ 2026-06-01 10:30:00 510.0
43
+ 2026-06-01 11:00:00 520.0
44
+ 2026-06-01 11:30:00 530.0
45
+ 2026-06-01 12:00:00 540.0
46
+ 2026-06-01 12:30:00 550.0
47
+ Freq: 30min, Name: ghi, dtype: float64
48
+ [site=Jayena/my-network latitude=36.9490° longitude=-3.8230° elevation=914.0 m]
49
+
50
+ >>> sdf_hourly = sdf.resample("h").mean()
51
+ >>> sdf_hourly
52
+ 2026-06-01 10:00:00 505.0
53
+ 2026-06-01 11:00:00 525.0
54
+ 2026-06-01 12:00:00 545.0
55
+ Freq: h, dtype: float64
56
+ [site=Jayena/my-network latitude=36.9490° longitude=-3.8230° elevation=914.0 m]
57
+ ```
58
+
59
+ - SolarDataFrame instances **can be serialized and de-serialized** to and from `parquet` or `csv` files **keeping the original metadata**. This opens the door to standardized metadata for solar time series following cf-compliant rules.
60
+
61
+ ```python
62
+ >>> sdf = sp.sample_data.load_carpentras_data()
63
+ >>> sdf.custom_metadata
64
+ {'station': 'CAR',
65
+ 'location': 'Carpentras, France',
66
+ 'network': 'BSRN',
67
+ 'source': 'BSRN FTP server via solarpandas',
68
+ 'institution': 'Jose A Ruiz-Arias (solarpandas dev) and BSRN data providers',
69
+ 'contact': 'xxx@xxx.xxx',
70
+ 'timestamp_alignment': 'center',
71
+ 'surface_type': 'cultivated',
72
+ 'topography_type': 'hilly, rural',
73
+ ...
74
+ 'variables': {
75
+ 'ghi': {
76
+ 'standard_name': 'surface_downwelling_shortwave_flux_in_air',
77
+ 'long_name': 'global horizontal irradiance',
78
+ 'short_name': 'ghi',
79
+ 'units': 'W m-2',
80
+ 'cell_methods': 'time: mean (interval: 1 minute)',
81
+ 'bsrn_name': 'global_horizontal_avg'
82
+ },
83
+ ...
84
+ }
85
+ }
86
+ ```
87
+
88
+ - 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 provides **fast memory-cached accessors for** key aspects of solar resource modeling, such as the calculation of **solar position** (via [sunwhere](https://github.com/jararias/sunwhere)) **and clear-sky irradiance** (via [sparta-solar](https://github.com/jararias/sparta-solar)). These parameters are not stored as columns of the dataframe, keeping it clean and compact, but are instead exposed as virtual columns through the accessors.
89
+
90
+ ```python
91
+ >>> sdf = sp.sample_data.load_carpentras_data()
92
+ >>> sdf
93
+ ghi dni dif
94
+ time
95
+ 2016-01-01 00:00:30+00:00 -1.0 0.0 -1.0
96
+ 2016-01-01 00:01:30+00:00 -1.0 0.0 -1.0
97
+ 2016-01-01 00:02:30+00:00 -1.0 0.0 -1.0
98
+ ... ... ... ...
99
+ 2016-12-31 23:57:30+00:00 -2.0 -1.0 -2.0
100
+ 2016-12-31 23:58:30+00:00 -2.0 -1.0 -2.0
101
+ 2016-12-31 23:59:30+00:00 -2.0 -1.0 -2.0
102
+ [527040 rows x 3 columns]
103
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
104
+
105
+ >>> sdf.solpos.zenith # solar zenith angle
106
+ time
107
+ 2016-01-01 00:00:30+00:00 158.666033
108
+ 2016-01-01 00:01:30+00:00 158.630072
109
+ 2016-01-01 00:02:30+00:00 158.592202
110
+ ...
111
+ 2016-12-31 23:57:30+00:00 158.713107
112
+ 2016-12-31 23:58:30+00:00 158.683687
113
+ 2016-12-31 23:59:30+00:00 158.652329
114
+ Length: 527040, dtype: float64
115
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
116
+
117
+ >>> sdf.solpos.sunrise(units="utc") # sunrise time, UTC
118
+ time
119
+ 2016-01-01 00:00:30+00:00 2016-01-01 07:37:23.580818129
120
+ 2016-01-01 00:01:30+00:00 2016-01-01 07:37:23.564837855
121
+ 2016-01-01 00:02:30+00:00 2016-01-01 07:37:23.548856487
122
+ ...
123
+ 2016-12-31 23:57:30+00:00 2017-01-01 07:37:05.570849828
124
+ 2016-12-31 23:58:30+00:00 2017-01-01 07:37:05.553684227
125
+ 2016-12-31 23:59:30+00:00 2017-01-01 07:37:05.536517540
126
+ Length: 527040, dtype: datetime64[ns]
127
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
128
+
129
+ >>> sdf.lta.ghi # clear-sky ghi assuming a long-term average clear-sky atmosphere
130
+ time
131
+ 2016-01-01 00:00:30+00:00 0.0
132
+ 2016-01-01 00:01:30+00:00 0.0
133
+ 2016-01-01 00:02:30+00:00 0.0
134
+ ...
135
+ 2016-12-31 23:57:30+00:00 0.0
136
+ 2016-12-31 23:58:30+00:00 0.0
137
+ 2016-12-31 23:59:30+00:00 0.0
138
+ Length: 527040, dtype: float64
139
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
140
+
141
+ >>> sdf.cda.ghi # idem, but for a clean and dry clear-sky atmosphere
142
+ time
143
+ 2016-01-01 00:00:30+00:00 0.0
144
+ 2016-01-01 00:01:30+00:00 0.0
145
+ 2016-01-01 00:02:30+00:00 0.0
146
+ ...
147
+ 2016-12-31 23:57:30+00:00 0.0
148
+ 2016-12-31 23:58:30+00:00 0.0
149
+ 2016-12-31 23:59:30+00:00 0.0
150
+ Length: 527040, dtype: float64
151
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
152
+
153
+ >>> sdf.clearsky.ghi # idem, but using a preset clear-sky atmosphere from sparta-solar
154
+ time
155
+ 2016-01-01 00:00:30+00:00 0.0
156
+ 2016-01-01 00:01:30+00:00 0.0
157
+ 2016-01-01 00:02:30+00:00 0.0
158
+ ...
159
+ 2016-12-31 23:57:30+00:00 0.0
160
+ 2016-12-31 23:58:30+00:00 0.0
161
+ 2016-12-31 23:59:30+00:00 0.0
162
+ Name: ghi, Length: 527040, dtype: float64
163
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
164
+
165
+ >>> sdf.clearsky.compute( # ad-hoc non-cached calculation
166
+ ... atmosphere="crs_soda",
167
+ ... model="SPARTA")
168
+ dni dhi dif ghi csi
169
+ time
170
+ 2016-01-01 00:00:30 0.0 0.0 0.0 0.0 0.0
171
+ 2016-01-01 00:01:30 0.0 0.0 0.0 0.0 0.0
172
+ 2016-01-01 00:02:30 0.0 0.0 0.0 0.0 0.0
173
+ ... ... ... ... ... ...
174
+ 2016-12-31 23:57:30 0.0 0.0 0.0 0.0 0.0
175
+ 2016-12-31 23:58:30 0.0 0.0 0.0 0.0 0.0
176
+ 2016-12-31 23:59:30 0.0 0.0 0.0 0.0 0.0
177
+ [527040 rows x 5 columns]
178
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
179
+ ```
180
+
181
+ - 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 is **shipped with BSRN high-level data retrieval** and parsing utilities. When BSRN data is requested for the first time, it is downloaded, parsed, and archived locally in `parquet` format for fast subsequent access.
182
+
183
+ ```python
184
+ >>> from solarpandas.origin import bsrn
185
+
186
+ >>> year_table = bsrn.data_availability(update="auto", as_year_table=True)
187
+ >>> print(year_table)
188
+ site | 9 0 0 1 1 2 2
189
+ | 5 0 5 0 5 0 5
190
+ -----+------------------------------------
191
+ abs | ######
192
+ aes |
193
+ ale | ###########
194
+ asp | ##########################
195
+ bar | ###############################
196
+ ber | ###################### ## #
197
+ bil | ###########################
198
+ ... ...
199
+
200
+ # 2) load station metadata (cached locally)
201
+ >>> meta = bsrn.load_metadata(update="auto")
202
+
203
+ # 3) load BSRN measurements for one station/year
204
+ >>> sdf = bsrn.load_data(
205
+ ... site="car",
206
+ ... years=2016,
207
+ ... logical_record="LR0100",
208
+ ... group="essential")
209
+ ghi dni dif
210
+ time
211
+ 2016-01-01 00:00:30+00:00 -1.0 0.0 -1.0
212
+ 2016-01-01 00:01:30+00:00 -1.0 0.0 -1.0
213
+ 2016-01-01 00:02:30+00:00 -1.0 0.0 -1.0
214
+ ... ... ... ...
215
+ 2016-12-31 23:57:30+00:00 -2.0 -1.0 -2.0
216
+ 2016-12-31 23:58:30+00:00 -2.0 -1.0 -2.0
217
+ 2016-12-31 23:59:30+00:00 -2.0 -1.0 -2.0
218
+ [527040 rows x 3 columns]
219
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
220
+ ```
221
+
222
+ - It has **built-in quality-control workflows** enhanced with a tailored qc-specific ExtensionDType, `qcflag`. The QC workflow is memory-cached and the 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴's `qcflag` dtype provides direct access to QC-specific methods via the `.flag` accessor.
223
+
224
+ ```python
225
+ >>> sdf = sp.sample_data.load_carpentras_data()
226
+ >>> sdf.qc.tests # perform the tests and return them
227
+ ghi_ppl dif_ppl ... closure trackeroff
228
+ time ...
229
+ 2016-01-01 00:00:30+00:00 0 0 ... 0 0
230
+ 2016-01-01 00:01:30+00:00 0 0 ... 0 0
231
+ 2016-01-01 00:02:30+00:00 0 0 ... 0 0
232
+ ... ... ... ... ... ...
233
+ 2016-12-31 23:57:30+00:00 0 0 ... 0 0
234
+ 2016-12-31 23:58:30+00:00 0 0 ... 0 0
235
+ 2016-12-31 23:59:30+00:00 0 0 ... 0 0
236
+ [527040 rows x 13 columns]
237
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=0.0 m]
238
+
239
+ >>> sdf.qc.ghi_ppl # access individual tests
240
+ time
241
+ 2016-01-01 00:00:30+00:00 0
242
+ 2016-01-01 00:01:30+00:00 0
243
+ 2016-01-01 00:02:30+00:00 0
244
+ ..
245
+ 2016-12-31 23:57:30+00:00 0
246
+ 2016-12-31 23:58:30+00:00 0
247
+ 2016-12-31 23:59:30+00:00 0
248
+ Name: ghi_ppl, Length: 527040, dtype: qcflag
249
+ [site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=0.0 m]
250
+
251
+ >>> sdf.qc.ghi_ppl.dtype # tests data have a special dtype `qcflag`
252
+ QCFlagDType()
253
+
254
+ # the type `qcflag` provides specific functionalities throught the `.flag` accessor
255
+ >>> sdf.qc.ghi_ppl.flag.counts() # all data points in this dataset pass this test (by default, night time is excluded)
256
+ PASSED 265417
257
+ NOT_VERIFIABLE 1653
258
+ Name: count, dtype: int64
259
+
260
+ # and additional plotting methods:
261
+ >>> sdf.qc.ghi_ppl.flag.pieplot()
262
+ >>> sdf.qc.ghi_ppl.flag.heatmap()
263
+ >>> sdf.qc.ghi_ppl.flag.plot(sdf)
264
+
265
+ # 4) bolean masks from sets of individual tests
266
+ >>> failed_ghi = sdf.qc.failed(component="ghi")
267
+ >>> passed_all = sdf.qc.passed()
268
+
269
+ # 5) mask failed data points
270
+ >>> sdf_masked = sdf.qc.mask_failed(component="ghi")
271
+ >>> sdf.qc.heatmap(component="ghi")
272
+ ```
273
+
274
+ - It provides specialized plotting helpers for solar datasets through the ``.solarplot`` accessor.
275
+
276
+ ```python
277
+ # diurnal line plot
278
+ >>> fig1 = sdf.solarplot.diurnal(column="ghi")
279
+
280
+ # date-time heatmap
281
+ >>> fig2 = sdf.solarplot.heatmap(column="ghi", time_ref="tst", twilight_line=True)
282
+ ```
283
+
284
+ ## Installation
285
+
286
+ With pip:
287
+
288
+ ```bash
289
+ pip install solarpandas
290
+ ```
291
+
292
+ and with [uv](https://docs.astral.sh/uv/):
293
+
294
+ ```bash
295
+ uv add solarpandas
296
+ ```
297
+
298
+ Find further details in the [documentation](https://jararias.github.io/solarpandas).