Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2023, 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 <cassert>
51 : #include <cmath>
52 : #include <limits>
53 : #include <tuple>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/Construction.hh>
57 : #include <EnergyPlus/Data/EnergyPlusData.hh>
58 : #include <EnergyPlus/DataEnvironment.hh>
59 : #include <EnergyPlus/DataHeatBalSurface.hh>
60 : #include <EnergyPlus/DataHeatBalance.hh>
61 : #include <EnergyPlus/DataLoopNode.hh>
62 : #include <EnergyPlus/DataZoneEquipment.hh>
63 : #include <EnergyPlus/Psychrometrics.hh>
64 : #include <EnergyPlus/UtilityRoutines.hh>
65 : #include <EnergyPlus/WindowManager.hh>
66 : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
67 :
68 : namespace EnergyPlus::DataSurfaces {
69 :
70 : // MODULE INFORMATION:
71 : // AUTHOR Linda Lawrie
72 : // DATE WRITTEN May 2000
73 : // MODIFIED July 2003, (CC) added a flag for reference air temperature
74 : // Dec 2006, DJS (PSU) added logical ecoroof variable
75 : // Dec 2008, TH added new properties to SurfaceWindowCalc for thermochromic windows
76 : // Jul 2011, M.J. Witte and C.O. Pedersen, add new fields to OSC for last T, max and min
77 : // RE-ENGINEERED na
78 :
79 : // Using/Aliasing
80 : using namespace DataVectorTypes;
81 : using namespace DataBSDFWindow;
82 : using namespace DataHeatBalance;
83 : using namespace DataZoneEquipment;
84 : using namespace DataLoopNode;
85 : using namespace Psychrometrics;
86 : using namespace DataEnvironment;
87 : using namespace WindowManager;
88 :
89 771 : Array1D_string const cExtBoundCondition({-6, 0}, {"KivaFoundation", "FCGround", "OSCM", "OSC", "OSC", "Ground", "ExternalEnvironment"});
90 :
91 : // Parameters to indicate surface classes
92 : // Surface Class (FLOOR, WALL, ROOF (incl's CEILING), WINDOW, DOOR, GLASSDOOR,
93 : // SHADING (includes OVERHANG, WING), DETACHED, INTMASS),
94 : // TDD:DOME, TDD:DIFFUSER (for tubular daylighting device)
95 : // (Note: GLASSDOOR and TDD:DIFFUSER get overwritten as WINDOW
96 : // in SurfaceGeometry.cc, SurfaceWindow%OriginalClass holds the true value)
97 : // why aren't these sequential (LKL - 13 Aug 2007)
98 :
99 : // Constructor
100 41478 : Surface2D::Surface2D(ShapeCat const shapeCat, int const axis, Vertices const &v, Vector2D const &vl, Vector2D const &vu)
101 41478 : : axis(axis), vertices(v), vl(vl), vu(vu)
102 : {
103 41478 : size_type const n(vertices.size());
104 41478 : assert(n >= 3);
105 :
106 : // Reverse vertices order if clockwise
107 : // If sorting by y for slab method can detect clockwise faster by just comparing edges at bottom or top-most vertex
108 41478 : Real64 area(0.0); // Actually 2x the signed area
109 208063 : for (Vertices::size_type i = 0; i < n; ++i) {
110 166585 : Vector2D const &v(vertices[i]);
111 166585 : Vector2D const &w(vertices[(i + 1) % n]);
112 166585 : area += (v.x * w.y) - (w.x * v.y);
113 : }
114 41478 : if (area < 0.0) std::reverse(vertices.begin() + 1, vertices.end()); // Vertices in clockwise order: Reverse all but first
115 :
116 : // Set up edge vectors for ray--surface intersection tests
117 41478 : edges.reserve(n);
118 208063 : for (Vertices::size_type i = 0; i < n; ++i) {
119 166585 : edges.push_back(vertices[(i + 1) % n] - vertices[i]);
120 : }
121 41478 : if (shapeCat == ShapeCat::Rectangular) { // Set side length squared for ray--surface intersection tests
122 36242 : assert(n == 4u);
123 36242 : s1 = edges[0].magnitude_squared();
124 36242 : s3 = edges[3].magnitude_squared();
125 5236 : } else if ((shapeCat == ShapeCat::Nonconvex) || (n >= nVerticesBig)) { // Set up slabs
126 167 : assert(n >= 4u);
127 167 : slabYs.reserve(n);
128 1691 : for (size_type i = 0; i < n; ++i)
129 1524 : slabYs.push_back(vertices[i].y);
130 167 : std::sort(slabYs.begin(), slabYs.end()); // Sort the vertex y coordinates
131 334 : auto const iClip(std::unique(slabYs.begin(), slabYs.end())); // Remove duplicate y-coordinate elements
132 167 : slabYs.erase(iClip, slabYs.end());
133 167 : slabYs.shrink_to_fit();
134 746 : for (size_type iSlab = 0, iSlab_end = slabYs.size() - 1; iSlab < iSlab_end; ++iSlab) { // Create slabs
135 579 : Real64 xl(std::numeric_limits<Real64>::max());
136 579 : Real64 xu(std::numeric_limits<Real64>::lowest());
137 579 : Real64 const yl(slabYs[iSlab]);
138 579 : Real64 const yu(slabYs[iSlab + 1]);
139 579 : slabs.push_back(Slab(yl, yu));
140 579 : Slab &slab(slabs.back());
141 : using CrossEdge = std::tuple<Real64, Real64, size_type>;
142 : using CrossEdges = std::vector<CrossEdge>;
143 1158 : CrossEdges crossEdges;
144 6709 : for (size_type i = 0; i < n; ++i) { // Find edges crossing slab
145 6130 : Vector2D const &v(vertices[i]);
146 6130 : Vector2D const &w(vertices[(i + 1) % n]);
147 11448 : if (((v.y <= yl) && (yu <= w.y)) || // Crosses upward
148 8419 : ((yu <= v.y) && (w.y <= yl))) // Crosses downward
149 : {
150 1624 : Edge const &e(edges[i]);
151 1624 : assert(e.y != 0.0);
152 1624 : Real64 const exy(e.x / e.y);
153 1624 : Real64 const xb(v.x + (yl - v.y) * exy); // x_bot coordinate where edge intersects yl
154 1624 : Real64 const xt(v.x + (yu - v.y) * exy); // x_top coordinate where edge intersects yu
155 1624 : xl = std::min(xl, std::min(xb, xt));
156 1624 : xu = std::max(xu, std::max(xb, xt));
157 1624 : crossEdges.push_back(std::make_tuple(xb, xt, i));
158 : }
159 : }
160 579 : slab.xl = xl;
161 579 : slab.xu = xu;
162 579 : assert(crossEdges.size() >= 2u);
163 579 : std::sort(crossEdges.begin(),
164 1158 : crossEdges.end(),
165 2248 : [](CrossEdge const &e1, CrossEdge const &e2) -> bool // Lambda to sort by x_mid
166 : {
167 2248 : return std::get<0>(e1) + std::get<1>(e1) <
168 2248 : std::get<0>(e2) + std::get<1>(e2); // Sort edges by x_mid: x_bot or x_top could have repeats with shared vertex
169 : });
170 : #ifndef NDEBUG // Check x_bot and x_top are also sorted
171 579 : Real64 xb(std::get<0>(crossEdges[0]));
172 579 : Real64 xt(std::get<1>(crossEdges[0]));
173 579 : Real64 const tol(1.0e-9 * std::max(std::abs(xl), std::abs(xu))); // EnergyPlus vertex precision is not tight so tolerance isn't either
174 2203 : for (auto const &edge : crossEdges) { // Detect non-simple polygon with crossing edges
175 1624 : Real64 const xbe(std::get<0>(edge));
176 1624 : Real64 const xte(std::get<1>(edge));
177 1624 : assert(xb <= xbe + tol);
178 1624 : assert(xt <= xte + tol);
179 1624 : xb = xbe;
180 1624 : xt = xte;
181 : }
182 : #endif
183 579 : assert((shapeCat == ShapeCat::Nonconvex) || (crossEdges.size() == 2));
184 2203 : for (auto const &edge : crossEdges) {
185 1624 : size_type const iEdge(std::get<2>(edge));
186 1624 : slab.edges.push_back(iEdge); // Add edge to slab
187 1624 : Vector2D const &e(edges[iEdge]);
188 1624 : assert(e.y != 0.0); // Constant y edge can't be a crossing edge
189 1624 : slab.edgesXY.push_back(e.y != 0.0 ? e.x / e.y : 0.0); // Edge inverse slope
190 : }
191 579 : assert(slab.edges.size() % 2 == 0u);
192 579 : assert(slab.edges.size() == slab.edgesXY.size());
193 : }
194 : }
195 41478 : }
196 :
197 : // Set Precomputed Parameters
198 43762 : void SurfaceData::set_computed_geometry()
199 : {
200 43762 : if (Vertex.size() >= 3) { // Skip no-vertex "surfaces"
201 41478 : shapeCat = computed_shapeCat();
202 41478 : plane = computed_plane();
203 41478 : surface2d = computed_surface2d();
204 : }
205 43762 : }
206 :
207 438248263 : Real64 SurfaceData::getInsideAirTemperature(EnergyPlusData &state, const int t_SurfNum) const
208 : {
209 : // SUBROUTINE INFORMATION:
210 : // AUTHOR Simon Vidanovic
211 : // DATE WRITTEN June 2016
212 : // MODIFIED na
213 : // RE-ENGINEERED na
214 :
215 : // PURPOSE OF THIS SUBROUTINE:
216 : // Routine calculates reference air temperature for given surface (refactoring from the code)
217 : //
218 : // NOTE: This routine has been copy/pasted in the past in several different modules with slight
219 : // modifications at some of those places. It is quite logical that reference air temperature
220 : // for the surface is calculated as public function of SurfaceData structure (class) and is
221 : // later called as needed. Note that SurfaceNum had to be passed to this routine because of
222 : // access to global array SurfTempEffBulkAir. I would propose refactoring where SurfTempEffBulkAir
223 : // is part of SurfaceData structure and instead of calling SurfTempEffBulkAir( SurfNum ) it should
224 : // be called Surface( SurfNum ).TempEffBulkAir (Simon Vidanovic)
225 :
226 438248263 : Real64 RefAirTemp = 0;
227 :
228 : // determine reference air temperature for this surface
229 438248263 : auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(this->Zone);
230 438248263 : switch (state.dataSurface->SurfTAirRef(t_SurfNum)) {
231 2492921 : case RefAirTemp::ZoneMeanAirTemp: {
232 2492921 : RefAirTemp = thisZoneHB.MAT;
233 2492921 : } break;
234 4037270 : case RefAirTemp::AdjacentAirTemp: {
235 4037270 : RefAirTemp = state.dataHeatBal->SurfTempEffBulkAir(t_SurfNum);
236 4037270 : } break;
237 0 : case RefAirTemp::ZoneSupplyAirTemp: {
238 : // determine ZoneEquipConfigNum for this zone
239 : // ControlledZoneAirFlag = .FALSE.
240 : // ZoneEquipConfigNum = ZoneNum;
241 : // check whether this zone is a controlled zone or not
242 0 : if (!state.dataHeatBal->Zone(Zone).IsControlled) {
243 0 : ShowFatalError(
244 0 : state, "Zones must be controlled for Ceiling-Diffuser Convection model. No system serves zone " + state.dataHeatBal->Zone(Zone).Name);
245 : // return;
246 : }
247 : // determine supply air conditions
248 0 : Real64 SumSysMCp = 0;
249 0 : Real64 SumSysMCpT = 0;
250 0 : for (int NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(Zone).NumInletNodes; ++NodeNum) {
251 0 : Real64 NodeTemp = state.dataLoopNodes->Node(state.dataZoneEquip->ZoneEquipConfig(Zone).InletNode(NodeNum)).Temp;
252 0 : Real64 MassFlowRate = state.dataLoopNodes->Node(state.dataZoneEquip->ZoneEquipConfig(Zone).InletNode(NodeNum)).MassFlowRate;
253 0 : Real64 CpAir = PsyCpAirFnW(thisZoneHB.ZoneAirHumRat);
254 0 : SumSysMCp += MassFlowRate * CpAir;
255 0 : SumSysMCpT += MassFlowRate * CpAir * NodeTemp;
256 : }
257 : // a weighted average of the inlet temperatures.
258 0 : if (SumSysMCp > 0.0) {
259 : // a weighted average of the inlet temperatures.
260 0 : RefAirTemp = SumSysMCpT / SumSysMCp;
261 : } else {
262 0 : RefAirTemp = thisZoneHB.MAT;
263 : }
264 0 : } break;
265 431718072 : default: {
266 : // currently set to mean air temp but should add error warning here
267 431718072 : RefAirTemp = thisZoneHB.MAT;
268 431718072 : } break;
269 : }
270 :
271 438248263 : return RefAirTemp;
272 : }
273 :
274 8064 : Real64 SurfaceData::getOutsideAirTemperature(EnergyPlusData &state, const int t_SurfNum) const
275 : {
276 : // SUBROUTINE INFORMATION:
277 : // AUTHOR Simon Vidanovic
278 : // DATE WRITTEN June 2016
279 : // MODIFIED na
280 : // RE-ENGINEERED na
281 :
282 : // PURPOSE OF THIS SUBROUTINE:
283 : // Routine calculates outside air temperature for given surface.
284 : // Routine will return inside air temperature if it is interior surface. (refactoring from the code)
285 : //
286 : // NOTE: This routine has been copy/pasted in the past in several different modules with slight
287 : // modifications at some of those places. Exterior/interior surface air temperature is tied to surface.
288 8064 : Real64 temperature = 0;
289 :
290 8064 : if (ExtBoundCond > 0) // Interzone window
291 : {
292 0 : temperature = getInsideAirTemperature(state, t_SurfNum);
293 : } else {
294 8064 : if (ExtWind) {
295 : // Window is exposed to wind (and possibly rain)
296 8064 : if (state.dataEnvrn->IsRain) {
297 : // Raining: since wind exposed, outside window surface gets wet
298 0 : temperature = state.dataSurface->SurfOutWetBulbTemp(t_SurfNum);
299 : } else {
300 : // Dry
301 8064 : temperature = state.dataSurface->SurfOutDryBulbTemp(t_SurfNum);
302 : }
303 : } else {
304 : // Window not exposed to wind
305 0 : temperature = state.dataSurface->SurfOutDryBulbTemp(t_SurfNum);
306 : }
307 : }
308 :
309 8064 : return temperature;
310 : }
311 :
312 4032 : Real64 SurfaceData::getOutsideIR(EnergyPlusData &state, const int t_SurfNum) const
313 : {
314 : // SUBROUTINE INFORMATION:
315 : // AUTHOR Simon Vidanovic
316 : // DATE WRITTEN July 2016
317 : // MODIFIED na
318 : // RE-ENGINEERED na
319 :
320 : // PURPOSE OF THIS SUBROUTINE:
321 : // Calculates outside infrared radiation
322 4032 : Real64 value = 0;
323 4032 : if (ExtBoundCond > 0) {
324 0 : value = state.dataSurface->SurfWinIRfromParentZone(ExtBoundCond) + state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(ExtBoundCond);
325 : } else {
326 4032 : Real64 tout = getOutsideAirTemperature(state, t_SurfNum) + DataGlobalConstants::KelvinConv;
327 4032 : value = state.dataWindowManager->sigma * pow_4(tout);
328 12096 : value = ViewFactorSkyIR *
329 12096 : (state.dataSurface->SurfAirSkyRadSplit(t_SurfNum) * state.dataWindowManager->sigma * pow_4(state.dataEnvrn->SkyTempKelvin) +
330 4032 : (1.0 - state.dataSurface->SurfAirSkyRadSplit(t_SurfNum)) * value) +
331 4032 : ViewFactorGroundIR * value;
332 : }
333 4032 : return value;
334 : }
335 :
336 4032 : Real64 SurfaceData::getSWIncident(EnergyPlusData &state, const int t_SurfNum)
337 : {
338 : // SUBROUTINE INFORMATION:
339 : // AUTHOR Simon Vidanovic
340 : // DATE WRITTEN July 2016
341 : // MODIFIED na
342 : // RE-ENGINEERED na
343 :
344 : // PURPOSE OF THIS SUBROUTINE:
345 : // Return total short wave incident to the surface
346 :
347 4032 : return state.dataHeatBal->SurfQRadSWOutIncident(t_SurfNum) +
348 4032 : state.dataHeatBal->EnclSolQSWRad(state.dataSurface->Surface(t_SurfNum).SolarEnclIndex);
349 : }
350 :
351 10752 : int SurfaceData::getTotLayers(EnergyPlusData &state) const
352 : {
353 : // SUBROUTINE INFORMATION:
354 : // AUTHOR Simon Vidanovic
355 : // DATE WRITTEN August 2016
356 : // MODIFIED na
357 : // RE-ENGINEERED na
358 :
359 : // PURPOSE OF THIS SUBROUTINE:
360 : // Returns total number of layer for current surface
361 :
362 10752 : auto &construction(state.dataConstruction->Construct(Construction));
363 10752 : return construction.TotLayers;
364 : }
365 :
366 : // Computed Shape Category
367 41478 : ShapeCat SurfaceData::computed_shapeCat() const
368 : {
369 41478 : if (Shape == SurfaceShape::Triangle) {
370 181 : return ShapeCat::Triangular;
371 41297 : } else if (Shape == SurfaceShape::TriangularWindow) {
372 2 : return ShapeCat::Triangular;
373 41295 : } else if (Shape == SurfaceShape::TriangularDoor) {
374 0 : return ShapeCat::Triangular;
375 41295 : } else if (Shape == SurfaceShape::Rectangle) {
376 29804 : return ShapeCat::Rectangular;
377 11491 : } else if (Shape == SurfaceShape::RectangularDoorWindow) {
378 6438 : return ShapeCat::Rectangular;
379 5053 : } else if (Shape == SurfaceShape::RectangularOverhang) {
380 0 : return ShapeCat::Rectangular;
381 5053 : } else if (Shape == SurfaceShape::RectangularLeftFin) {
382 0 : return ShapeCat::Rectangular;
383 5053 : } else if (Shape == SurfaceShape::RectangularRightFin) {
384 0 : return ShapeCat::Rectangular;
385 5053 : } else if (IsConvex) {
386 4886 : return ShapeCat::Convex;
387 : } else {
388 167 : return ShapeCat::Nonconvex;
389 : }
390 : }
391 :
392 : // Computed Plane
393 82956 : SurfaceData::Plane SurfaceData::computed_plane() const
394 : {
395 82956 : Vertices::size_type const n(Vertex.size());
396 82956 : assert(n >= 3);
397 165912 : Vector center(0.0); // Center (vertex average) point (not mass centroid)
398 82956 : Real64 a(0.0), b(0.0), c(0.0), d(0.0); // Plane coefficients
399 416126 : for (Vertices::size_type i = 0; i < n; ++i) { // Newell's method for robustness (not speed)
400 333170 : Vector const &v(Vertex[i]);
401 333170 : Vector const &w(Vertex[(i + 1) % n]);
402 333170 : a += (v.y - w.y) * (v.z + w.z);
403 333170 : b += (v.z - w.z) * (v.x + w.x);
404 333170 : c += (v.x - w.x) * (v.y + w.y);
405 333170 : center += v;
406 : }
407 82956 : d = -(dot(center, Vector(a, b, c)) / n); // center/n is the center point
408 165912 : return Plane(a, b, c, d); // a*x + b*y + c*z + d = 0
409 : }
410 :
411 : // Computed axis-projected 2D surface
412 41478 : Surface2D SurfaceData::computed_surface2d() const
413 : {
414 : // Project along axis of min surface range for 2D intersection use
415 41478 : Vertices::size_type const n(Vertex.size());
416 41478 : assert(n >= 3);
417 41478 : assert(plane == computed_plane()); // Set plane first
418 : using Vertex2D = ObjexxFCL::Vector2<Real64>;
419 : using Vertices2D = ObjexxFCL::Array1D<Vertex2D>;
420 :
421 : // Select axis to project along
422 41478 : Real64 const a(std::abs(plane.x)); // Plane normal x coordinate magnitude
423 41478 : Real64 const b(std::abs(plane.y)); // Plane normal y coordinate magnitude
424 41478 : Real64 const c(std::abs(plane.z)); // Plane normal z coordinate magnitude
425 41478 : int const axis(a >= std::max(b, c) ? 0 : (b >= std::max(a, c) ? 1 : 2)); // Project along plane's normal's largest magnitude coordinate
426 :
427 : // Set up 2D surface
428 82956 : Vertices2D v2d(n);
429 41478 : Vector const &v0(Vertex[0]);
430 41478 : if (axis == 0) { // Use y,z for 2D surface
431 12845 : Real64 yl(v0.y), yu(v0.y); // y coordinate ranges
432 12845 : Real64 zl(v0.z), zu(v0.z); // z coordinate ranges
433 64139 : for (Vertices::size_type i = 0; i < n; ++i) {
434 51294 : Vector const &v(Vertex[i]);
435 51294 : v2d[i] = Vertex2D(v.y, v.z);
436 51294 : yl = std::min(yl, v.y);
437 51294 : yu = std::max(yu, v.y);
438 51294 : zl = std::min(zl, v.z);
439 51294 : zu = std::max(zu, v.z);
440 : }
441 12845 : return Surface2D(shapeCat, axis, v2d, Vertex2D(yl, zl), Vertex2D(yu, zu));
442 28633 : } else if (axis == 1) { // Use x,z for 2D surface
443 14880 : Real64 xl(v0.x), xu(v0.x); // x coordinate ranges
444 14880 : Real64 zl(v0.z), zu(v0.z); // z coordinate ranges
445 74362 : for (Vertices::size_type i = 0; i < n; ++i) {
446 59482 : Vector const &v(Vertex[i]);
447 59482 : v2d[i] = Vertex2D(v.x, v.z);
448 59482 : xl = std::min(xl, v.x);
449 59482 : xu = std::max(xu, v.x);
450 59482 : zl = std::min(zl, v.z);
451 59482 : zu = std::max(zu, v.z);
452 : }
453 14880 : return Surface2D(shapeCat, axis, v2d, Vertex2D(xl, zl), Vertex2D(xu, zu));
454 : } else { // Use x,y for 2D surface
455 13753 : Real64 xl(v0.x), xu(v0.x); // x coordinate ranges
456 13753 : Real64 yl(v0.y), yu(v0.y); // y coordinate ranges
457 69562 : for (Vertices::size_type i = 0; i < n; ++i) {
458 55809 : Vector const &v(Vertex[i]);
459 55809 : v2d[i] = Vertex2D(v.x, v.y);
460 55809 : xl = std::min(xl, v.x);
461 55809 : xu = std::max(xu, v.x);
462 55809 : yl = std::min(yl, v.y);
463 55809 : yu = std::max(yu, v.y);
464 : }
465 13753 : return Surface2D(shapeCat, axis, v2d, Vertex2D(xl, yl), Vertex2D(xu, yu));
466 : }
467 : }
468 :
469 15 : Real64 SurfaceData::get_average_height(EnergyPlusData &state) const
470 : {
471 15 : if (std::abs(SinTilt) < 1.e-4) {
472 0 : return 0.0;
473 : }
474 : using Vertex2D = ObjexxFCL::Vector2<Real64>;
475 : using Vertices2D = ObjexxFCL::Array1D<Vertex2D>;
476 15 : Vertices::size_type const n(Vertex.size());
477 15 : assert(n >= 3);
478 :
479 30 : Vertices2D v2d(n);
480 :
481 : // project onto 2D vertical plane
482 15 : Real64 xRef = Vertex[0].x;
483 15 : Real64 yRef = Vertex[0].y;
484 15 : Real64 const &saz(SinAzim);
485 15 : Real64 const &caz(CosAzim);
486 75 : for (Vertices::size_type i = 0; i < n; ++i) {
487 60 : Vector const &v(Vertex[i]);
488 60 : v2d[i] = Vertex2D(-(v.x - xRef) * caz + (v.y - yRef) * saz, v.z);
489 : }
490 :
491 : // piecewise linear integration
492 :
493 : // Get total width of polygon
494 15 : Real64 minX(v2d[0].x), maxX(v2d[0].x);
495 75 : for (Vertices::size_type i = 0; i < n; ++i) {
496 60 : Vertex2D const &v(v2d[i]);
497 60 : minX = std::min(minX, v.x);
498 60 : maxX = std::max(maxX, v.x);
499 : }
500 15 : Real64 totalWidth = maxX - minX;
501 :
502 15 : if (totalWidth == 0.0) {
503 : // This should never happen, but if it does, print a somewhat meaningful fatal error
504 : // (instead of allowing a divide by zero).
505 0 : ShowFatalError(state, "Calculated projected surface width is zero for surface=\"" + Name + "\"");
506 : }
507 :
508 15 : Real64 averageHeight = 0.0;
509 75 : for (Vertices::size_type i = 0; i < n; ++i) {
510 60 : Vertex2D const &v(v2d[i]);
511 :
512 : Vertex2D *v2;
513 60 : if (i == n - 1) {
514 15 : v2 = &v2d[0];
515 : } else {
516 45 : v2 = &v2d[i + 1];
517 : }
518 60 : averageHeight += 0.5 * (v.y + v2->y) * (v2->x - v.x) / totalWidth;
519 : }
520 15 : return std::abs(averageHeight) / SinTilt;
521 : }
522 :
523 579 : void SurfaceData::make_hash_key(EnergyPlusData &state, const int SurfNum)
524 : {
525 579 : calcHashKey = SurfaceCalcHashKey();
526 579 : calcHashKey.Construction = Construction;
527 579 : calcHashKey.Azimuth = round(Azimuth * 10.0) / 10.0;
528 579 : calcHashKey.Tilt = round(Tilt * 10.0) / 10.0;
529 579 : calcHashKey.Height = round(Height * 10.0) / 10.0;
530 579 : calcHashKey.Zone = Zone;
531 579 : calcHashKey.EnclIndex = SolarEnclIndex;
532 579 : calcHashKey.TAirRef = state.dataSurface->SurfTAirRef(SurfNum);
533 :
534 579 : auto extBoundCond = state.dataSurface->Surface(SurfNum).ExtBoundCond;
535 579 : if (extBoundCond > 0) {
536 256 : calcHashKey.ExtZone = state.dataSurface->Surface(extBoundCond).Zone;
537 256 : calcHashKey.ExtEnclIndex = state.dataSurface->Surface(extBoundCond).SolarEnclIndex;
538 256 : calcHashKey.ExtCond = 1;
539 : } else {
540 323 : calcHashKey.ExtZone = 0;
541 323 : calcHashKey.ExtEnclIndex = 0;
542 323 : calcHashKey.ExtCond = extBoundCond;
543 : }
544 :
545 579 : calcHashKey.ExtSolar = ExtSolar;
546 579 : calcHashKey.ExtWind = ExtWind;
547 579 : calcHashKey.ViewFactorGround = round(ViewFactorGround * 10.0) / 10.0;
548 579 : calcHashKey.ViewFactorSky = round(ViewFactorSky * 10.0) / 10.0;
549 :
550 579 : calcHashKey.HeatTransferAlgorithm = HeatTransferAlgorithm;
551 579 : calcHashKey.IntConvCoeff = state.dataSurface->SurfIntConvCoeffIndex(SurfNum);
552 579 : calcHashKey.ExtConvCoeff = state.dataSurface->SurfExtConvCoeffIndex(SurfNum);
553 579 : calcHashKey.OSCPtr = OSCPtr;
554 579 : calcHashKey.OSCMPtr = OSCMPtr;
555 :
556 579 : calcHashKey.FrameDivider = FrameDivider;
557 579 : calcHashKey.SurfWinStormWinConstr = state.dataSurface->SurfWinStormWinConstr(SurfNum);
558 :
559 579 : calcHashKey.MaterialMovInsulExt = state.dataSurface->SurfMaterialMovInsulExt(SurfNum);
560 579 : calcHashKey.MaterialMovInsulInt = state.dataSurface->SurfMaterialMovInsulInt(SurfNum);
561 579 : calcHashKey.SchedMovInsulExt = state.dataSurface->SurfSchedMovInsulExt(SurfNum);
562 579 : calcHashKey.SchedMovInsulInt = state.dataSurface->SurfSchedMovInsulInt(SurfNum);
563 579 : calcHashKey.ExternalShadingSchInd = state.dataSurface->Surface(SurfNum).SurfExternalShadingSchInd;
564 579 : calcHashKey.SurroundingSurfacesNum = state.dataSurface->Surface(SurfNum).SurfSurroundingSurfacesNum;
565 579 : calcHashKey.LinkedOutAirNode = state.dataSurface->Surface(SurfNum).SurfLinkedOutAirNode;
566 579 : calcHashKey.OutsideHeatSourceTermSchedule = OutsideHeatSourceTermSchedule;
567 579 : calcHashKey.InsideHeatSourceTermSchedule = InsideHeatSourceTermSchedule;
568 579 : }
569 :
570 579 : void SurfaceData::set_representative_surface(EnergyPlusData &state, const int SurfNum)
571 : {
572 : // Make hash key for this surface (used to determine uniqueness)
573 579 : state.dataSurface->Surface(SurfNum).make_hash_key(state, SurfNum);
574 : // Insert surface key into map. If key already exists, it will not be added.
575 : // Assign the representative surface number based on the first instance of the identical key
576 579 : state.dataSurface->Surface(SurfNum).RepresentativeCalcSurfNum =
577 1158 : state.dataSurface->RepresentativeSurfaceMap.insert({state.dataSurface->Surface(SurfNum).calcHashKey, SurfNum}).first->second;
578 :
579 579 : state.dataSurface->Surface(state.dataSurface->Surface(SurfNum).RepresentativeCalcSurfNum).ConstituentSurfaceNums.push_back(SurfNum);
580 579 : }
581 :
582 : // Functions
583 :
584 2568509 : void SetSurfaceOutBulbTempAt(EnergyPlusData &state)
585 : {
586 2568509 : if (state.dataEnvrn->SiteTempGradient == 0.0) {
587 0 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
588 0 : state.dataSurface->SurfOutDryBulbTemp(SurfNum) = state.dataEnvrn->OutDryBulbTemp;
589 0 : state.dataSurface->SurfOutWetBulbTemp(SurfNum) = state.dataEnvrn->OutWetBulbTemp;
590 : }
591 : } else {
592 2568509 : Real64 const BaseDryTemp(state.dataEnvrn->OutDryBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
593 2568509 : Real64 const BaseWetTemp(state.dataEnvrn->OutWetBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
594 166715563 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
595 : // Base temperatures at Z = 0 (C)
596 164147054 : Real64 const Z(state.dataSurface->Surface(SurfNum).Centroid.z); // Centroid value
597 164147054 : if (Z <= 0.0) {
598 22838190 : state.dataSurface->SurfOutDryBulbTemp(SurfNum) = BaseDryTemp;
599 22838190 : state.dataSurface->SurfOutWetBulbTemp(SurfNum) = BaseWetTemp;
600 : } else {
601 141308864 : Real64 GradientDividend = state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z;
602 141308864 : Real64 GradientDivisor = DataEnvironment::EarthRadius + Z;
603 141308864 : state.dataSurface->SurfOutDryBulbTemp(SurfNum) = BaseDryTemp - GradientDividend / GradientDivisor;
604 141308864 : state.dataSurface->SurfOutWetBulbTemp(SurfNum) = BaseWetTemp - GradientDividend / GradientDivisor;
605 : }
606 : }
607 : }
608 2568509 : }
609 :
610 2568509 : void CheckSurfaceOutBulbTempAt(EnergyPlusData &state)
611 : {
612 : // Using/Aliasing
613 : using DataEnvironment::SetOutBulbTempAt_error;
614 :
615 2568509 : Real64 minBulb = 0.0;
616 166715563 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
617 164147054 : minBulb = min(minBulb, state.dataSurface->SurfOutDryBulbTemp(SurfNum), state.dataSurface->SurfOutWetBulbTemp(SurfNum));
618 164147054 : if (minBulb < -100.0)
619 0 : SetOutBulbTempAt_error(state, "Surface", state.dataSurface->Surface(SurfNum).Centroid.z, state.dataSurface->Surface(SurfNum).Name);
620 : }
621 2568509 : }
622 :
623 2568509 : void SetSurfaceWindSpeedAt(EnergyPlusData &state)
624 : {
625 2568509 : Real64 const fac(state.dataEnvrn->WindSpeed * state.dataEnvrn->WeatherFileWindModCoeff *
626 2568509 : std::pow(state.dataEnvrn->SiteWindBLHeight, -state.dataEnvrn->SiteWindExp));
627 2568509 : if (state.dataEnvrn->SiteWindExp == 0.0) {
628 0 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
629 0 : state.dataSurface->SurfOutWindSpeed(SurfNum) = state.dataEnvrn->WindSpeed;
630 : }
631 : } else {
632 :
633 166715563 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
634 164147054 : if (!state.dataSurface->Surface(SurfNum).ExtWind) continue;
635 64897072 : Real64 const Z(state.dataSurface->Surface(SurfNum).Centroid.z); // Centroid value
636 64897072 : if (Z <= 0.0) {
637 72555 : state.dataSurface->SurfOutWindSpeed(SurfNum) = 0.0;
638 : } else {
639 : // [Met] - at meterological Station, Height of measurement is usually 10m above ground
640 : // LocalWindSpeed = Windspeed [Met] * (Wind Boundary LayerThickness [Met]/Height [Met])**Wind Exponent[Met] &
641 : // * (Height above ground / Site Wind Boundary Layer Thickness) ** Site Wind Exponent
642 64824517 : state.dataSurface->SurfOutWindSpeed(SurfNum) = fac * std::pow(Z, state.dataEnvrn->SiteWindExp);
643 : }
644 : }
645 : }
646 2568509 : }
647 :
648 2568509 : void SetSurfaceWindDirAt(EnergyPlusData &state)
649 : {
650 166715563 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
651 164147054 : state.dataSurface->SurfOutWindDir(SurfNum) = state.dataEnvrn->WindDir;
652 : }
653 2568509 : }
654 :
655 37314 : std::string cSurfaceClass(SurfaceClass const ClassNo)
656 : {
657 :
658 : // FUNCTION INFORMATION:
659 : // AUTHOR Linda Lawrie
660 : // DATE WRITTEN May 2006
661 : // MODIFIED na
662 : // RE-ENGINEERED na
663 :
664 : // PURPOSE OF THIS FUNCTION:
665 : // This function returns a string based on class number.
666 :
667 : // Return value
668 37314 : std::string ClassName;
669 :
670 37314 : switch (ClassNo) {
671 17593 : case SurfaceClass::Wall: {
672 17593 : ClassName = "Wall";
673 17593 : } break;
674 5461 : case SurfaceClass::Floor: {
675 5461 : ClassName = "Floor";
676 5461 : } break;
677 4687 : case SurfaceClass::Roof: {
678 4687 : ClassName = "Roof";
679 4687 : } break;
680 5398 : case SurfaceClass::Window: {
681 5398 : ClassName = "Window";
682 5398 : } break;
683 0 : case SurfaceClass::GlassDoor: {
684 0 : ClassName = "Glass Door";
685 0 : } break;
686 412 : case SurfaceClass::Door: {
687 412 : ClassName = "Door";
688 412 : } break;
689 4 : case SurfaceClass::TDD_Dome: {
690 4 : ClassName = "TubularDaylightDome";
691 4 : } break;
692 0 : case SurfaceClass::TDD_Diffuser: {
693 0 : ClassName = "TubularDaylightDiffuser";
694 0 : } break;
695 3119 : case SurfaceClass::IntMass: {
696 3119 : ClassName = "Internal Mass";
697 3119 : } break;
698 556 : case SurfaceClass::Shading: {
699 556 : ClassName = "Shading";
700 556 : } break;
701 30 : case SurfaceClass::Detached_B: {
702 30 : ClassName = "Detached Shading:Building";
703 30 : } break;
704 54 : case SurfaceClass::Detached_F: {
705 54 : ClassName = "Detached Shading:Fixed";
706 54 : } break;
707 0 : default: {
708 0 : ClassName = "Invalid/Unknown";
709 0 : } break;
710 : }
711 :
712 37314 : return ClassName;
713 : }
714 71522 : Real64 AbsFrontSide(EnergyPlusData &state, int SurfNum)
715 : {
716 : Real64 AbsorptanceFromExteriorFrontSide =
717 71522 : (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
718 71522 : state.dataSurface->SurfWinShadeAbsFacFace1(SurfNum);
719 : Real64 AbsorptanceFromInteriorFrontSide =
720 71522 : (state.dataSurface->SurfWinIntBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinIntSWAbsByShade(SurfNum)) *
721 71522 : state.dataSurface->SurfWinShadeAbsFacFace2(SurfNum);
722 71522 : return AbsorptanceFromExteriorFrontSide + AbsorptanceFromInteriorFrontSide;
723 : }
724 :
725 71522 : Real64 AbsBackSide(EnergyPlusData &state, int SurfNum)
726 : {
727 : Real64 AbsorptanceFromInteriorBackSide =
728 71522 : (state.dataSurface->SurfWinIntBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinIntSWAbsByShade(SurfNum)) *
729 71522 : state.dataSurface->SurfWinShadeAbsFacFace1(SurfNum);
730 : Real64 AbsorptanceFromExteriorBackSide =
731 71522 : (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
732 71522 : state.dataSurface->SurfWinShadeAbsFacFace2(SurfNum);
733 71522 : return AbsorptanceFromExteriorBackSide + AbsorptanceFromInteriorBackSide;
734 : }
735 :
736 2313 : } // namespace EnergyPlus::DataSurfaces
|