Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, 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 3 : 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 3 : Vector3<Real64> RefPt_WCS_Coord;
153 3 : Array1D_int iWndoConstIndexes(100);
154 : bool lWndoConstFound; // Flag for non-unique window const index
155 3 : 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 3 : ErrorsFound = false;
206 :
207 3 : GetInputDElightComplexFenestration(state, ErrorsFound);
208 :
209 3 : CheckForGeometricTransform(state, ldoTransform, roldAspectRatio, rnewAspectRatio);
210 :
211 : // Init the counter for Thermal Zones with hosted Daylighting:DElight objects
212 3 : iNumDElightZones = 0;
213 :
214 : // Init the counter for Window Construction types for writing to Library Data section of DElight input file
215 3 : int iNumWndoConsts = 0;
216 :
217 : // Open a file for writing DElight input from EnergyPlus data
218 6 : auto delightInFile = state.files.delightIn.open(state, "DElightInputGenerator", state.files.outputControl.delightin); // (THIS_AUTO_OK)
219 :
220 : // Start of DElight input file
221 3 : 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 3 : cNameWOBlanks = ReplaceBlanksWithUnderscores(state.dataHeatBal->BuildingName);
226 3 : print(delightInFile,
227 : Format_902,
228 : cNameWOBlanks,
229 3 : state.dataEnvrn->Latitude,
230 3 : state.dataEnvrn->Longitude,
231 3 : state.dataEnvrn->Elevation * M2FT,
232 3 : state.dataHeatBal->BuildingAzimuth,
233 3 : state.dataEnvrn->TimeZoneNumber);
234 :
235 : // Calc cos and sin of Building Relative North values for later use in transforming Reference Point coordinates
236 3 : CosBldgRelNorth = std::cos(-state.dataHeatBal->BuildingAzimuth * Constant::DegToRadians);
237 3 : SinBldgRelNorth = std::sin(-state.dataHeatBal->BuildingAzimuth * Constant::DegToRadians);
238 :
239 : // Loop through the Daylighting:Controls objects that use DElight checking for a host Zone
240 9 : for (auto &znDayl : state.dataDayltg->daylightControl) {
241 6 : if (znDayl.DaylightMethod == Dayltg::DaylightingMethod::DElight) {
242 :
243 : // Register Error if 0 DElight RefPts have been input for valid DElight object
244 3 : 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 3 : 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 3 : assert((int)znDayl.refPts.size() == znDayl.TotalDaylRefPoints);
259 9 : for (auto &refPt : znDayl.refPts) {
260 6 : refPt.absCoords = {0.0, 0.0, 0.0};
261 6 : refPt.lums[(int)Lum::Illum] = 0.0;
262 6 : 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 3 : ++iNumDElightZones;
274 : }
275 : } // traverse ZoneDaylight array
276 :
277 : // Zone Data Section
278 3 : print(delightInFile, Format_903, iNumDElightZones);
279 :
280 : // Loop through the Daylighting:DElight objects searching for a match to the current Zone
281 :
282 9 : for (auto &znDayl : state.dataDayltg->daylightControl) {
283 6 : if (znDayl.DaylightMethod == Dayltg::DaylightingMethod::DElight) {
284 3 : int const izone = Util::FindItemInList(znDayl.ZoneName, state.dataHeatBal->Zone);
285 3 : if (izone != 0) {
286 :
287 3 : rLightLevel = GetDesignLightingLevelForZone(state, izone);
288 3 : CheckLightsReplaceableMinMaxForZone(state, izone);
289 3 : 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 3 : cNameWOBlanks = ReplaceBlanksWithUnderscores(zn.Name);
294 3 : print(delightInFile,
295 : Format_904,
296 : cNameWOBlanks,
297 0 : zn.OriginX * M2FT,
298 0 : zn.OriginY * M2FT,
299 0 : zn.OriginZ * M2FT,
300 3 : 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 3 : znDayl.MinPowerFraction,
306 3 : znDayl.MinLightFraction,
307 3 : znDayl.LightControlSteps,
308 3 : znDayl.LightControlProbability,
309 3 : znDayl.DElightGriddingResolution * M22FT2);
310 :
311 : // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
312 3 : CosZoneRelNorth = std::cos(-zn.RelNorth * Constant::DegToRadians);
313 3 : SinZoneRelNorth = std::sin(-zn.RelNorth * Constant::DegToRadians);
314 :
315 : // Zone Lighting Schedule Data Section
316 : // NOTE: Schedules are not required since hourly values are retrieved from EnergyPlus as needed
317 3 : print(delightInFile, Format_905);
318 :
319 : // Zone Surface Data Section
320 : // Count the number of opaque surfaces bounding the current zone
321 3 : iNumOpaqueSurfs = 0;
322 6 : for (int spaceNum : zn.spaceIndexes) {
323 3 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
324 24 : for (int isurf = thisSpace.HTSurfaceFirst; isurf <= thisSpace.HTSurfaceLast; ++isurf) {
325 21 : auto const &surf(state.dataSurface->Surface(isurf));
326 21 : if (surf.Class == SurfaceClass::Wall) ++iNumOpaqueSurfs;
327 21 : if (surf.Class == SurfaceClass::Roof) ++iNumOpaqueSurfs;
328 21 : if (surf.Class == SurfaceClass::Floor) ++iNumOpaqueSurfs;
329 : }
330 3 : } // Zone Opaque Surface loop
331 :
332 3 : print(delightInFile, Format_906, iNumOpaqueSurfs);
333 :
334 : // Write each opaque bounding Surface to the DElight input file
335 6 : for (int spaceNum : zn.spaceIndexes) {
336 3 : auto &thisSpace = state.dataHeatBal->space(spaceNum);
337 3 : int const iSurfaceFirst = thisSpace.HTSurfaceFirst;
338 3 : int const iSurfaceLast = thisSpace.HTSurfaceLast;
339 24 : for (int isurf = iSurfaceFirst; isurf <= iSurfaceLast; ++isurf) {
340 :
341 21 : auto &surf(state.dataSurface->Surface(isurf));
342 :
343 : // Only process "opaque bounding" surface types
344 21 : if ((surf.Class == SurfaceClass::Wall) || (surf.Class == SurfaceClass::Roof) || (surf.Class == SurfaceClass::Floor)) {
345 :
346 : // Get the Construction index for this Surface
347 18 : iconstruct = surf.Construction;
348 :
349 : // Is this Surface exposed to the exterior?
350 18 : if (surf.ExtSolar) {
351 : // Get the index for the outside (i.e., 1st) Material Layer for this Construction
352 12 : 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 : auto const *thisMaterial =
356 12 : dynamic_cast<const Material::MaterialChild *>(state.dataMaterial->Material(iMatlLayer));
357 12 : assert(thisMaterial != nullptr);
358 12 : rExtVisRefl = 1.0 - thisMaterial->AbsorpVisible;
359 : } else {
360 6 : rExtVisRefl = 0.0;
361 : }
362 :
363 : // Remove any blanks from the Surface Name for ease of input to DElight
364 18 : cNameWOBlanks = ReplaceBlanksWithUnderscores(surf.Name);
365 18 : print(delightInFile,
366 : Format_907,
367 : cNameWOBlanks,
368 18 : surf.Azimuth,
369 18 : surf.Tilt,
370 18 : state.dataConstruction->Construct(iconstruct).ReflectVisDiffBack,
371 : rExtVisRefl,
372 18 : surf.Sides);
373 :
374 : // Write out the vertex coordinates for each vertex
375 90 : for (int ivert = 1; ivert <= surf.Sides; ++ivert) {
376 72 : print(delightInFile,
377 : Format_908,
378 72 : surf.Vertex(ivert).x * M2FT,
379 72 : surf.Vertex(ivert).y * M2FT,
380 144 : surf.Vertex(ivert).z * M2FT);
381 : }
382 :
383 : // Count each Window hosted by the current opaque bounding Surface
384 18 : iNumWindows = 0;
385 144 : for (int iwndo = iSurfaceFirst; iwndo <= iSurfaceLast; ++iwndo) {
386 126 : if (state.dataSurface->Surface(iwndo).Class == SurfaceClass::Window) {
387 18 : auto &wndo(state.dataSurface->Surface(iwndo));
388 18 : if (wndo.BaseSurfName == surf.Name) {
389 :
390 : // Error if window has multiplier > 1 since this causes incorrect illuminance calc
391 3 : if (wndo.Multiplier > 1.0) {
392 0 : ShowSevereError(
393 : state,
394 0 : format(
395 : "Multiplier > 1.0 for window {} not allowed since it is in a zone with DElight daylighting.",
396 0 : wndo.Name));
397 0 : ErrorsFound = true;
398 : }
399 :
400 : // Error if window has a shading device (blind/shade/screen) since
401 : // DElight cannot perform dynamic shading device deployment
402 3 : if (wndo.HasShadeControl) {
403 0 : ShowSevereError(state,
404 0 : format("Shading Device on window {} dynamic control is not supported in a zone with "
405 : "DElight daylighting.",
406 0 : wndo.Name));
407 0 : ErrorsFound = true;
408 : }
409 :
410 : // Loop through all Doppelganger Surface Names to ignore these Windows
411 3 : lWndoIsDoppelganger = false;
412 5 : for (auto const &cfs : state.dataDayltg->DElightComplexFene) {
413 :
414 : // Is the current Window Surface a Doppelganger?
415 2 : if (wndo.Name == cfs.wndwName) {
416 : // Ignore this Doppelganger Window
417 2 : lWndoIsDoppelganger = true;
418 : }
419 :
420 : } // CFS object loop A
421 :
422 3 : if (!lWndoIsDoppelganger) {
423 1 : ++iNumWindows;
424 : }
425 :
426 : } // Surface hosts Window test
427 : } // Window test
428 : } // Window loop
429 :
430 18 : print(delightInFile, Format_909, iNumWindows);
431 :
432 : // If the current opaque bounding Surface hosts Windows,
433 : // then write each hosted Window to the DElight input file
434 : // and track the Window Construction type for later writing
435 18 : if (iNumWindows > 0) {
436 8 : for (int iwndo2 = iSurfaceFirst; iwndo2 <= iSurfaceLast; ++iwndo2) {
437 7 : if (state.dataSurface->Surface(iwndo2).Class == SurfaceClass::Window) {
438 :
439 1 : auto &wndo2(state.dataSurface->Surface(iwndo2));
440 :
441 1 : if (wndo2.BaseSurfName == surf.Name) {
442 :
443 : // Loop through all Doppelganger Surface Names to ignore these Windows
444 1 : lWndoIsDoppelganger = false;
445 :
446 1 : for (auto const &cfs : state.dataDayltg->DElightComplexFene) {
447 :
448 : // Is the current Window Surface a Doppelganger?
449 0 : if (wndo2.Name == cfs.wndwName) {
450 : // Ignore this Doppelganger Window
451 0 : lWndoIsDoppelganger = true;
452 : }
453 :
454 : } // CFS object loop A
455 :
456 1 : if (!lWndoIsDoppelganger) {
457 :
458 : // Track unique window construction types here for later writing to
459 : // the library section of DElight input file
460 :
461 : // Get the Construction index for this Window Surface
462 1 : iconstruct = wndo2.Construction;
463 :
464 : // Has the current Construction index been encountered before?
465 1 : lWndoConstFound = false;
466 1 : for (int iconst = 1; iconst <= iNumWndoConsts; ++iconst) {
467 0 : if (iconstruct == iWndoConstIndexes(iconst)) lWndoConstFound = true;
468 : }
469 1 : if (!lWndoConstFound) {
470 1 : ++iNumWndoConsts;
471 1 : iWndoConstIndexes(iNumWndoConsts) = iconstruct;
472 : }
473 :
474 : // Write this Window to the DElight input file
475 : // Remove any blanks from the Window Surface Name for ease of input to DElight
476 1 : cNameWOBlanks = ReplaceBlanksWithUnderscores(wndo2.Name);
477 1 : print(delightInFile, Format_910, cNameWOBlanks, iconstruct + 10000, wndo2.Sides);
478 : // Use WndoConstIndex + 10000 as the Glass Type Name
479 : // to differentiate EPlus glass types within DElight
480 :
481 : // Write out the vertex coordinates for each vertex
482 5 : for (int ivert = 1; ivert <= wndo2.Sides; ++ivert) {
483 4 : print(delightInFile,
484 : Format_908,
485 4 : wndo2.Vertex(ivert).x * M2FT,
486 4 : wndo2.Vertex(ivert).y * M2FT,
487 8 : wndo2.Vertex(ivert).z * M2FT);
488 : }
489 : } //! lWndoIsDoppelganger
490 : } // Surface hosts Window2 test
491 : } // Window2 Class test
492 : } // Window2 loop
493 : } // Hosted Windows test
494 :
495 : // Write the number of CFS hosted by the current Opaque Bounding Surface
496 18 : iHostedCFS = 0;
497 :
498 : // Loop through the input CFS objects searching for a match to the current Opaque Bounding Surface
499 30 : for (auto const &cfs : state.dataDayltg->DElightComplexFene) {
500 :
501 : // Does the current Opaque Bounding Surface host the current CFS object?
502 12 : if (surf.Name == cfs.surfName) {
503 : // Count this hosted CFS
504 2 : ++iHostedCFS;
505 : }
506 : } // CFS object loop 1
507 :
508 18 : print(delightInFile, Format_911, iHostedCFS);
509 :
510 : // Now write each of the hosted CFS data
511 : // Loop through the input CFS objects searching for a match to the current Opaque Bounding Surface
512 30 : for (auto const &cfs : state.dataDayltg->DElightComplexFene) {
513 :
514 : // Does the current Opaque Bounding Surface host the current CFS object?
515 12 : if (surf.Name == cfs.surfName) {
516 :
517 : // Get the Doppelganger surface for this CFS
518 2 : iDoppelganger = 0;
519 16 : for (int iwndo3 = iSurfaceFirst; iwndo3 <= iSurfaceLast; ++iwndo3) {
520 :
521 14 : auto const &wndo3(state.dataSurface->Surface(iwndo3));
522 :
523 14 : if (wndo3.Class == SurfaceClass::Window) {
524 :
525 : // Is the current Window Surface the Doppelganger for the current CFS?
526 2 : if (wndo3.Name == cfs.wndwName) {
527 : // Store the window surface index for future reference
528 2 : iDoppelganger = iwndo3;
529 : }
530 : }
531 : }
532 :
533 : // Make sure that a valid Doppelganger surface exists
534 2 : if (iDoppelganger > 0) {
535 :
536 : // Write the data for this hosted CFS
537 2 : auto &doppelgangerSurf(state.dataSurface->Surface(iDoppelganger));
538 :
539 : // Remove any blanks from the CFS Name for ease of input to DElight
540 2 : cNameWOBlanks = ReplaceBlanksWithUnderscores(cfs.Name);
541 2 : print(
542 2 : delightInFile, Format_915, cNameWOBlanks, cfs.ComplexFeneType, cfs.feneRota, doppelgangerSurf.Sides);
543 :
544 : // Write out the vertex coordinates for each vertex
545 10 : for (int ivert = 1; ivert <= doppelgangerSurf.Sides; ++ivert) {
546 8 : print(delightInFile,
547 : Format_908,
548 8 : doppelgangerSurf.Vertex(ivert).x * M2FT,
549 8 : doppelgangerSurf.Vertex(ivert).y * M2FT,
550 16 : doppelgangerSurf.Vertex(ivert).z * M2FT);
551 : }
552 : }
553 : // Register Error if there is no valid Doppelganger for current Complex Fenestration
554 2 : if (iDoppelganger == 0) {
555 0 : ShowSevereError(state,
556 0 : format("No Doppelganger Window Surface found for Complex Fenestration ={}", cfs.Name));
557 0 : ErrorsFound = true;
558 : }
559 : } // The current Opaque Bounding Surface hosts the current CFS object?
560 : } // CFS object loop 2
561 : } // Opaque Bounding Surface test
562 : }
563 3 : } // Zone Surface loop
564 :
565 : // Write ZONE REFERENCE POINTS
566 3 : print(delightInFile, Format_912, znDayl.TotalDaylRefPoints);
567 :
568 : // Loop through the Daylighting:DElight:Reference Point objects checking for the current DElight Zone host
569 15 : for (auto &refPt : state.dataDayltg->DaylRefPt) {
570 :
571 : // Is this RefPt hosted by current DElight Zone?
572 12 : if (izone == refPt.ZoneNum) {
573 6 : auto &thisZone = state.dataHeatBal->Zone(izone);
574 :
575 : // Limit to maximum of 100 RefPts
576 6 : if (znDayl.TotalDaylRefPoints <= 100) {
577 :
578 6 : if (state.dataSurface->DaylRefWorldCoordSystem) {
579 0 : RefPt_WCS_Coord = refPt.coords;
580 : } else {
581 : // Transform reference point coordinates into building coordinate system
582 6 : Xb = refPt.coords.x * CosZoneRelNorth - refPt.coords.y * SinZoneRelNorth + thisZone.OriginX;
583 6 : Yb = refPt.coords.x * SinZoneRelNorth + refPt.coords.y * CosZoneRelNorth + thisZone.OriginY;
584 : // Transform into World Coordinate System
585 6 : RefPt_WCS_Coord.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
586 6 : RefPt_WCS_Coord.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
587 6 : RefPt_WCS_Coord.z = refPt.coords.z + thisZone.OriginZ;
588 6 : if (ldoTransform) { // Geometry transform
589 0 : Xo = RefPt_WCS_Coord.x; // world coordinates.... shifted by relative north angle...
590 0 : Yo = RefPt_WCS_Coord.y;
591 : // next derotate the building
592 0 : XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
593 0 : YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
594 : // translate
595 0 : Xtrans = XnoRot * std::sqrt(rnewAspectRatio / roldAspectRatio);
596 0 : Ytrans = YnoRot * std::sqrt(roldAspectRatio / rnewAspectRatio);
597 : // rerotate
598 0 : RefPt_WCS_Coord.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
599 :
600 0 : RefPt_WCS_Coord.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
601 : }
602 : }
603 6 : znDayl.refPts(refPt.indexToFracAndIllum).absCoords = RefPt_WCS_Coord;
604 :
605 : // Validate that Reference Point coordinates are within the host Zone
606 6 : if (RefPt_WCS_Coord.x < thisZone.MinimumX || RefPt_WCS_Coord.x > thisZone.MaximumX) {
607 0 : ShowSevereError(state,
608 0 : format("DElightInputGenerator:Reference point X Value outside Zone Min/Max X, Zone={}", zn.Name));
609 0 : ShowContinueError(state,
610 0 : format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
611 0 : thisZone.MinimumX,
612 : RefPt_WCS_Coord.x,
613 0 : thisZone.MaximumX));
614 0 : ErrorsFound = true;
615 : }
616 6 : if (RefPt_WCS_Coord.y < thisZone.MinimumY || RefPt_WCS_Coord.y > thisZone.MaximumY) {
617 0 : ShowSevereError(state,
618 0 : format("DElightInputGenerator:Reference point Y Value outside Zone Min/Max Y, Zone={}", zn.Name));
619 0 : ShowContinueError(state,
620 0 : format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
621 0 : thisZone.MinimumY,
622 : RefPt_WCS_Coord.y,
623 0 : thisZone.MaximumY));
624 0 : ErrorsFound = true;
625 : }
626 6 : if (RefPt_WCS_Coord.z < state.dataHeatBal->Zone(izone).MinimumZ || RefPt_WCS_Coord.z > thisZone.MaximumZ) {
627 0 : ShowSevereError(
628 : state,
629 0 : format("DElightInputGenerator:Reference point Z Value outside Zone Min/Max Z, Zone={}", thisZone.Name));
630 0 : ShowContinueError(state,
631 0 : format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
632 0 : thisZone.MinimumZ,
633 : RefPt_WCS_Coord.z,
634 0 : thisZone.MaximumZ));
635 0 : ErrorsFound = true;
636 : }
637 :
638 : // Write this RefPt to the DElight input file
639 :
640 : // Remove any blanks from the RefPt Name for ease of input to DElight
641 6 : cNameWOBlanks = ReplaceBlanksWithUnderscores(refPt.Name);
642 6 : if (refPt.indexToFracAndIllum != 0) {
643 6 : print(delightInFile,
644 : Format_913,
645 : cNameWOBlanks,
646 0 : RefPt_WCS_Coord.x * M2FT,
647 0 : RefPt_WCS_Coord.y * M2FT,
648 0 : RefPt_WCS_Coord.z * M2FT,
649 6 : znDayl.refPts(refPt.indexToFracAndIllum).fracZoneDaylit,
650 6 : znDayl.refPts(refPt.indexToFracAndIllum).illumSetPoint * LUX2FC,
651 6 : znDayl.LightControlType);
652 : // RJH 2008-03-07: Set up DaylIllumAtRefPt for output for this DElight zone RefPt
653 12 : SetupOutputVariable(state,
654 : "Daylighting Reference Point Illuminance",
655 : Constant::Units::lux,
656 6 : znDayl.refPts(refPt.indexToFracAndIllum).lums[(int)Lum::Illum],
657 : OutputProcessor::TimeStepType::Zone,
658 : OutputProcessor::StoreType::Average,
659 6 : refPt.Name);
660 : } else {
661 0 : print(delightInFile,
662 : Format_913,
663 : cNameWOBlanks,
664 0 : RefPt_WCS_Coord.x * M2FT,
665 0 : RefPt_WCS_Coord.y * M2FT,
666 0 : RefPt_WCS_Coord.z * M2FT,
667 0 : 0.0,
668 0 : 0.0 * LUX2FC,
669 0 : znDayl.LightControlType); // should never happen but just in case send zero fraction and illuminance
670 : }
671 : } // Max 100 RefPt test
672 : } // RefPt in current DElight Zone test
673 : } // traverse reference points loop
674 : } // if in a zone
675 : } // Zone hosts DElight object test
676 : } // traverse ZoneDayLight object loop
677 :
678 : // Write BUILDING SHADES
679 3 : print(delightInFile, Format_914);
680 :
681 : // Write LIBRARY DATA
682 3 : print(delightInFile, Format_920, iNumWndoConsts);
683 :
684 : // Write GLASS TYPES
685 : // VisBeamCoeffs are processed in EPlus by POLYF() function
686 : // Use WndoConstIndex + 10000 as the Glass Type Name to differentiate EPlus glass types within DElight
687 4 : for (int iconst = 1; iconst <= iNumWndoConsts; ++iconst) {
688 13 : print(delightInFile,
689 : Format_921,
690 1 : iWndoConstIndexes(iconst) + 10000,
691 1 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransDiffVis,
692 1 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).ReflectVisDiffBack,
693 1 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransVisBeamCoef(1),
694 1 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransVisBeamCoef(2),
695 1 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransVisBeamCoef(3),
696 1 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransVisBeamCoef(4),
697 1 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransVisBeamCoef(5),
698 1 : state.dataConstruction->Construct(iWndoConstIndexes(iconst)).TransVisBeamCoef(6));
699 :
700 : } // Glass Type loop
701 :
702 3 : if (ErrorsFound) ShowFatalError(state, "Problems with Daylighting:DElight input, see previous error messages");
703 3 : }
704 :
705 3 : void GenerateDElightDaylightCoefficients(Real64 &dLatitude, int &iErrorFlag)
706 : {
707 :
708 : // SUBROUTINE INFORMATION:
709 : // AUTHOR Linda Lawrie
710 : // DATE WRITTEN September 2012
711 : // MODIFIED na
712 : // RE-ENGINEERED na
713 :
714 : // PURPOSE OF THIS SUBROUTINE:
715 : // The purpose of this subroutine is to provide an envelop to the DElightDaylightCoefficients routine
716 :
717 3 : delightdaylightcoefficients(dLatitude, &iErrorFlag);
718 3 : }
719 :
720 3 : void GetInputDElightComplexFenestration(EnergyPlusData &state, bool &ErrorsFound)
721 : {
722 : // Perform GetInput function for the Daylighting:DELight:ComplexFenestration object
723 : // Glazer - July 2016
724 :
725 : int NumAlpha;
726 : int NumNumber;
727 : int IOStat;
728 3 : int CFSNum = 0;
729 :
730 3 : constexpr std::string_view cCurrentModuleObject("Daylighting:DELight:ComplexFenestration");
731 :
732 3 : int TotDElightCFS = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
733 3 : state.dataDayltg->DElightComplexFene.allocate(TotDElightCFS);
734 5 : for (auto &cfs : state.dataDayltg->DElightComplexFene) {
735 4 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
736 : cCurrentModuleObject,
737 : ++CFSNum,
738 2 : state.dataIPShortCut->cAlphaArgs,
739 : NumAlpha,
740 2 : state.dataIPShortCut->rNumericArgs,
741 : NumNumber,
742 : IOStat,
743 2 : state.dataIPShortCut->lNumericFieldBlanks,
744 2 : state.dataIPShortCut->lAlphaFieldBlanks,
745 2 : state.dataIPShortCut->cAlphaFieldNames,
746 2 : state.dataIPShortCut->cNumericFieldNames);
747 2 : cfs.Name = state.dataIPShortCut->cAlphaArgs(1);
748 2 : cfs.ComplexFeneType = state.dataIPShortCut->cAlphaArgs(2);
749 2 : cfs.surfName = state.dataIPShortCut->cAlphaArgs(3);
750 2 : if (Util::FindItemInList(cfs.surfName, state.dataSurface->Surface) == 0) {
751 0 : ShowSevereError(state,
752 0 : format("{}{}",
753 : cCurrentModuleObject,
754 0 : ": " + cfs.Name + ", invalid " + state.dataIPShortCut->cAlphaFieldNames(3) + "=\"" + cfs.surfName + "\"."));
755 0 : ErrorsFound = true;
756 : }
757 2 : cfs.wndwName = state.dataIPShortCut->cAlphaArgs(4);
758 2 : if (Util::FindItemInList(cfs.surfName, state.dataSurface->Surface) == 0) {
759 0 : ShowSevereError(state,
760 0 : format("{}{}",
761 : cCurrentModuleObject,
762 0 : ": " + cfs.Name + ", invalid " + state.dataIPShortCut->cAlphaFieldNames(4) + "=\"" + cfs.wndwName + "\"."));
763 0 : ErrorsFound = true;
764 : }
765 2 : cfs.feneRota = state.dataIPShortCut->rNumericArgs(1);
766 2 : if (cfs.feneRota < 0. || cfs.feneRota > 360.) {
767 0 : ShowSevereError(state,
768 0 : format("{}{}",
769 : cCurrentModuleObject,
770 0 : ": " + cfs.Name + ", invalid " + state.dataIPShortCut->cNumericFieldNames(1) + " outside of range 0 to 360."));
771 0 : ErrorsFound = true;
772 : }
773 : }
774 3 : }
775 :
776 3 : void CheckForGeometricTransform(EnergyPlusData &state, bool &doTransform, Real64 &OldAspectRatio, Real64 &NewAspectRatio)
777 : {
778 :
779 : // SUBROUTINE INFORMATION:
780 : // AUTHOR Linda Lawrie
781 : // DATE WRITTEN February 2009
782 : // MODIFIED na
783 : // RE-ENGINEERED na
784 :
785 : // PURPOSE OF THIS SUBROUTINE:
786 : // check for geometrytransform in the daylighting access for reference points
787 :
788 : // METHODOLOGY EMPLOYED:
789 : // once reference points have been converted to WCS,
790 : // change them to reflect a different aspect
791 : // ratio for the entire building based on user input.
792 :
793 : // SUBROUTINE PARAMETER DEFINITIONS:
794 3 : constexpr std::string_view CurrentModuleObject("GeometryTransform");
795 :
796 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
797 3 : Array1D_string cAlphas(1);
798 3 : Array1D<Real64> rNumerics(2);
799 :
800 : // begin execution
801 : // get user input...
802 3 : doTransform = false;
803 3 : OldAspectRatio = 1.0;
804 3 : NewAspectRatio = 1.0;
805 :
806 3 : if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject) == 1) {
807 : int NAlphas;
808 : int NNum;
809 : int IOStat;
810 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
811 : CurrentModuleObject,
812 : 1,
813 : cAlphas,
814 : NAlphas,
815 : rNumerics,
816 : NNum,
817 : IOStat,
818 0 : state.dataIPShortCut->lNumericFieldBlanks,
819 0 : state.dataIPShortCut->lAlphaFieldBlanks,
820 0 : state.dataIPShortCut->cAlphaFieldNames,
821 0 : state.dataIPShortCut->cNumericFieldNames);
822 0 : OldAspectRatio = rNumerics(1);
823 0 : NewAspectRatio = rNumerics(2);
824 0 : if (cAlphas(1) != "XY") {
825 0 : ShowWarningError(
826 : state,
827 0 : format("{}{}", CurrentModuleObject, ": invalid " + state.dataIPShortCut->cAlphaFieldNames(1) + "=" + cAlphas(1) + "...ignored."));
828 : }
829 0 : doTransform = true;
830 0 : state.dataSurface->AspectTransform = true;
831 : }
832 3 : if (state.dataSurface->WorldCoordSystem) {
833 3 : doTransform = false;
834 3 : state.dataSurface->AspectTransform = false;
835 : }
836 3 : }
837 :
838 33 : std::string ReplaceBlanksWithUnderscores(std::string const &InputString) // Input String
839 : {
840 :
841 : // FUNCTION INFORMATION:
842 : // AUTHOR Robert J. Hitchcock
843 : // DATE WRITTEN August 2003
844 :
845 : // PURPOSE OF THIS SUBROUTINE:
846 : // This function returns a representation of the InputString with blanks replaced with underscores.
847 :
848 : // METHODOLOGY EMPLOYED:
849 : // Uses the std::replace function from the C++ library
850 :
851 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
852 :
853 33 : std::string ResultString(trimmed(InputString));
854 33 : std::replace(ResultString.begin(), ResultString.end(), ' ', '_');
855 33 : return ResultString;
856 0 : }
857 :
858 2037 : void DElightElecLtgCtrl(int iNameLength,
859 : std::string const &cZoneName,
860 : Real64 dBldgLat,
861 : Real64 dHISKF,
862 : Real64 dHISUNF,
863 : Real64 dCloudFraction,
864 : Real64 dSOLCOSX,
865 : Real64 dSOLCOSY,
866 : Real64 dSOLCOSZ,
867 : Real64 &pdPowerReducFac,
868 : int piErrorFlag)
869 : {
870 2037 : std::vector<char> zoneNameArr(getCharArrayFromString(cZoneName));
871 2037 : delightelecltgctrl(
872 2037 : iNameLength, &zoneNameArr[0], dBldgLat, dHISKF, dHISUNF, dCloudFraction, dSOLCOSX, dSOLCOSY, dSOLCOSZ, &pdPowerReducFac, &piErrorFlag);
873 2037 : }
874 :
875 2037 : std::vector<char> getCharArrayFromString(std::string const &originalString)
876 : {
877 2037 : std::vector<char> returnVal(originalString.begin(), originalString.end());
878 2037 : returnVal.push_back('\0'); // get null terminated string of chars
879 2037 : return returnVal;
880 0 : }
881 :
882 0 : std::string getStringFromCharArray(std::vector<char> const &originalCharArray)
883 : {
884 0 : return std::string(originalCharArray.begin(), originalCharArray.end());
885 : }
886 :
887 : } // namespace DElightManagerF
888 :
889 : } // namespace EnergyPlus
|