pvlib-python icon indicating copy to clipboard operation
pvlib-python copied to clipboard

Consider updating pvlib.tracking.singleaxis() to return zeros (or similar) for nighttime instead of NaNs

Open williamhobbs opened this issue 4 months ago • 13 comments

Is your feature request related to a problem? Please describe. pvlib.tracking.singleaxis returns NaNs for intervals where the sun is below the horizon. This can result in NaNs propogating to lots of other pvlib outputs and can cause confusion, other issues, etc. See https://groups.google.com/g/pvlib-python/c/ZeVmTC7eddY as an example.

It appears to happen here:

https://github.com/pvlib/pvlib-python/blob/6dfeaf8ee97db6a7a8f3c8109eed8b5d285c4e41/pvlib/tracking.py#L155

Describe the solution you'd like Perhaps change the default to be zero for surface_tilt and tracker_theta (and whatever is most appropriate for aoi and surface_azimuth). Or have an optional input for pvlib.tracking.singleaxis to specify whether it returns NaNs or numbers at night.

Describe alternatives you've considered Applying .fillna(0) (or similar) to the output of pvlib.tracking.singleaxis is what I typically do.

Additional context It looks like this zero vs nan situation was briefly discussed in https://github.com/pvlib/pvlib-python/issues/569. It seems it would be worth further discussion. Is there a significant reason that NaN is the output at night?

fillna(0) is used in the gallery here and here, but not here.

Tagging @cwhanse.

williamhobbs avatar Aug 21 '25 15:08 williamhobbs

It depends on the variable. In this case NaNs make sense because the algorithm cannot calculate the optimal angle under these conditions, and I would think those NaNs are best processed downstream.

adriesse avatar Aug 21 '25 15:08 adriesse

@adriesse, it makes sense to return NaN from the perspective of returning an optimal surface orientation. I'm not sure the docs for pvlib.tracking.singleaxis make it clear that this is the goal of the function, though. It seems reasonable to me to allow the function to have a night stow position.

At a minimum, I think the docs should clarify that it will return NaNs at night (hopefully I didn't simply miss that already being there...).

As far as NaNs being processed downstream, it seems that should be at least addressed in the modelchain, e.g., as used in the google groups example linked above. But I don't ever use modelchain, so I coul dbe missing something.

williamhobbs avatar Aug 21 '25 16:08 williamhobbs

@williamhobbs, a stow capability could reasonably be added to the function, as you say. Either way a small update to the docs would help.

adriesse avatar Aug 21 '25 16:08 adriesse

In general, the tricky part with setting all NaNs to something else is that the NaNs may have different origins, and you may not want to fill them all. A gap in measured data should probably not be filled, or may need special filling logic. In this case, there is no good reason to have a gap in sun position data, so the fillna() should be fine.

adriesse avatar Aug 22 '25 09:08 adriesse

I'm +1 for a night_stow argument, perhaps default of 0., that is set based on only the zenith angle.

I don't think I'd want to just apply fillna unless there was a clear use case as I would rather respect the convention of NaN in --> NaN out.

wholmgren avatar Aug 22 '25 17:08 wholmgren

Good points about NaN in --> NaN out.

Should a new argument be something like night_stow_theta to be explicit about what the value is? And would it be up to the user to pass np.nan if they wanted to preserve the current behavior?

williamhobbs avatar Aug 22 '25 18:08 williamhobbs

NaN in --> NaN out.

I guess that's what I was trying to say in a round about way. :)

adriesse avatar Aug 22 '25 18:08 adriesse

Should a new argument be something like night_stow_theta to be explicit about what the value is? And would it be up to the user to pass np.nan if they wanted to preserve the current behavior?

night_stow or night_stow_angle for me. Passing np.nan shouldn't be prevented, but I would favor a default of 0 as @wholmgren suggests.

cwhanse avatar Aug 22 '25 18:08 cwhanse

night_stow or night_stow_angle for me

I prefer night_stow_angle from those two options. night_stow might not be descriptive enough: it could be boolean or something else. Docs could clarify that it uses the same convention as the tracker_theta output.

I would favor a default of 0

Same for me.

williamhobbs avatar Aug 22 '25 20:08 williamhobbs

It might be good to have consistency between the naming of the output numbers and the night-time default number.

adriesse avatar Aug 26 '25 16:08 adriesse

@adriesse, assuming I understand what you are suggesting, I agree. Because there are a lot of "angle" variables as inputs and outputs, and I frequently see angle definitions and conventions be confused by users, I like descriptive variable names, even if they are long and a bit ugly.

Something like night_stow_tracker_theta would be very descriptive, for example. night_stow_angle could be assumed to be an underdefined "surface tilt" that doesn't specify azimuth. Docs can clarify, of course, either way.

williamhobbs avatar Aug 26 '25 16:08 williamhobbs

Something like night_stow_tracker_theta would be very descriptive

Because the output rotation angle is tracker_theta - if I didn't know that I wouldn't know what "tracker_theta" meant in night_stow_tracker_theta. Too bad we didn't name the output rotation. But as you note, the key is to clearly define the parameter in the docs.

cwhanse avatar Aug 26 '25 16:08 cwhanse

I didn't have a concrete suggestion earlier. Now I'm thinking tracker_theta and tracker_theta_night. I guess the other three outputs can be calculated during the night as well then.

adriesse avatar Aug 26 '25 18:08 adriesse