// De-transposition method to the be used if the meter and panel tilt do not match void PyranoDetranspose(SimMeteo SimMet) { if (pyranoTilter.NoPyranoAnglesDefined) { SimTracker.Calculate(SimSun.Zenith, SimSun.Azimuth, SimMet.Year, SimMet.DayOfYear); pyranoTilter.itsSurfaceAzimuth = SimTracker.SurfAzimuth; pyranoTilter.itsSurfaceSlope = SimTracker.SurfSlope; pyranoTilter.IncidenceAngle = SimTracker.IncidenceAngle; } // Lower bound of bisection double HGloLo = 0; // Higher bound of bisection double HGloHi = SimSun.NExtra; // Calculating the Incidence Angle for the current setup double cosInc = Tilt.GetCosIncidenceAngle(SimSun.Zenith, SimSun.Azimuth, pyranoTilter.itsSurfaceSlope, pyranoTilter.itsSurfaceAzimuth); // Trivial case if (SimMet.TGlo <= 0) { SimSplitter.Calculate(SimSun.Zenith, 0, NExtra: SimSun.NExtra); pyranoTilter.Calculate(SimSplitter.NDir, SimSplitter.HDif, SimSun.NExtra, SimSun.Zenith, SimSun.Azimuth, SimSun.AirMass, SimMet.MonthOfYear, SimMet.Albedo); } else if ((SimSun.Zenith > 87.5 * Util.DTOR) || (cosInc <= Math.Cos(87.5 * Util.DTOR))) { SimMet.HGlo = SimMet.TGlo / ((1 + Math.Cos(pyranoTilter.itsSurfaceSlope)) / 2 + pyranoTilter.itsMonthlyAlbedo[SimMet.MonthOfYear] * (1 - Math.Cos(pyranoTilter.itsSurfaceSlope)) / 2); // Forcing the horizontal irradiance to be composed entirely of diffuse irradiance SimSplitter.HGlo = SimMet.HGlo; SimSplitter.HDif = SimMet.HGlo; SimSplitter.NDir = 0; SimSplitter.HDir = 0; //SimSplitter.Calculate(SimSun.Zenith, HGlo, NExtra: SimSun.NExtra); pyranoTilter.Calculate(SimSplitter.NDir, SimSplitter.HDif, SimSun.NExtra, SimSun.Zenith, SimSun.Azimuth, SimSun.AirMass, SimMet.MonthOfYear, SimMet.Albedo); } // Otherwise, bisection loop else { // Bisection loop while (Math.Abs(HGloHi - HGloLo) > 0.01) { // Use the central value between the domain to start the bisection, and then solve for TGlo, double HGloAv = (HGloLo + HGloHi) / 2; SimSplitter.Calculate(SimSun.Zenith, _HGlo: HGloAv, NExtra: SimSun.NExtra); pyranoTilter.Calculate(SimSplitter.NDir, SimSplitter.HDif, SimSun.NExtra, SimSun.Zenith, SimSun.Azimuth, SimSun.AirMass, SimMet.MonthOfYear, SimMet.Albedo); double TGloAv = pyranoTilter.TGlo; // Compare the TGloAv calculated from the Horizontal guess to the acutal TGlo and change the bounds for analysis // comparing the TGloAv and TGlo if (TGloAv < SimMet.TGlo) { HGloLo = HGloAv; } else { HGloHi = HGloAv; } } } SimMet.HGlo = SimSplitter.HGlo; // This value of the horizontal global should now be transposed to the tilt value from the array. Transpose(SimMet); }
// Calculate the tracker slope, azimuth and incidence angle using public void Calculate(double SunZenith, double SunAzimuth, int Year, int DayOfYear) { switch (itsTrackMode) { case TrackMode.SAXT: // Surface stays parallel to the ground. if (itsTrackerSlope == 0.0) { // For east-west tracking, the absolute value of the sun-azimuth is checked against the tracker azimuth // This is from Duffie and Beckman Page 22. if (itsTrackerAzimuth == Math.PI / 2 || itsTrackerAzimuth == -Math.PI / 2) { // If the user inputs a minimum tilt less than 0, the tracker is able to face the non-dominant direction, so the surface azimuth will change based on the sun azimuth. // However, if the minimum tilt is greater than zero, the tracker can only face the dominant direction. if (itsMinTilt <= 0) { // Math.Abs is used so that the surface azimuth is set to 0 degrees if the sun azimuth is between -90 and 90, and set to 180 degrees if the sun azimuth is between -180 and -90 or between 90 and 180 if (Math.Abs(SunAzimuth) >= Math.Abs(itsTrackerAzimuth)) { SurfAzimuth = Math.PI; } else { SurfAzimuth = 0; } } else { SurfAzimuth = itsTrackerAzimuth - Math.PI / 2; } } else if (itsTrackerAzimuth == 0) { // For north-south tracking, the sign of the sun-azimuth is checked against the tracker azimuth // This is from Duffie and Beckman Page 22. if (SunAzimuth >= itsTrackerAzimuth) { SurfAzimuth = Math.PI / 2; } else { SurfAzimuth = -Math.PI / 2; } } // Surface slope calculated from eq. 31 of reference guide SurfSlope = Math.Atan2(Math.Sin(SunZenith) * Math.Cos(SurfAzimuth - SunAzimuth), Math.Cos(SunZenith)); // If the shadow is greater than the Pitch and backtracking is selected if (useBackTracking) { if (itsTrackerBW / (Math.Cos(SurfSlope)) > itsTrackerPitch) { // NB: From Lorenzo, Narvarte, and Munoz AngleCorrection = Math.Acos((itsTrackerPitch * (Math.Cos(SurfSlope))) / itsTrackerBW); SurfSlope = SurfSlope - AngleCorrection; } } // Adjusting limits for elevation tracking, so if positive min tilt, the tracker operates within limits properly if (itsTrackerAzimuth == Math.PI / 2 || itsTrackerAzimuth == -Math.PI / 2) { if (itsMinTilt <= 0) { if (Math.Abs(SunAzimuth) <= itsTrackerAzimuth) { SurfSlope = Math.Min(itsMaxTilt, SurfSlope); } else if (Math.Abs(SunAzimuth) > itsTrackerAzimuth) { SurfSlope = Math.Min(Math.Abs(itsMinTilt), SurfSlope); } } else if (itsMinTilt > 0) { SurfSlope = Math.Min(SurfSlope, itsMaxTilt); SurfSlope = Math.Max(SurfSlope, itsMinTilt); } } else if (itsTrackerAzimuth == 0) { SurfSlope = Math.Min(itsMaxTilt, SurfSlope); } } else { // Tilt and Roll double aux = Tilt.GetCosIncidenceAngle(SunZenith, SunAzimuth, itsTrackerSlope, itsTrackerAzimuth); // Equation (7) from Marion and Dobos RotAngle = Math.Atan2((Math.Sin(SunZenith) * Math.Sin(SunAzimuth - itsTrackerAzimuth)), aux); //NB: enforcing rotation limits on tracker RotAngle = Math.Min(itsMaxRotationAngle, RotAngle); RotAngle = Math.Max(itsMinRotationAngle, RotAngle); // Slope from equation (1) in Marion and Dobos SurfSlope = Math.Acos(Math.Cos(RotAngle) * Math.Cos(itsTrackerSlope)); // Surface Azimuth from NREL paper if (SurfSlope != 0) { // Equation (3) in Marion and Dobos if ((-Math.PI <= RotAngle) && (RotAngle < -Math.PI / 2)) { SurfAzimuth = itsTrackerAzimuth - Math.Asin(Math.Sin(RotAngle) / Math.Sin(SurfSlope)) - Math.PI; } // Equation (4) in Marion and Dobos else if ((Math.PI / 2 < RotAngle) && (RotAngle <= Math.PI)) { SurfAzimuth = itsTrackerAzimuth - Math.Asin(Math.Sin(RotAngle) / Math.Sin(SurfSlope)) + Math.PI; } // Equation (2) in Marion and Dobos else { SurfAzimuth = itsTrackerAzimuth + Math.Asin(Math.Sin(RotAngle) / Math.Sin(SurfSlope)); } } //NB: 360 degree correction to put Surface Azimuth into the correct quadrant, see Note 1 if (SurfAzimuth > Math.PI) { SurfAzimuth -= (Math.PI) * 2; } else if (SurfAzimuth < -Math.PI) { SurfAzimuth += (Math.PI) * 2; } } break; // Two Axis Tracking case TrackMode.TAXT: // Defining the surface slope SurfSlope = SunZenith; SurfSlope = Math.Max(itsMinTilt, SurfSlope); SurfSlope = Math.Min(itsMaxTilt, SurfSlope); // Defining the surface azimuth SurfAzimuth = SunAzimuth; // Changes the reference frame to be with respect to the reference azimuth if (SurfAzimuth >= 0) { SurfAzimuth -= itsAzimuthRef; } else { SurfAzimuth += itsAzimuthRef; } // Enforcing the rotation limits with respect to the reference azimuth SurfAzimuth = Math.Max(itsMinAzimuth, SurfAzimuth); SurfAzimuth = Math.Min(itsMaxAzimuth, SurfAzimuth); // Moving the surface azimuth back into the azimuth variable convention if (SurfAzimuth >= 0) { SurfAzimuth -= itsAzimuthRef; } else { SurfAzimuth += itsAzimuthRef; } break; // Azimuth Vertical Axis Tracking case TrackMode.AVAT: // Slope is constant. // Defining the surface azimuth SurfAzimuth = SunAzimuth; // Changes the reference frame to be with respect to the reference azimuth if (SurfAzimuth >= 0) { SurfAzimuth -= itsAzimuthRef; } else { SurfAzimuth += itsAzimuthRef; } // Enforcing the rotation limits with respect to the reference azimuth SurfAzimuth = Math.Max(itsMinAzimuth, SurfAzimuth); SurfAzimuth = Math.Min(itsMaxAzimuth, SurfAzimuth); // Moving the surface azimuth back into the azimuth variable convention if (SurfAzimuth >= 0) { SurfAzimuth -= itsAzimuthRef; } else { SurfAzimuth += itsAzimuthRef; } break; // Fixed Tilt with Seasonal Adjustment // determining if the current timestamp is in the summer or winter season and setting SurfSlope accordingly case TrackMode.FTSA: // SummerDate and WinterDate must be recalculated if year changes due to possible leap year if (previousYear != Year) { SummerDate = new DateTime(Year, itsSummerMonth, itsSummerDay); WinterDate = new DateTime(Year, itsWinterMonth, itsWinterDay); } previousYear = Year; // Winter date is before summer date in calender year if (SummerDate.DayOfYear - WinterDate.DayOfYear > 0) { if (DayOfYear >= WinterDate.DayOfYear && DayOfYear < SummerDate.DayOfYear) { SurfSlope = itsPlaneTiltWinter; } else { SurfSlope = itsPlaneTiltSummer; } } // Summer date is before winter date in calender year else { if (DayOfYear >= SummerDate.DayOfYear && DayOfYear < WinterDate.DayOfYear) { SurfSlope = itsPlaneTiltSummer; } else { SurfSlope = itsPlaneTiltWinter; } } break; case TrackMode.NOAT: break; // Throw error to user if there is an issue with the tracker. default: ErrorLogger.Log("Tracking Parameters were incorrectly defined. Please check your input file.", ErrLevel.FATAL); break; } IncidenceAngle = Tilt.GetIncidenceAngle(SunZenith, SunAzimuth, SurfSlope, SurfAzimuth); }