Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <algorithm>
50 : #include <cmath>
51 : #include <fstream>
52 : #include <ostream>
53 :
54 : // ObjexxFCL Headers
55 : #include <ObjexxFCL/Array1D.hh>
56 : #include <ObjexxFCL/string.functions.hh>
57 :
58 : // EnergyPlus Headers
59 : #include <EnergyPlus/Construction.hh>
60 : #include <EnergyPlus/DElightManagerF.hh>
61 : #include <EnergyPlus/Data/EnergyPlusData.hh>
62 : #include <EnergyPlus/DataDElight.hh>
63 : // #include <EnergyPlus/DataDaylighting.hh>
64 : #include <EnergyPlus/DataEnvironment.hh>
65 : #include <EnergyPlus/DataGlobals.hh>
66 : #include <EnergyPlus/DataHeatBalance.hh>
67 : #include <EnergyPlus/DataIPShortCuts.hh>
68 : #include <EnergyPlus/DataStringGlobals.hh>
69 : #include <EnergyPlus/DataSurfaces.hh>
70 : #include <EnergyPlus/DaylightingManager.hh>
71 : #include <EnergyPlus/General.hh>
72 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
73 : #include <EnergyPlus/InternalHeatGains.hh>
74 : #include <EnergyPlus/Material.hh>
75 : #include <EnergyPlus/OutputProcessor.hh>
76 : #include <EnergyPlus/UtilityRoutines.hh>
77 :
78 : extern "C" {
79 : #include <DElight/DElightManagerC.h>
80 : }
81 :
82 : namespace EnergyPlus {
83 :
84 : namespace DElightManagerF {
85 :
86 : // MODULE INFORMATION
87 : // AUTHOR Robert J. Hitchcock
88 : // DATE WRITTEN August 2003
89 : // MODIFIED January 2004
90 : // RE-ENGINEERED na
91 :
92 : // PURPOSE OF THIS MODULE:
93 :
94 : // Defines INTERFACE Statements for Fortran calls to the DElightManagerC.cpp module.
95 : // The DElightManager.cpp module in turn defines the C/C++ calls to the DElight DLL.
96 : // Also, contains subroutines for performing associated operations.
97 :
98 : // METHODOLOGY EMPLOYED:
99 :
100 : // C Language Implementation of DOE2.1d and Superlite 3.0
101 : // Daylighting Algorithms with new Complex Fenestration System
102 : // analysis algorithms.
103 : // The original DOE2 daylighting algorithms and implementation
104 : // in FORTRAN were developed by F.C. Winkelmann at the
105 : // Lawrence Berkeley National Laboratory.
106 : // The original Superlite algorithms and implementation in FORTRAN
107 : // were developed by Michael Modest and Jong-Jin Kim
108 : // under contract with Lawrence Berkeley National Laboratory.
109 : // REFERENCES:
110 :
111 : // "Daylighting Calculation in DOE-2," F.C.Winkelmann, LBL-11353, May 1983
112 : // "Daylighting Simulation in the DOE-2 Building Energy Analysis Program,"
113 : // F.C. Winkelmann and S. Selkowitz, Energy and Buildings 8(1985)271-286
114 :
115 : // USE STATEMENTS:
116 : using namespace DataDElight;
117 :
118 0 : void DElightInputGenerator(EnergyPlusData &state)
119 : {
120 :
121 : // SUBROUTINE INFORMATION:
122 : // AUTHOR Robert J. Hitchcock
123 : // DATE WRITTEN August 2003
124 : // MODIFIED February 2004 - Changes to accommodate mods in DElight IDD
125 : // RE-ENGINEERED na
126 :
127 : // PURPOSE OF THIS SUBROUTINE:
128 : // This subroutine creates a DElight input file from EnergyPlus processed input.
129 :
130 : // USE STATEMENTS:
131 : using namespace DataHeatBalance; // Gives access to Building, Zone and Lights data
132 : using namespace DataEnvironment; // Gives access to Site data
133 : using namespace DataSurfaces; // Gives access to Surface data
134 : using namespace DataStringGlobals; // Gives access to Program Path and Current Time/Date
135 : using InternalHeatGains::CheckLightsReplaceableMinMaxForZone;
136 : using InternalHeatGains::GetDesignLightingLevelForZone;
137 :
138 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
139 : int iNumDElightZones; // Counter for Thermal Zones with hosted Daylighting:DElight objects
140 : int iNumOpaqueSurfs; // Counter for opaque surfaces in each zone
141 : int iNumWindows; // Counter for windows hosted in each surface
142 : int iconstruct; // Index for construction type of surfaces
143 : int iMatlLayer; // Index for the outside (i.e., 1st) Material Layer for a Construction
144 : Real64 rExtVisRefl; // Exterior visible reflectance of a material
145 : Real64 rLightLevel; // installed lighting level for current zone
146 : Real64 CosBldgRelNorth; // Cosine of Building rotation
147 : Real64 SinBldgRelNorth; // Sine of Building rotation
148 : Real64 CosZoneRelNorth; // Cosine of Zone rotation
149 : Real64 SinZoneRelNorth; // Sine of Zone rotation
150 : Real64 Xb; // temp var for transformation calc
151 : Real64 Yb; // temp var for transformation calc
152 0 : Vector3<Real64> RefPt_WCS_Coord;
153 0 : Array1D_int iWndoConstIndexes(100);
154 : bool lWndoConstFound; // Flag for non-unique window const index
155 0 : std::string cNameWOBlanks; // Name without blanks
156 : bool ErrorsFound;
157 : int iHostedCFS;
158 : bool lWndoIsDoppelganger; // Flag for doppelganger window test
159 : int iDoppelganger;
160 : bool ldoTransform;
161 : Real64 roldAspectRatio;
162 : Real64 rnewAspectRatio;
163 : Real64 Xo;
164 : Real64 XnoRot;
165 : Real64 Xtrans;
166 : Real64 Yo;
167 : Real64 YnoRot;
168 : Real64 Ytrans;
169 :
170 : // Formats
171 : static constexpr std::string_view Format_901("Version EPlus : DElight input generated from EnergyPlus processed input {}\n");
172 : static constexpr std::string_view Format_902(
173 : "\nBuilding_Name {}\nSite_Latitude {:12.4F}\nSite_Longitude {:12.4F}\nSite_Altitude {:12.4F}\nBldg_Azimuth {:12.4F}\nSite_Time_Zone "
174 : "{:12.4F}\nAtm_Moisture 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07\nAtm_Turbidity 0.12 0.12 0.12 0.12 0.12 0.12 0.12 "
175 : "0.12 0.12 0.12 0.12 0.12\n");
176 : static constexpr std::string_view Format_903("\nZONES\nN_Zones {:4}\n");
177 : static constexpr std::string_view Format_904(
178 : "\nZONE DATA\nZone {}\nBldgSystem_Zone_Origin {:12.4F}{:12.4F}{:12.4F}\nZone_Azimuth {:12.4F}\nZone_Multiplier {:5}\nZone_Floor_Area "
179 : "{:12.4F}\nZone_Volume {:12.4F}\nZone_Installed_Lighting {:12.4F}\nMin_Input_Power {:12.4F}\nMin_Light_Fraction "
180 : "{:12.4F}\nLight_Ctrl_Steps {:3}\nLight_Ctrl_Prob {:12.4F}\nView_Azimuth 0.0\nMax_Grid_Node_Area {:12.4F}\n");
181 : static constexpr std::string_view Format_905("\nZONE LIGHTING SCHEDULES\nN_Lt_Scheds 0\n");
182 : static constexpr std::string_view Format_906("\nZONE SURFACES\nN_Surfaces {:4}\n");
183 : static constexpr std::string_view Format_907("\nZONE SURFACE DATA\nSurface {}\nWCS_Azimuth {:12.4F}\nWCS_Tilt {:12.4F}\nVis_Refl "
184 : "{:12.4F}\nExt_Refl {:12.4F}\nGnd_Refl 0.2\nN_WCS_Vertices {:6}\n");
185 : static constexpr std::string_view Format_908("Vertex {:12.4F}{:12.4F}{:12.4F}\n");
186 : static constexpr std::string_view Format_909("\nSURFACE WINDOWS\nN_Windows {:6}\n");
187 : static constexpr std::string_view Format_910(
188 : "\nSURFACE WINDOW DATA\nWindow {}\nGlass_Type {:8}\nShade_Flag 0\nOverhang_Fin_Depth 0.0 0.0 "
189 : "0.0\nOverhang_Fin_Distance 0.0 0.0 0.0\nN_WCS_Vertices {:4}\n");
190 : static constexpr std::string_view Format_911("\nSURFACE CFS\nN_CFS {:6}\n");
191 : static constexpr std::string_view Format_915(
192 : "\nCOMPLEX FENESTRATION DATA\nCFS_Name {}\nCFS_Type {}\nFenestration_Rotation {:12.4F}\nN_WCS_Vertices {:4}\n");
193 : static constexpr std::string_view Format_912("\nZONE REFERENCE POINTS\nN_Ref_Pts {:4}\n");
194 : static constexpr std::string_view Format_913(
195 : "\nZONE REFERENCE POINT DATA\nReference_Point {}\nRefPt_WCS_Coords {:12.4F}{:12.4F}{:12.4F}\nZone_Fraction "
196 : "{:12.4F}\nLight_Set_Pt {:12.4F}\nLight_Ctrl_Type {:4}\n");
197 : static constexpr std::string_view Format_914("\nBUILDING SHADES\nN_BShades 0\n");
198 : static constexpr std::string_view Format_920("\nLIBRARY DATA\nGLASS TYPES\nN_Glass_Types {:4}\n");
199 : static constexpr std::string_view Format_921(
200 : "\nGLASS TYPE DATA\nName {:6}\nEPlusDiffuse_Transmittance {:12.4F}\nEPlusDiffuse_Int_Reflectance "
201 : "{:12.4F}\nEPlus_Vis_Trans_Coeff_1 {:17.9F}\nEPlus_Vis_Trans_Coeff_2 {:17.9F}\nEPlus_Vis_Trans_Coeff_3 "
202 : "{:17.9F}\nEPlus_Vis_Trans_Coeff_4 {:17.9F}\nEPlus_Vis_Trans_Coeff_5 {:17.9F}\nEPlus_Vis_Trans_Coeff_6 {:17.9F}\n");
203 :
204 : // Init the ErrorsFound flag
205 0 : ErrorsFound = false;
206 :
207 0 : GetInputDElightComplexFenestration(state, ErrorsFound);
208 :
209 0 : CheckForGeometricTransform(state, ldoTransform, roldAspectRatio, rnewAspectRatio);
210 :
211 : // Init the counter for Thermal Zones with hosted Daylighting:DElight objects
212 0 : iNumDElightZones = 0;
213 :
214 : // Init the counter for Window Construction types for writing to Library Data section of DElight input file
215 0 : int iNumWndoConsts = 0;
216 :
217 : // Open a file for writing DElight input from EnergyPlus data
218 0 : auto delightInFile = state.files.delightIn.open(state, "DElightInputGenerator", state.files.outputControl.delightin); // (THIS_AUTO_OK)
219 :
220 : // Start of DElight input file
221 0 : print(delightInFile, Format_901, state.dataStrGlobals->CurrentDateTime);
222 :
223 : // Building Data Section retrieved from DataHeatBalance and DataEnvironment modules
224 : // Remove any blanks from the Building Name for ease of input to DElight
225 0 : cNameWOBlanks = ReplaceBlanksWithUnderscores(state.dataHeatBal->BuildingName);
226 0 : print(delightInFile,
227 : Format_902,
228 : cNameWOBlanks,
229 0 : state.dataEnvrn->Latitude,
230 0 : state.dataEnvrn->Longitude,
231 0 : state.dataEnvrn->Elevation * M2FT,
232 0 : state.dataHeatBal->BuildingAzimuth,
233 0 : state.dataEnvrn->TimeZoneNumber);
234 :
235 : // Calc cos and sin of Building Relative North values for later use in transforming Reference Point coordinates
236 0 : CosBldgRelNorth = std::cos(-state.dataHeatBal->BuildingAzimuth * Constant::DegToRad);
237 0 : SinBldgRelNorth = std::sin(-state.dataHeatBal->BuildingAzimuth * Constant::DegToRad);
238 :
239 : // Loop through the Daylighting:Controls objects that use DElight checking for a host Zone
240 0 : for (auto &znDayl : state.dataDayltg->daylightControl) {
241 0 : if (znDayl.DaylightMethod == Dayltg::DaylightingMethod::DElight) {
242 :
243 : // Register Error if 0 DElight RefPts have been input for valid DElight object
244 0 : if (znDayl.TotalDaylRefPoints == 0) {
245 0 : ShowSevereError(state, format("No Reference Points input for daylighting zone using DElight ={}", znDayl.Name));
246 0 : ErrorsFound = true;
247 : }
248 :
249 : // Register Warning if more than 100 DElight RefPts have been input for valid DElight object
250 0 : if (znDayl.TotalDaylRefPoints > 100) {
251 : // Restrict to 100 Ref Pt maximum
252 0 : znDayl.TotalDaylRefPoints = 100;
253 0 : ShowWarningError(state, format("Maximum of 100 Reference Points exceeded for daylighting zone using DElight ={}", znDayl.Name));
254 0 : ShowWarningError(state, " Only first 100 Reference Points included in DElight analysis");
255 : }
256 :
257 : // Should already be allocated
258 0 : assert((int)znDayl.refPts.size() == znDayl.TotalDaylRefPoints);
259 0 : for (auto &refPt : znDayl.refPts) {
260 0 : refPt.absCoords = {0.0, 0.0, 0.0};
261 0 : refPt.lums[(int)Lum::Illum] = 0.0;
262 0 : refPt.glareIndex = 0.0;
263 : }
264 :
265 : // RJH 2008-03-07: Allocate and Init DaylIllumAtRefPt array for this DElight zone
266 : // znDayl.DaylIllumAtRefPt.allocate(znDayl.TotalDaylRefPoints);
267 : // znDayl.DaylIllumAtRefPt = 0.0;
268 : // The following not used in DElight but allocated for convenience
269 : // znDayl.GlareIndexAtRefPt.allocate(znDayl.TotalDaylRefPoints);
270 : // znDayl.GlareIndexAtRefPt = 0.0;
271 :
272 : // Increment counter of Thermal Zones with valid hosted DElight object
273 0 : ++iNumDElightZones;
274 : }
275 : } // traverse ZoneDaylight array
276 :
277 : // Zone Data Section
278 0 : print(delightInFile, Format_903, iNumDElightZones);
279 :
280 : // Loop through the Daylighting:DElight objects searching for a match to the current Zone
281 :
282 0 : for (auto &znDayl : state.dataDayltg->daylightControl) {
283 0 : if (znDayl.DaylightMethod == Dayltg::DaylightingMethod::DElight) {
284 0 : int const izone = Util::FindItemInList(znDayl.ZoneName, state.dataHeatBal->Zone);
285 0 : if (izone != 0) {
286 :
287 0 : rLightLevel = GetDesignLightingLevelForZone(state, izone);
288 0 : CheckLightsReplaceableMinMaxForZone(state, izone);
289 0 : auto &zn(state.dataHeatBal->Zone(izone));
290 :
291 : // Write this Zone to the DElight input file
292 : // Remove any blanks from the Zone Name for ease of input to DElight
293 0 : cNameWOBlanks = ReplaceBlanksWithUnderscores(zn.Name);
294 0 : print(delightInFile,
295 : Format_904,
296 : cNameWOBlanks,
297 0 : zn.OriginX * M2FT,
298 0 : zn.OriginY * M2FT,
299 0 : zn.OriginZ * M2FT,
300 0 : zn.RelNorth,
301 0 : zn.Multiplier * zn.ListMultiplier,
302 0 : zn.FloorArea * M22FT2,
303 0 : zn.Volume * M32FT3,
304 0 : rLightLevel / (zn.FloorArea * M22FT2 + 0.00001),
305 0 : znDayl.MinPowerFraction,
306 0 : znDayl.MinLightFraction,
307 0 : znDayl.LightControlSteps,
308 0 : znDayl.LightControlProbability,
309 0 : znDayl.DElightGriddingResolution * M22FT2);
310 :
311 : // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
312 0 : CosZoneRelNorth = std::cos(-zn.RelNorth * Constant::DegToRad);
313 0 : SinZoneRelNorth = std::sin(-zn.RelNorth * Constant::DegToRad);
314 :
315 : // Zone Lighting Schedule Data Section
316 : // NOTE: Schedules are not required since hourly values are retrieved from EnergyPlus as needed
317 0 : print(delightInFile, Format_905);
318 :
319 : // Zone Surface Data Section
320 : // Count the number of opaque surfaces bounding the current zone
321 0 : iNumOpaqueSurfs = 0;
322 0 : for (int spaceNum : zn.spaceIndexes) {
323 0 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
324 0 : for (int isurf = thisSpace.HTSurfaceFirst; isurf <= thisSpace.HTSurfaceLast; ++isurf) {
325 0 : auto const &surf(state.dataSurface->Surface(isurf));
326 0 : if (surf.Class == SurfaceClass::Wall) ++iNumOpaqueSurfs;
327 0 : if (surf.Class == SurfaceClass::Roof) ++iNumOpaqueSurfs;
328 0 : if (surf.Class == SurfaceClass::Floor) ++iNumOpaqueSurfs;
329 : }
330 : } // Zone Opaque Surface loop
331 :
332 0 : print(delightInFile, Format_906, iNumOpaqueSurfs);
333 :
334 : // Write each opaque bounding Surface to the DElight input file
335 0 : for (int spaceNum : zn.spaceIndexes) {
336 0 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
337 0 : int const iSurfaceFirst = thisSpace.HTSurfaceFirst;
338 0 : int const iSurfaceLast = thisSpace.HTSurfaceLast;
339 0 : for (int isurf = iSurfaceFirst; isurf <= iSurfaceLast; ++isurf) {
340 :
341 0 : auto &surf(state.dataSurface->Surface(isurf));
342 :
343 : // Only process "opaque bounding" surface types
344 0 : if ((surf.Class == SurfaceClass::Wall) || (surf.Class == SurfaceClass::Roof) || (surf.Class == SurfaceClass::Floor)) {
345 :
346 : // Get the Construction index for this Surface
347 0 : iconstruct = surf.Construction;
348 :
349 : // Is this Surface exposed to the exterior?
350 0 : if (surf.ExtSolar) {
351 : // Get the index for the outside (i.e., 1st) Material Layer for this Construction
352 0 : iMatlLayer = state.dataConstruction->Construct(iconstruct).LayerPoint(1);
353 : // Get the outside visible reflectance of this material layer
354 : // (since Construct(iconstruct)%ReflectVisDiffFront always appears to == 0.0)
355 0 : rExtVisRefl = 1.0 - state.dataMaterial->materials(iMatlLayer)->AbsorpVisible;
356 : } else {
357 0 : rExtVisRefl = 0.0;
358 : }
359 :
360 : // Remove any blanks from the Surface Name for ease of input to DElight
361 0 : cNameWOBlanks = ReplaceBlanksWithUnderscores(surf.Name);
362 0 : print(delightInFile,
363 : Format_907,
364 : cNameWOBlanks,
365 0 : surf.Azimuth,
366 0 : surf.Tilt,
367 0 : state.dataConstruction->Construct(iconstruct).ReflectVisDiffBack,
368 : rExtVisRefl,
369 0 : surf.Sides);
370 :
371 : // Write out the vertex coordinates for each vertex
372 0 : for (int ivert = 1; ivert <= surf.Sides; ++ivert) {
373 0 : print(delightInFile,
374 : Format_908,
375 0 : surf.Vertex(ivert).x * M2FT,
376 0 : surf.Vertex(ivert).y * M2FT,
377 0 : surf.Vertex(ivert).z * M2FT);
378 : }
379 :
380 : // Count each Window hosted by the current opaque bounding Surface
381 0 : iNumWindows = 0;
382 0 : for (int iwndo = iSurfaceFirst; iwndo <= iSurfaceLast; ++iwndo) {
383 0 : if (state.dataSurface->Surface(iwndo).Class == SurfaceClass::Window) {
384 0 : auto &wndo(state.dataSurface->Surface(iwndo));
385 0 : if (wndo.BaseSurfName == surf.Name) {
386 :
387 : // Error if window has multiplier > 1 since this causes incorrect illuminance calc
388 0 : if (wndo.Multiplier > 1.0) {
389 0 : ShowSevereError(
390 : state,
391 0 : format(
392 : "Multiplier > 1.0 for window {} not allowed since it is in a zone with DElight daylighting.",
393 0 : wndo.Name));
394 0 : ErrorsFound = true;
395 : }
396 :
397 : // Error if window has a shading device (blind/shade/screen) since
398 : // DElight cannot perform dynamic shading device deployment
399 0 : if (wndo.HasShadeControl) {
400 0 : ShowSevereError(state,
401 0 : format("Shading Device on window {} dynamic control is not supported in a zone with "
402 : "DElight daylighting.",
403 0 : wndo.Name));
404 0 : ErrorsFound = true;
405 : }
406 :
407 : // Loop through all Doppelganger Surface Names to ignore these Windows
408 0 : lWndoIsDoppelganger = false;
409 0 : for (auto const &cfs : state.dataDayltg->DElightComplexFene) {
410 :
411 : // Is the current Window Surface a Doppelganger?
412 0 : if (wndo.Name == cfs.wndwName) {
413 : // Ignore this Doppelganger Window
414 0 : lWndoIsDoppelganger = true;
415 : }
416 :
417 : } // CFS object loop A
418 :
419 0 : if (!lWndoIsDoppelganger) {
420 0 : ++iNumWindows;
421 : }
422 :
423 : } // Surface hosts Window test
424 : } // Window test
425 : } // Window loop
426 :
427 0 : print(delightInFile, Format_909, iNumWindows);
428 :
429 : // If the current opaque bounding Surface hosts Windows,
430 : // then write each hosted Window to the DElight input file
431 : // and track the Window Construction type for later writing
432 0 : if (iNumWindows > 0) {
433 0 : for (int iwndo2 = iSurfaceFirst; iwndo2 <= iSurfaceLast; ++iwndo2) {
434 0 : if (state.dataSurface->Surface(iwndo2).Class == SurfaceClass::Window) {
435 :
436 0 : auto &wndo2(state.dataSurface->Surface(iwndo2));
437 :
438 0 : if (wndo2.BaseSurfName == surf.Name) {
439 :
440 : // Loop through all Doppelganger Surface Names to ignore these Windows
441 0 : lWndoIsDoppelganger = false;
442 :
443 0 : for (auto const &cfs : state.dataDayltg->DElightComplexFene) {
444 :
445 : // Is the current Window Surface a Doppelganger?
446 0 : if (wndo2.Name == cfs.wndwName) {
447 : // Ignore this Doppelganger Window
448 0 : lWndoIsDoppelganger = true;
449 : }
450 :
451 : } // CFS object loop A
452 :
453 0 : if (!lWndoIsDoppelganger) {
454 :
455 : // Track unique window construction types here for later writing to
456 : // the library section of DElight input file
457 :
458 : // Get the Construction index for this Window Surface
459 0 : iconstruct = wndo2.Construction;
460 :
461 : // Has the current Construction index been encountered before?
462 0 : lWndoConstFound = false;
463 0 : for (int iconst = 1; iconst <= iNumWndoConsts; ++iconst) {
464 0 : if (iconstruct == iWndoConstIndexes(iconst)) lWndoConstFound = true;
465 : }
466 0 : if (!lWndoConstFound) {
467 0 : ++iNumWndoConsts;
468 0 : iWndoConstIndexes(iNumWndoConsts) = iconstruct;
469 : }
470 :
471 : // Write this Window to the DElight input file
472 : // Remove any blanks from the Window Surface Name for ease of input to DElight
473 0 : cNameWOBlanks = ReplaceBlanksWithUnderscores(wndo2.Name);
474 0 : print(delightInFile, Format_910, cNameWOBlanks, iconstruct + 10000, wndo2.Sides);
475 : // Use WndoConstIndex + 10000 as the Glass Type Name
476 : // to differentiate EPlus glass types within DElight
477 :
478 : // Write out the vertex coordinates for each vertex
479 0 : for (int ivert = 1; ivert <= wndo2.Sides; ++ivert) {
480 0 : print(delightInFile,
481 : Format_908,
482 0 : wndo2.Vertex(ivert).x * M2FT,
483 0 : wndo2.Vertex(ivert).y * M2FT,
484 0 : wndo2.Vertex(ivert).z * M2FT);
485 : }
486 : } //! lWndoIsDoppelganger
487 : } // Surface hosts Window2 test
488 : } // Window2 Class test
489 : } // Window2 loop
490 : } // Hosted Windows test
491 :
492 : // Write the number of CFS hosted by the current Opaque Bounding Surface
493 0 : iHostedCFS = 0;
494 :
495 : // Loop through the input CFS objects searching for a match to the current Opaque Bounding Surface
496 0 : for (auto const &cfs : state.dataDayltg->DElightComplexFene) {
497 :
498 : // Does the current Opaque Bounding Surface host the current CFS object?
499 0 : if (surf.Name == cfs.surfName) {
500 : // Count this hosted CFS
501 0 : ++iHostedCFS;
502 : }
503 : } // CFS object loop 1
504 :
505 0 : print(delightInFile, Format_911, iHostedCFS);
506 :
507 : // Now write each of the hosted CFS data
508 : // Loop through the input CFS objects searching for a match to the current Opaque Bounding Surface
509 0 : for (auto const &cfs : state.dataDayltg->DElightComplexFene) {
510 :
511 : // Does the current Opaque Bounding Surface host the current CFS object?
512 0 : if (surf.Name == cfs.surfName) {
513 :
514 : // Get the Doppelganger surface for this CFS
515 0 : iDoppelganger = 0;
516 0 : for (int iwndo3 = iSurfaceFirst; iwndo3 <= iSurfaceLast; ++iwndo3) {
517 :
518 0 : auto const &wndo3(state.dataSurface->Surface(iwndo3));
519 :
520 0 : if (wndo3.Class == SurfaceClass::Window) {
521 :
522 : // Is the current Window Surface the Doppelganger for the current CFS?
523 0 : if (wndo3.Name == cfs.wndwName) {
524 : // Store the window surface index for future reference
525 0 : iDoppelganger = iwndo3;
526 : }
527 : }
528 : }
529 :
530 : // Make sure that a valid Doppelganger surface exists
531 0 : if (iDoppelganger > 0) {
532 :
533 : // Write the data for this hosted CFS
534 0 : auto &doppelgangerSurf(state.dataSurface->Surface(iDoppelganger));
535 :
536 : // Remove any blanks from the CFS Name for ease of input to DElight
537 0 : cNameWOBlanks = ReplaceBlanksWithUnderscores(cfs.Name);
538 0 : print(
539 0 : delightInFile, Format_915, cNameWOBlanks, cfs.ComplexFeneType, cfs.feneRota, doppelgangerSurf.Sides);
540 :
541 : // Write out the vertex coordinates for each vertex
542 0 : for (int ivert = 1; ivert <= doppelgangerSurf.Sides; ++ivert) {
543 0 : print(delightInFile,
544 : Format_908,
545 0 : doppelgangerSurf.Vertex(ivert).x * M2FT,
546 0 : doppelgangerSurf.Vertex(ivert).y * M2FT,
547 0 : doppelgangerSurf.Vertex(ivert).z * M2FT);
548 : }
549 : }
550 : // Register Error if there is no valid Doppelganger for current Complex Fenestration
551 0 : if (iDoppelganger == 0) {
552 0 : ShowSevereError(state,
553 0 : format("No Doppelganger Window Surface found for Complex Fenestration ={}", cfs.Name));
554 0 : ErrorsFound = true;
555 : }
556 : } // The current Opaque Bounding Surface hosts the current CFS object?
557 : } // CFS object loop 2
558 : } // Opaque Bounding Surface test
559 : }
560 : } // Zone Surface loop
561 :
562 : // Write ZONE REFERENCE POINTS
563 0 : print(delightInFile, Format_912, znDayl.TotalDaylRefPoints);
564 :
565 : // Loop through the Daylighting:DElight:Reference Point objects checking for the current DElight Zone host
566 0 : for (auto &refPt : state.dataDayltg->DaylRefPt) {
567 :
568 : // Is this RefPt hosted by current DElight Zone?
569 0 : if (izone == refPt.ZoneNum) {
570 0 : auto &thisZone = state.dataHeatBal->Zone(izone);
571 :
572 : // Limit to maximum of 100 RefPts
573 0 : if (znDayl.TotalDaylRefPoints <= 100) {
574 :
575 0 : if (state.dataSurface->DaylRefWorldCoordSystem) {
576 0 : RefPt_WCS_Coord = refPt.coords;
577 : } else {
578 : // Transform reference point coordinates into building coordinate system
579 0 : Xb = refPt.coords.x * CosZoneRelNorth - refPt.coords.y * SinZoneRelNorth + thisZone.OriginX;
580 0 : Yb = refPt.coords.x * SinZoneRelNorth + refPt.coords.y * CosZoneRelNorth + thisZone.OriginY;
581 : // Transform into World Coordinate System
582 0 : RefPt_WCS_Coord.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
583 0 : RefPt_WCS_Coord.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
584 0 : RefPt_WCS_Coord.z = refPt.coords.z + thisZone.OriginZ;
585 0 : if (ldoTransform) { // Geometry transform
586 0 : Xo = RefPt_WCS_Coord.x; // world coordinates.... shifted by relative north angle...
587 0 : Yo = RefPt_WCS_Coord.y;
588 : // next derotate the building
589 0 : XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
590 0 : YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
591 : // translate
592 0 : Xtrans = XnoRot * std::sqrt(rnewAspectRatio / roldAspectRatio);
593 0 : Ytrans = YnoRot * std::sqrt(roldAspectRatio / rnewAspectRatio);
594 : // rerotate
595 0 : RefPt_WCS_Coord.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
596 :
597 0 : RefPt_WCS_Coord.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
598 : }
599 : }
600 0 : znDayl.refPts(refPt.indexToFracAndIllum).absCoords = RefPt_WCS_Coord;
601 :
602 : // Validate that Reference Point coordinates are within the host Zone
603 0 : if (RefPt_WCS_Coord.x < thisZone.MinimumX || RefPt_WCS_Coord.x > thisZone.MaximumX) {
604 0 : ShowSevereError(state,
605 0 : format("DElightInputGenerator:Reference point X Value outside Zone Min/Max X, Zone={}", zn.Name));
606 0 : ShowContinueError(state,
607 0 : format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
608 0 : thisZone.MinimumX,
609 : RefPt_WCS_Coord.x,
610 0 : thisZone.MaximumX));
611 0 : ErrorsFound = true;
612 : }
613 0 : if (RefPt_WCS_Coord.y < thisZone.MinimumY || RefPt_WCS_Coord.y > thisZone.MaximumY) {
614 0 : ShowSevereError(state,
615 0 : format("DElightInputGenerator:Reference point Y Value outside Zone Min/Max Y, Zone={}", zn.Name));
616 0 : ShowContinueError(state,
617 0 : format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
618 0 : thisZone.MinimumY,
619 : RefPt_WCS_Coord.y,
620 0 : thisZone.MaximumY));
621 0 : ErrorsFound = true;
622 : }
623 0 : if (RefPt_WCS_Coord.z < state.dataHeatBal->Zone(izone).MinimumZ || RefPt_WCS_Coord.z > thisZone.MaximumZ) {
624 0 : ShowSevereError(
625 : state,
626 0 : format("DElightInputGenerator:Reference point Z Value outside Zone Min/Max Z, Zone={}", thisZone.Name));
627 0 : ShowContinueError(state,
628 0 : format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
629 0 : thisZone.MinimumZ,
630 : RefPt_WCS_Coord.z,
631 0 : thisZone.MaximumZ));
632 0 : ErrorsFound = true;
633 : }
634 :
635 : // Write this RefPt to the DElight input file
636 :
637 : // Remove any blanks from the RefPt Name for ease of input to DElight
638 0 : cNameWOBlanks = ReplaceBlanksWithUnderscores(refPt.Name);
639 0 : if (refPt.indexToFracAndIllum != 0) {
640 0 : print(delightInFile,
641 : Format_913,
642 : cNameWOBlanks,
643 0 : RefPt_WCS_Coord.x * M2FT,
644 0 : RefPt_WCS_Coord.y * M2FT,
645 0 : RefPt_WCS_Coord.z * M2FT,
646 0 : znDayl.refPts(refPt.indexToFracAndIllum).fracZoneDaylit,
647 0 : znDayl.refPts(refPt.indexToFracAndIllum).illumSetPoint * LUX2FC,
648 0 : znDayl.LightControlType);
649 : // RJH 2008-03-07: Set up DaylIllumAtRefPt for output for this DElight zone RefPt
650 0 : SetupOutputVariable(state,
651 : "Daylighting Reference Point Illuminance",
652 : Constant::Units::lux,
653 0 : znDayl.refPts(refPt.indexToFracAndIllum).lums[(int)Lum::Illum],
654 : OutputProcessor::TimeStepType::Zone,
655 : OutputProcessor::StoreType::Average,
656 0 : refPt.Name);
657 : } else {
658 0 : print(delightInFile,
659 : Format_913,
660 : cNameWOBlanks,
661 0 : RefPt_WCS_Coord.x * M2FT,
662 0 : RefPt_WCS_Coord.y * M2FT,
663 0 : RefPt_WCS_Coord.z * M2FT,
664 0 : 0.0,
665 0 : 0.0 * LUX2FC,
666 0 : znDayl.LightControlType); // should never happen but just in case send zero fraction and illuminance
667 : }
668 : } // Max 100 RefPt test
669 : } // RefPt in current DElight Zone test
670 : } // traverse reference points loop
671 : } // if in a zone
672 : } // Zone hosts DElight object test
673 : } // traverse ZoneDayLight object loop
674 :
675 : // Write BUILDING SHADES
676 0 : print(delightInFile, Format_914);
677 :
678 : // Write LIBRARY DATA
679 0 : print(delightInFile, Format_920, iNumWndoConsts);
680 :
681 : // Write GLASS TYPES
682 : // VisBeamCoeffs are processed in EPlus by POLYF() function
683 : // Use WndoConstIndex + 10000 as the Glass Type Name to differentiate EPlus glass types within DElight
684 0 : for (int iconst = 1; iconst <= iNumWndoConsts; ++iconst) {
685 0 : print(delightInFile,
686 : Format_921,
687 0 : iWndoConstIndexes(iconst) + 10000,
688 0 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransDiffVis,
689 0 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).ReflectVisDiffBack,
690 0 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransVisBeamCoef[0],
691 0 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransVisBeamCoef[1],
692 0 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransVisBeamCoef[2],
693 0 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransVisBeamCoef[3],
694 0 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransVisBeamCoef[4],
695 0 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransVisBeamCoef[5]);
696 :
697 : } // Glass Type loop
698 :
699 0 : if (ErrorsFound) ShowFatalError(state, "Problems with Daylighting:DElight input, see previous error messages");
700 0 : }
701 :
702 0 : void GenerateDElightDaylightCoefficients(Real64 &dLatitude, int &iErrorFlag)
703 : {
704 :
705 : // SUBROUTINE INFORMATION:
706 : // AUTHOR Linda Lawrie
707 : // DATE WRITTEN September 2012
708 : // MODIFIED na
709 : // RE-ENGINEERED na
710 :
711 : // PURPOSE OF THIS SUBROUTINE:
712 : // The purpose of this subroutine is to provide an envelop to the DElightDaylightCoefficients routine
713 :
714 0 : delightdaylightcoefficients(dLatitude, &iErrorFlag);
715 0 : }
716 :
717 1 : void GetInputDElightComplexFenestration(EnergyPlusData &state, bool &ErrorsFound)
718 : {
719 : // Perform GetInput function for the Daylighting:DELight:ComplexFenestration object
720 : // Glazer - July 2016
721 :
722 : int NumAlpha;
723 : int NumNumber;
724 : int IOStat;
725 1 : int CFSNum = 0;
726 :
727 1 : constexpr std::string_view cCurrentModuleObject("Daylighting:DELight:ComplexFenestration");
728 :
729 1 : int TotDElightCFS = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
730 1 : state.dataDayltg->DElightComplexFene.allocate(TotDElightCFS);
731 2 : for (auto &cfs : state.dataDayltg->DElightComplexFene) {
732 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
733 : cCurrentModuleObject,
734 : ++CFSNum,
735 1 : state.dataIPShortCut->cAlphaArgs,
736 : NumAlpha,
737 1 : state.dataIPShortCut->rNumericArgs,
738 : NumNumber,
739 : IOStat,
740 1 : state.dataIPShortCut->lNumericFieldBlanks,
741 1 : state.dataIPShortCut->lAlphaFieldBlanks,
742 1 : state.dataIPShortCut->cAlphaFieldNames,
743 1 : state.dataIPShortCut->cNumericFieldNames);
744 1 : cfs.Name = state.dataIPShortCut->cAlphaArgs(1);
745 1 : cfs.ComplexFeneType = state.dataIPShortCut->cAlphaArgs(2);
746 1 : cfs.surfName = state.dataIPShortCut->cAlphaArgs(3);
747 1 : if (Util::FindItemInList(cfs.surfName, state.dataSurface->Surface) == 0) {
748 0 : ShowSevereError(state,
749 0 : format("{}{}",
750 : cCurrentModuleObject,
751 0 : ": " + cfs.Name + ", invalid " + state.dataIPShortCut->cAlphaFieldNames(3) + "=\"" + cfs.surfName + "\"."));
752 0 : ErrorsFound = true;
753 : }
754 1 : cfs.wndwName = state.dataIPShortCut->cAlphaArgs(4);
755 1 : if (Util::FindItemInList(cfs.surfName, state.dataSurface->Surface) == 0) {
756 0 : ShowSevereError(state,
757 0 : format("{}{}",
758 : cCurrentModuleObject,
759 0 : ": " + cfs.Name + ", invalid " + state.dataIPShortCut->cAlphaFieldNames(4) + "=\"" + cfs.wndwName + "\"."));
760 0 : ErrorsFound = true;
761 : }
762 1 : cfs.feneRota = state.dataIPShortCut->rNumericArgs(1);
763 1 : if (cfs.feneRota < 0. || cfs.feneRota > 360.) {
764 0 : ShowSevereError(state,
765 0 : format("{}{}",
766 : cCurrentModuleObject,
767 0 : ": " + cfs.Name + ", invalid " + state.dataIPShortCut->cNumericFieldNames(1) + " outside of range 0 to 360."));
768 0 : ErrorsFound = true;
769 : }
770 : }
771 1 : }
772 :
773 0 : void CheckForGeometricTransform(EnergyPlusData &state, bool &doTransform, Real64 &OldAspectRatio, Real64 &NewAspectRatio)
774 : {
775 :
776 : // SUBROUTINE INFORMATION:
777 : // AUTHOR Linda Lawrie
778 : // DATE WRITTEN February 2009
779 : // MODIFIED na
780 : // RE-ENGINEERED na
781 :
782 : // PURPOSE OF THIS SUBROUTINE:
783 : // check for geometrytransform in the daylighting access for reference points
784 :
785 : // METHODOLOGY EMPLOYED:
786 : // once reference points have been converted to WCS,
787 : // change them to reflect a different aspect
788 : // ratio for the entire building based on user input.
789 :
790 : // SUBROUTINE PARAMETER DEFINITIONS:
791 0 : constexpr std::string_view CurrentModuleObject("GeometryTransform");
792 :
793 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
794 0 : Array1D_string cAlphas(1);
795 0 : Array1D<Real64> rNumerics(2);
796 :
797 : // begin execution
798 : // get user input...
799 0 : doTransform = false;
800 0 : OldAspectRatio = 1.0;
801 0 : NewAspectRatio = 1.0;
802 :
803 0 : if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject) == 1) {
804 : int NAlphas;
805 : int NNum;
806 : int IOStat;
807 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
808 : CurrentModuleObject,
809 : 1,
810 : cAlphas,
811 : NAlphas,
812 : rNumerics,
813 : NNum,
814 : IOStat,
815 0 : state.dataIPShortCut->lNumericFieldBlanks,
816 0 : state.dataIPShortCut->lAlphaFieldBlanks,
817 0 : state.dataIPShortCut->cAlphaFieldNames,
818 0 : state.dataIPShortCut->cNumericFieldNames);
819 0 : OldAspectRatio = rNumerics(1);
820 0 : NewAspectRatio = rNumerics(2);
821 0 : if (cAlphas(1) != "XY") {
822 0 : ShowWarningError(
823 : state,
824 0 : format("{}{}", CurrentModuleObject, ": invalid " + state.dataIPShortCut->cAlphaFieldNames(1) + "=" + cAlphas(1) + "...ignored."));
825 : }
826 0 : doTransform = true;
827 0 : state.dataSurface->AspectTransform = true;
828 : }
829 0 : if (state.dataSurface->WorldCoordSystem) {
830 0 : doTransform = false;
831 0 : state.dataSurface->AspectTransform = false;
832 : }
833 0 : }
834 :
835 0 : std::string ReplaceBlanksWithUnderscores(std::string const &InputString) // Input String
836 : {
837 :
838 : // FUNCTION INFORMATION:
839 : // AUTHOR Robert J. Hitchcock
840 : // DATE WRITTEN August 2003
841 :
842 : // PURPOSE OF THIS SUBROUTINE:
843 : // This function returns a representation of the InputString with blanks replaced with underscores.
844 :
845 : // METHODOLOGY EMPLOYED:
846 : // Uses the std::replace function from the C++ library
847 :
848 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
849 :
850 0 : std::string ResultString(trimmed(InputString));
851 0 : std::replace(ResultString.begin(), ResultString.end(), ' ', '_');
852 0 : return ResultString;
853 : }
854 :
855 0 : void DElightElecLtgCtrl(int iNameLength,
856 : std::string const &cZoneName,
857 : Real64 dBldgLat,
858 : Real64 dHISKF,
859 : Real64 dHISUNF,
860 : Real64 dCloudFraction,
861 : Real64 dSOLCOSX,
862 : Real64 dSOLCOSY,
863 : Real64 dSOLCOSZ,
864 : Real64 &pdPowerReducFac,
865 : int piErrorFlag)
866 : {
867 0 : std::vector<char> zoneNameArr(getCharArrayFromString(cZoneName));
868 0 : delightelecltgctrl(
869 0 : iNameLength, &zoneNameArr[0], dBldgLat, dHISKF, dHISUNF, dCloudFraction, dSOLCOSX, dSOLCOSY, dSOLCOSZ, &pdPowerReducFac, &piErrorFlag);
870 0 : }
871 :
872 0 : std::vector<char> getCharArrayFromString(std::string const &originalString)
873 : {
874 0 : std::vector<char> returnVal(originalString.begin(), originalString.end());
875 0 : returnVal.push_back('\0'); // get null terminated string of chars
876 0 : return returnVal;
877 0 : }
878 :
879 0 : std::string getStringFromCharArray(std::vector<char> const &originalCharArray)
880 : {
881 0 : return std::string(originalCharArray.begin(), originalCharArray.end());
882 : }
883 :
884 : } // namespace DElightManagerF
885 :
886 : } // namespace EnergyPlus
|